]> sjero.net Git - iperf/blob - src/Listener.cpp
Native IPv6 support for iperf
[iperf] / src / Listener.cpp
1 /*--------------------------------------------------------------- 
2  * Copyright (c) 1999,2000,2001,2002,2003                              
3  * The Board of Trustees of the University of Illinois            
4  * All Rights Reserved.                                           
5  *--------------------------------------------------------------- 
6  * Permission is hereby granted, free of charge, to any person    
7  * obtaining a copy of this software (Iperf) and associated       
8  * documentation files (the "Software"), to deal in the Software  
9  * without restriction, including without limitation the          
10  * rights to use, copy, modify, merge, publish, distribute,        
11  * sublicense, and/or sell copies of the Software, and to permit     
12  * persons to whom the Software is furnished to do
13  * so, subject to the following conditions: 
14  *
15  *     
16  * Redistributions of source code must retain the above 
17  * copyright notice, this list of conditions and 
18  * the following disclaimers. 
19  *
20  *     
21  * Redistributions in binary form must reproduce the above 
22  * copyright notice, this list of conditions and the following 
23  * disclaimers in the documentation and/or other materials 
24  * provided with the distribution. 
25  * 
26  *     
27  * Neither the names of the University of Illinois, NCSA, 
28  * nor the names of its contributors may be used to endorse 
29  * or promote products derived from this Software without
30  * specific prior written permission. 
31  * 
32  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
33  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
34  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
35  * NONINFRINGEMENT. IN NO EVENT SHALL THE CONTIBUTORS OR COPYRIGHT 
36  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
37  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
38  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE
39  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
40  * ________________________________________________________________
41  * National Laboratory for Applied Network Research 
42  * National Center for Supercomputing Applications 
43  * University of Illinois at Urbana-Champaign 
44  * http://www.ncsa.uiuc.edu
45  * ________________________________________________________________ 
46  *
47  * Listener.cpp
48  * by Mark Gates <mgates@nlanr.net> 
49  * &  Ajay Tirumala <tirumala@ncsa.uiuc.edu> 
50  * ------------------------------------------------------------------- 
51  * Listener sets up a socket listening on the server host. For each 
52  * connected socket that accept() returns, this creates a Server 
53  * socket and spawns a thread for it. 
54  * 
55  * Changes to the latest version. Listener will run as a daemon 
56  * Multicast Server is now Multi-threaded 
57  * ------------------------------------------------------------------- 
58  * headers 
59  * uses 
60  *   <stdlib.h> 
61  *   <stdio.h> 
62  *   <string.h> 
63  *   <errno.h> 
64  * 
65  *   <sys/types.h> 
66  *   <unistd.h> 
67  * 
68  *   <netdb.h> 
69  *   <netinet/in.h> 
70  *   <sys/socket.h> 
71  * ------------------------------------------------------------------- */ 
72
73
74 #define HEADERS() 
75
76 #include "headers.h" 
77 #include "Listener.hpp"
78 #include "SocketAddr.h"
79 #include "PerfSocket.hpp"
80 #include "List.h"
81 #include "util.h" 
82
83 /* ------------------------------------------------------------------- 
84  * Stores local hostname and socket info. 
85  * ------------------------------------------------------------------- */ 
86
87 Listener::Listener( thread_Settings *inSettings ) {
88
89     mClients = inSettings->mThreads;
90     mBuf = NULL;
91     mSettings = inSettings;
92
93     // initialize buffer
94     mBuf = new char[ mSettings->mBufLen ];
95
96     // open listening socket 
97     Listen( ); 
98     ReportSettings( inSettings );
99
100 } // end Listener 
101
102 /* ------------------------------------------------------------------- 
103  * Delete memory (buffer). 
104  * ------------------------------------------------------------------- */ 
105 Listener::~Listener() {
106     if ( mSettings->mSock != INVALID_SOCKET ) {
107         int rc = close( mSettings->mSock );
108         WARN_errno( rc == SOCKET_ERROR, "close" );
109         mSettings->mSock = INVALID_SOCKET;
110     }
111     DELETE_ARRAY( mBuf );
112 } // end ~Listener 
113
114 /* ------------------------------------------------------------------- 
115  * Listens for connections and starts Servers to handle data. 
116  * For TCP, each accepted connection spawns a Server thread. 
117  * For datagram-oriented protocols, spawn a new Server thread.
118  * ------------------------------------------------------------------- */ 
119 void Listener::Run(void)
120 {
121 #ifdef sun
122     if ( ( isPacketOriented( mSettings ) &&
123            isMulticast( mSettings )      &&
124           !isSingleUDP( mSettings )    ) || isSingleUDP( mSettings ) ) {
125         UDPSingleServer();
126 #else
127     if ( isSingleUDP( mSettings ) ) {
128         UDPSingleServer();
129 #endif
130     } else {
131         bool client = (mSettings->mHost != NULL),
132              mCount = (mSettings->mThreads != 0);
133         thread_Settings *tempSettings = NULL;
134         Iperf_ListEntry *exist, *listtemp;
135         client_hdr* hdr;
136
137         Settings_Copy( mSettings, &server );
138         server->mThreadMode = kMode_Server;
139
140         // Accept each packet, 
141         // If there is no existing client, then start  
142         // a new thread to service the new client 
143         // The listener runs in a single thread 
144         // Thread per client model is followed 
145         do {
146             // Get a new socket
147             Accept( server );
148             if ( server->mSock == INVALID_SOCKET ) {
149                 break;
150             }
151             if ( sInterupted != 0 ) {
152                 close( server->mSock );
153                 break;
154             }
155
156             // Reset Single Client Stuff
157             if ( isSingleClient( mSettings ) && clients == NULL ) {
158                 mClients--;
159                 client = true;
160                 // Once all the server threads exit then quit
161                 // Must keep going in case this client sends
162                 // more streams
163                 if ( mClients == 0 ) {
164                     thread_release_nonterm( 0 );
165                     mClients = 1;
166                 }
167             }
168             // Copy peer address for individual/bidirectional dual-test
169             // NOTE: There is no longer a reverse-lookup for the peer to
170             //       check if it is allowed. The reason is that it simply
171             //       gets too complex: compare v4/v6, v4/v4, v6/v4, v6/v6
172             if (client)
173                 mSettings->peer = server->peer;
174     
175             // Create an entry for the connection list
176             listtemp = new Iperf_ListEntry;
177             memcpy(listtemp, &server->peer, sizeof(server->peer));
178             listtemp->next = NULL;
179     
180             // See if we need to do summing
181             Mutex_Lock( &clients_mutex );
182             exist = Iperf_hostpresent( &server->peer, clients); 
183     
184             if ( exist != NULL ) {
185                 // Copy group ID
186                 listtemp->holder = exist->holder;
187                 server->multihdr = exist->holder;
188             } else {
189                 server->mThreads = 0;
190                 Mutex_Lock( &groupCond );
191                 groupID--;
192                 listtemp->holder = InitMulti( server, groupID );
193                 server->multihdr = listtemp->holder;
194                 Mutex_Unlock( &groupCond );
195             }
196     
197             // Store entry in connection list
198             Iperf_pushback( listtemp, &clients ); 
199             Mutex_Unlock( &clients_mutex ); 
200     
201             tempSettings = NULL;
202             if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) {
203                 if ( !isConnectionLess(mSettings)) {
204                     hdr = (client_hdr*) mBuf;
205
206                     // TCP/DCCP does not have the info yet
207                     if ( recv( server->mSock, (char*)hdr, sizeof(client_hdr), 0) > 0 ) {
208                         Settings_GenerateClientSettings( server, &tempSettings, hdr );
209                     }
210                 } else {
211                     hdr = (client_hdr *) ( ((dgram_record*)mBuf) + 1 );
212                     Settings_GenerateClientSettings( server, &tempSettings, hdr );
213                 }
214             }
215     
216             if ( tempSettings != NULL ) {
217                 client_init( tempSettings );
218                 if ( tempSettings->mMode == kTest_DualTest ) {
219 #ifdef HAVE_THREAD
220                     server->runNow =  tempSettings;
221 #else
222                     server->runNext = tempSettings;
223 #endif
224                 } else {
225                     server->runNext =  tempSettings;
226                 }
227             }
228     
229             // Start the server
230             thread_start( server );
231     
232             // create a new socket
233             if ( isConnectionLess( mSettings ) ) {
234                 mSettings->mSock = -1; 
235                 Listen( );
236             }
237     
238             // Prep for next connection
239             if ( !isSingleClient( mSettings ) ) {
240                 mClients--;
241             }
242             Settings_Copy( mSettings, &server );
243             server->mThreadMode = kMode_Server;
244         } while ( !sInterupted && (!mCount || ( mCount && mClients > 0 )) );
245     
246         Settings_Destroy( server );
247     }
248 } // end Run 
249
250 /* -------------------------------------------------------------------
251  * Setup a socket listening on a port.
252  * For connection-oriented protocols, this calls bind() and listen().
253  * For connection-less protocols, this just calls bind().
254  * If inLocalhost is not null, bind to that address rather than the
255  * wildcard server address, specifying what incoming interface to
256  * accept connections on.
257  * ------------------------------------------------------------------- */
258 void Listener::Listen()
259 {
260     MakeSocket(mSettings);
261
262     // default backlog traditionally 5
263     if (!isConnectionLess(mSettings) && listen(mSettings->mSock, 5) < 0)
264         WARN_errno( 1, "listen" );
265
266     // if multicast, join the group specified earlier via -B
267     if (isMulticast(mSettings))
268         McastJoin();
269 }
270
271 /* -------------------------------------------------------------------
272  * Joins the multicast group, with the default interface.
273  * ------------------------------------------------------------------- */
274
275 void Listener::McastJoin()
276 {
277     if (SockAddr_isIPv6(&mSettings->local)) {
278         struct ipv6_mreq mreq;
279
280         memset(&mreq, 0, sizeof(mreq));
281         memcpy(&mreq.ipv6mr_multiaddr, &((struct sockaddr_in6*)&mSettings->local)->sin6_addr,
282                sizeof(mreq.ipv6mr_multiaddr));
283
284         mreq.ipv6mr_interface = mSettings->mMcastIface;
285
286         if (setsockopt(mSettings->mSock, IPPROTO_IPV6,
287                                          IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
288                 die("IPv6 multicast join failed: did you use -j/-J?");
289
290     } else {
291         struct ip_mreqn mreq;
292
293         memset(&mreq, 0, sizeof(mreq));
294         memcpy(&mreq.imr_multiaddr, &((struct sockaddr_in*)&mSettings->local)->sin_addr,
295                 sizeof(mreq.imr_multiaddr));
296
297         mreq.imr_ifindex = mSettings->mMcastIface;
298
299         if (setsockopt(mSettings->mSock, IPPROTO_IP,
300                                          IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
301                 die("IPv4 multicast join failed: did you use -j/-J?");
302     }
303 }
304
305 /* -------------------------------------------------------------------
306  * Sets the Multicast TTL for outgoing packets.
307  * ------------------------------------------------------------------- */
308
309 void Listener::McastSetTTL( int val ) {
310 #ifdef HAVE_MULTICAST
311     if ( !SockAddr_isIPv6( &mSettings->local ) ) {
312         int rc = setsockopt( mSettings->mSock, IPPROTO_IP, IP_MULTICAST_TTL,
313                              (char*) &val, sizeof(val));
314         WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
315     }
316 #ifdef HAVE_IPV6_MULTICAST
317       else {
318         int rc = setsockopt( mSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
319                              (char*) &val, sizeof(val));
320         WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
321     }
322 #endif
323 #endif
324 }
325 // end McastSetTTL
326
327 /* -------------------------------------------------------------------
328  * After Listen() has setup mSock, this will block
329  * until a new connection arrives.
330  * ------------------------------------------------------------------- */
331
332 void Listener::Accept( thread_Settings *server )
333 {
334     socklen_t socklen = sizeof(server->peer);
335
336     if ( isConnectionLess( server ) ) {
337         /* ------------------------------------------------------------------- 
338          * Do the equivalent of an accept() call for connection-less sockets.
339          * This waits on a listening datagram socket until we get a datagram.
340          * ------------------------------------------------------------------- */
341         int rc;
342         Iperf_ListEntry *exist;
343         int32_t datagramID;
344
345         server->mSock = INVALID_SOCKET;
346         while ( server->mSock == INVALID_SOCKET ) {
347             rc = recvfrom( mSettings->mSock, mBuf, mSettings->mBufLen, 0, 
348                            (struct sockaddr *)&server->peer, &socklen );
349             FAIL_errno( rc == SOCKET_ERROR, "recvfrom", mSettings );
350
351             Mutex_Lock( &clients_mutex );
352     
353             // Handle connection for datagram-based sockets.
354             exist = Iperf_present( &server->peer, clients);
355             datagramID = ntohl( ((dgram_record*) mBuf)->id );
356             if ( exist == NULL && datagramID >= 0 ) {
357                 server->mSock = mSettings->mSock;
358                 int rc = connect(server->mSock, (struct sockaddr *)&server->peer, socklen);
359                 FAIL_errno( rc == SOCKET_ERROR, "datagram-based connect", mSettings );
360             } else {
361                 server->mSock = INVALID_SOCKET;
362             }
363             Mutex_Unlock( &clients_mutex );
364         }
365     } else {
366         // Handles interupted accepts. Returns the newly connected socket.
367         do {
368             server->mSock = accept(mSettings->mSock, (struct sockaddr *)&server->peer, &socklen);
369         } while (server->mSock == INVALID_SOCKET);
370     }
371     // find out which local interface the connection uses
372     socklen = sizeof(server->local);
373     getsockname(server->mSock, (struct sockaddr *)&server->local, &socklen);
374 }
375
376 void Listener::UDPSingleServer( ) {
377     
378     bool client = (mSettings->mHost != NULL),
379          mCount = (mSettings->mThreads != 0);
380     thread_Settings *tempSettings = NULL;
381     Iperf_ListEntry *exist, *listtemp;
382     int rc;
383     int32_t datagramID;
384     socklen_t socklen = sizeof(server->peer);
385     ReportStruct *reportstruct = new ReportStruct;
386     dgram_record *dgram_hdr = (dgram_record *)mBuf;
387     
388     assert( isPacketOriented( mSettings ) );
389
390     Settings_Copy( mSettings, &server );
391     server->mThreadMode = kMode_Server;
392
393     // Accept each packet, 
394     // If there is no existing client, then start  
395     // a new report to service the new client 
396     // The listener runs in a single thread 
397     Mutex_Lock( &clients_mutex );
398     do {
399         // Get next packet
400         while ( sInterupted == 0) {
401             rc = recvfrom(mSettings->mSock, mBuf, mSettings->mBufLen, 0,
402                           (struct sockaddr *)&server->peer, &socklen);
403             if (rc == SOCKET_ERROR) {
404                 WARN_errno( 1, "recvfrom" );
405                 return;
406             }
407         
408             // Handle connection for datagram-based sockets.
409             exist = Iperf_present( &server->peer, clients);
410             datagramID = ntohl( dgram_hdr->id );
411             if ( datagramID >= 0 ) {
412                 if ( exist != NULL ) {
413                     // read the datagram ID and sentTime out of the buffer 
414                     reportstruct->packetID = datagramID; 
415                     reportstruct->sentTime.tv_sec = ntohl( dgram_hdr->tv_sec  );
416                     reportstruct->sentTime.tv_usec = ntohl( dgram_hdr->tv_usec );
417         
418                     reportstruct->packetLen = rc;
419                     gettimeofday( &(reportstruct->packetTime), NULL );
420         
421                     ReportPacket( exist->server->reporthdr, reportstruct );
422                 } else {
423                     Mutex_Lock( &groupCond );
424                     groupID--;
425                     server->mSock = -groupID;
426                     Mutex_Unlock( &groupCond );
427                     // make this a connected socket and determine local interface
428                     connect(mSettings->mSock, (sockaddr *)&server->peer, socklen);
429                     socklen = sizeof(server->local);
430                     getsockname(mSettings->mSock, (sockaddr *)&server->local, &socklen);
431                     break;
432                 }
433             } else {
434                 if ( exist != NULL ) {
435                     // read the datagram ID and sentTime out of the buffer 
436                     reportstruct->packetID = -datagramID; 
437                     reportstruct->sentTime.tv_sec = ntohl( dgram_hdr->tv_sec  );
438                     reportstruct->sentTime.tv_usec = ntohl( dgram_hdr->tv_usec );
439         
440                     reportstruct->packetLen = rc;
441                     gettimeofday( &(reportstruct->packetTime), NULL );
442         
443                     ReportPacket( exist->server->reporthdr, reportstruct );
444                     // stop timing 
445                     gettimeofday( &(reportstruct->packetTime), NULL );
446                     CloseReport( exist->server->reporthdr, reportstruct );
447         
448                     if ( rc > (int)(sizeof(dgram_record) + sizeof(server_hdr)) ) {
449                         server_hdr *hdr = (server_hdr *)(dgram_hdr + 1);
450                         Transfer_Info *stats = GetReport( exist->server->reporthdr );
451         
452                         hdr->flags        = htonl( HEADER_VERSION1 );
453                         hdr->total_len1   = htonl( (long) (stats->TotalLen >> 32) );
454                         hdr->total_len2   = htonl( (long) (stats->TotalLen & 0xFFFFFFFF) );
455                         hdr->stop_sec     = htonl( (long) stats->endTime );
456                         hdr->stop_usec    = htonl( (long)((stats->endTime - (long)stats->endTime)
457                                                           * rMillion));
458                         hdr->error_cnt    = htonl( stats->cntError );
459                         hdr->outorder_cnt = htonl( stats->cntOutofOrder );
460                         hdr->datagrams    = htonl( stats->cntDatagrams );
461                         hdr->jitter1      = htonl( (long) stats->jitter );
462                         hdr->jitter2      = htonl( (long) ((stats->jitter - (long)stats->jitter) 
463                                                            * rMillion) );
464         
465                     }
466                     EndReport( exist->server->reporthdr );
467                     exist->server->reporthdr = NULL;
468                     Iperf_delete( &(exist->server->peer), &clients );
469
470                 } else if ( rc > (int)(sizeof(dgram_record) + sizeof(server_hdr)) ) {
471                     server_hdr *hdr = (server_hdr *) (dgram_hdr + 1);
472                     hdr->flags = htonl( 0 );
473                 }
474                 if (write(mSettings->mSock, mBuf, mSettings->mBufLen) < 0)
475                         WARN_errno(1, "UDPSingleServer write");
476             }
477         }
478         if ( server->mSock == INVALID_SOCKET ) {
479             break;
480         }
481         if ( sInterupted != 0 ) {
482             close( server->mSock );
483             break;
484         }
485         // Reset Single Client Stuff
486         if ( isSingleClient( mSettings ) && clients == NULL ) {
487             mClients--;
488             client = true;
489             // Once all the server threads exit then quit
490             // Must keep going in case this client sends
491             // more streams
492             if ( mClients == 0 ) {
493                 thread_release_nonterm( 0 );
494                 mClients = 1;
495             }
496         }
497         // Copy peer address (see note in Listener::Run())
498         if (client)
499             mSettings->peer = server->peer;
500
501         // Create an entry for the connection list
502         listtemp = new Iperf_ListEntry;
503         memcpy(listtemp, &server->peer, sizeof(server->peer));
504         listtemp->server = server;
505         listtemp->next = NULL;
506
507         // See if we need to do summing
508         exist = Iperf_hostpresent( &server->peer, clients); 
509
510         if ( exist != NULL ) {
511             // Copy group ID
512             listtemp->holder = exist->holder;
513             server->multihdr = exist->holder;
514         } else {
515             server->mThreads = 0;
516             Mutex_Lock( &groupCond );
517             groupID--;
518             listtemp->holder = InitMulti( server, groupID );
519             server->multihdr = listtemp->holder;
520             Mutex_Unlock( &groupCond );
521         }
522
523         // Store entry in connection list
524         Iperf_pushback( listtemp, &clients ); 
525
526         tempSettings = NULL;
527         if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) {
528             client_hdr* hdr = (client_hdr *)(dgram_hdr + 1);
529
530             Settings_GenerateClientSettings(server, &tempSettings, hdr);
531         }
532
533
534         if ( tempSettings != NULL ) {
535             client_init( tempSettings );
536             if ( tempSettings->mMode == kTest_DualTest ) {
537 #ifdef HAVE_THREAD
538                 thread_start( tempSettings );
539 #else
540                 server->runNext = tempSettings;
541 #endif
542             } else {
543                 server->runNext =  tempSettings;
544             }
545         }
546         server->reporthdr = InitReport( server );
547
548         // Prep for next connection
549         if ( !isSingleClient( mSettings ) ) {
550             mClients--;
551         }
552         Settings_Copy( mSettings, &server );
553         server->mThreadMode = kMode_Server;
554     } while ( !sInterupted && (!mCount || ( mCount && mClients > 0 )) );
555     Mutex_Unlock( &clients_mutex );
556
557     Settings_Destroy( server );
558 }
559
560 /* -------------------------------------------------------------------- 
561  * Run the server as a daemon  
562  * --------------------------------------------------------------------*/ 
563
564 void Listener::runAsDaemon(const char *pname, int facility) {
565     pid_t pid; 
566
567     /* Create a child process & if successful, exit from the parent process */ 
568     if ( (pid = fork()) == -1 ) {
569         fprintf( stderr, "error in first child create\n");     
570         exit(0); 
571     } else if ( pid != 0 ) {
572         exit(0); 
573     }
574
575     /* Try becoming the session leader, once the parent exits */
576     if ( setsid() == -1 ) {           /* Become the session leader */ 
577         fprintf( stderr, "Cannot change the session group leader\n"); 
578     } else {
579     } 
580     signal(SIGHUP,SIG_IGN); 
581
582
583     /* Now fork() and get released from the terminal */  
584     if ( (pid = fork()) == -1 ) {
585         fprintf( stderr, "error\n");   
586         exit(0); 
587     } else if ( pid != 0 ) {
588         exit(0); 
589     }
590
591     chdir("."); 
592     fprintf( stderr, "Running Iperf Server as a daemon\n"); 
593     fprintf( stderr, "The Iperf daemon process ID : %d\n",((int)getpid())); 
594     fflush(stderr); 
595
596     fclose(stdin); 
597 }
598