1 /*---------------------------------------------------------------
2 * Copyright (c) 1999,2000,2001,2002,2003
3 * The Board of Trustees of the University of Illinois
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:
16 * Redistributions of source code must retain the above
17 * copyright notice, this list of conditions and
18 * the following disclaimers.
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.
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.
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 * ________________________________________________________________
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.
55 * Changes to the latest version. Listener will run as a daemon
56 * Multicast Server is now Multi-threaded
57 * -------------------------------------------------------------------
71 * ------------------------------------------------------------------- */
77 #include "Listener.hpp"
78 #include "SocketAddr.h"
79 #include "PerfSocket.hpp"
83 /* -------------------------------------------------------------------
84 * Stores local hostname and socket info.
85 * ------------------------------------------------------------------- */
87 Listener::Listener( thread_Settings *inSettings ) {
89 mClients = inSettings->mThreads;
91 mSettings = inSettings;
94 mBuf = new char[ mSettings->mBufLen ];
96 // open listening socket
98 ReportSettings( inSettings );
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;
111 DELETE_ARRAY( mBuf );
114 /* -------------------------------------------------------------------
115 * Listens for connections and starts Servers to handle data.
116 * For TCP, each accepted connection spawns a Server thread.
117 * For UDP, handle all data in this thread for Win32 Only, otherwise
118 * spawn a new Server thread.
119 * ------------------------------------------------------------------- */
120 void Listener::Run( void ) {
122 if ( ( isUDP( mSettings ) &&
123 isMulticast( mSettings ) &&
124 !isSingleUDP( mSettings ) ) ||
125 isSingleUDP( mSettings ) ) {
129 if ( isSingleUDP( mSettings ) ) {
134 bool client = false, UDP = isUDP( mSettings ), mCount = (mSettings->mThreads != 0);
135 thread_Settings *tempSettings = NULL;
136 Iperf_ListEntry *exist, *listtemp;
137 client_hdr* hdr = ( UDP ? (client_hdr*) (((UDP_datagram*)mBuf) + 1) :
140 if ( mSettings->mHost != NULL ) {
142 SockAddr_remoteAddr( mSettings );
144 Settings_Copy( mSettings, &server );
145 server->mThreadMode = kMode_Server;
148 // Accept each packet,
149 // If there is no existing client, then start
150 // a new thread to service the new client
151 // The listener runs in a single thread
152 // Thread per client model is followed
156 if ( server->mSock == INVALID_SOCKET ) {
159 if ( sInterupted != 0 ) {
160 close( server->mSock );
163 // Reset Single Client Stuff
164 if ( isSingleClient( mSettings ) && clients == NULL ) {
165 mSettings->peer = server->peer;
168 // Once all the server threads exit then quit
169 // Must keep going in case this client sends
171 if ( mClients == 0 ) {
172 thread_release_nonterm( 0 );
176 // Verify that it is allowed
178 if ( !SockAddr_Hostare_Equal( (sockaddr*) &mSettings->peer,
179 (sockaddr*) &server->peer ) ) {
180 // Not allowed try again
181 close( server->mSock );
182 if ( isUDP( mSettings ) ) {
183 mSettings->mSock = -1;
190 // Create an entry for the connection list
191 listtemp = new Iperf_ListEntry;
192 memcpy(listtemp, &server->peer, sizeof(iperf_sockaddr));
193 listtemp->next = NULL;
195 // See if we need to do summing
196 Mutex_Lock( &clients_mutex );
197 exist = Iperf_hostpresent( &server->peer, clients);
199 if ( exist != NULL ) {
201 listtemp->holder = exist->holder;
202 server->multihdr = exist->holder;
204 server->mThreads = 0;
205 Mutex_Lock( &groupCond );
207 listtemp->holder = InitMulti( server, groupID );
208 server->multihdr = listtemp->holder;
209 Mutex_Unlock( &groupCond );
212 // Store entry in connection list
213 Iperf_pushback( listtemp, &clients );
214 Mutex_Unlock( &clients_mutex );
217 if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) {
219 // TCP does not have the info yet
220 if ( recv( server->mSock, (char*)hdr, sizeof(client_hdr), 0) > 0 ) {
221 Settings_GenerateClientSettings( server, &tempSettings,
225 Settings_GenerateClientSettings( server, &tempSettings,
231 if ( tempSettings != NULL ) {
232 client_init( tempSettings );
233 if ( tempSettings->mMode == kTest_DualTest ) {
235 server->runNow = tempSettings;
237 server->runNext = tempSettings;
240 server->runNext = tempSettings;
245 thread_start( server );
247 // create a new socket
249 mSettings->mSock = -1;
253 // Prep for next connection
254 if ( !isSingleClient( mSettings ) ) {
257 Settings_Copy( mSettings, &server );
258 server->mThreadMode = kMode_Server;
259 } while ( !sInterupted && (!mCount || ( mCount && mClients > 0 )) );
261 Settings_Destroy( server );
265 /* -------------------------------------------------------------------
266 * Setup a socket listening on a port.
267 * For TCP, this calls bind() and listen().
268 * For UDP, this just calls bind().
269 * If inLocalhost is not null, bind to that address rather than the
270 * wildcard server address, specifying what incoming interface to
271 * accept connections on.
272 * ------------------------------------------------------------------- */
273 void Listener::Listen( ) {
276 SockAddr_localAddr( mSettings );
278 // create an internet TCP socket
279 int type = (isUDP( mSettings ) ? SOCK_DGRAM : SOCK_STREAM);
280 int domain = (SockAddr_isIPv6( &mSettings->local ) ?
288 mSettings->mSock = socket( domain, type, 0 );
289 WARN_errno( mSettings->mSock == INVALID_SOCKET, "socket" );
291 SetSocketOptions( mSettings );
293 // reuse the address, so we can run if a former server was killed off
295 Socklen_t len = sizeof(boolean);
296 setsockopt( mSettings->mSock, SOL_SOCKET, SO_REUSEADDR, (char*) &boolean, len );
298 // bind socket to server address
299 rc = bind( mSettings->mSock, (sockaddr*) &mSettings->local, mSettings->size_local );
300 WARN_errno( rc == SOCKET_ERROR, "bind" );
302 // listen for connections (TCP only).
303 // default backlog traditionally 5
304 if ( !isUDP( mSettings ) ) {
305 rc = listen( mSettings->mSock, 5 );
306 WARN_errno( rc == SOCKET_ERROR, "listen" );
309 // if multicast, join the group
310 if ( SockAddr_isMulticast( &mSettings->local ) ) {
315 /* -------------------------------------------------------------------
316 * Joins the multicast group, with the default interface.
317 * ------------------------------------------------------------------- */
319 void Listener::McastJoin( ) {
320 #ifdef HAVE_MULTICAST
321 if ( !SockAddr_isIPv6( &mSettings->local ) ) {
324 memcpy( &mreq.imr_multiaddr, SockAddr_get_in_addr( &mSettings->local ),
325 sizeof(mreq.imr_multiaddr));
327 mreq.imr_interface.s_addr = htonl( INADDR_ANY );
329 int rc = setsockopt( mSettings->mSock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
330 (char*) &mreq, sizeof(mreq));
331 WARN_errno( rc == SOCKET_ERROR, "multicast join" );
333 #ifdef HAVE_IPV6_MULTICAST
335 struct ipv6_mreq mreq;
337 memcpy( &mreq.ipv6mr_multiaddr, SockAddr_get_in6_addr( &mSettings->local ),
338 sizeof(mreq.ipv6mr_multiaddr));
340 mreq.ipv6mr_interface = 0;
342 int rc = setsockopt( mSettings->mSock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
343 (char*) &mreq, sizeof(mreq));
344 WARN_errno( rc == SOCKET_ERROR, "multicast join" );
351 /* -------------------------------------------------------------------
352 * Sets the Multicast TTL for outgoing packets.
353 * ------------------------------------------------------------------- */
355 void Listener::McastSetTTL( int val ) {
356 #ifdef HAVE_MULTICAST
357 if ( !SockAddr_isIPv6( &mSettings->local ) ) {
358 int rc = setsockopt( mSettings->mSock, IPPROTO_IP, IP_MULTICAST_TTL,
359 (char*) &val, sizeof(val));
360 WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
362 #ifdef HAVE_IPV6_MULTICAST
364 int rc = setsockopt( mSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
365 (char*) &val, sizeof(val));
366 WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
373 /* -------------------------------------------------------------------
374 * After Listen() has setup mSock, this will block
375 * until a new connection arrives.
376 * ------------------------------------------------------------------- */
378 void Listener::Accept( thread_Settings *server ) {
380 server->size_peer = sizeof(iperf_sockaddr);
381 if ( isUDP( server ) ) {
382 /* -------------------------------------------------------------------
383 * Do the equivalent of an accept() call for UDP sockets. This waits
384 * on a listening UDP socket until we get a datagram.
385 * ------------------------------------------------------------------- */
387 Iperf_ListEntry *exist;
389 server->mSock = INVALID_SOCKET;
390 while ( server->mSock == INVALID_SOCKET ) {
391 rc = recvfrom( mSettings->mSock, mBuf, mSettings->mBufLen, 0,
392 (struct sockaddr*) &server->peer, &server->size_peer );
393 FAIL_errno( rc == SOCKET_ERROR, "recvfrom", mSettings );
395 Mutex_Lock( &clients_mutex );
397 // Handle connection for UDP sockets.
398 exist = Iperf_present( &server->peer, clients);
399 datagramID = ntohl( ((UDP_datagram*) mBuf)->id );
400 if ( exist == NULL && datagramID >= 0 ) {
401 server->mSock = mSettings->mSock;
402 int rc = connect( server->mSock, (struct sockaddr*) &server->peer,
404 FAIL_errno( rc == SOCKET_ERROR, "connect UDP", mSettings );
406 server->mSock = INVALID_SOCKET;
408 Mutex_Unlock( &clients_mutex );
411 // Handles interupted accepts. Returns the newly connected socket.
412 server->mSock = INVALID_SOCKET;
414 while ( server->mSock == INVALID_SOCKET ) {
415 // accept a connection
416 server->mSock = accept( mSettings->mSock,
417 (sockaddr*) &server->peer, &server->size_peer );
418 if ( server->mSock == INVALID_SOCKET && errno == EINTR ) {
423 server->size_local = sizeof(iperf_sockaddr);
424 getsockname( server->mSock, (sockaddr*) &server->local,
425 &server->size_local );
428 void Listener::UDPSingleServer( ) {
430 bool client = false, UDP = isUDP( mSettings ), mCount = (mSettings->mThreads != 0);
431 thread_Settings *tempSettings = NULL;
432 Iperf_ListEntry *exist, *listtemp;
435 client_hdr* hdr = ( UDP ? (client_hdr*) (((UDP_datagram*)mBuf) + 1) :
437 ReportStruct *reportstruct = new ReportStruct;
439 if ( mSettings->mHost != NULL ) {
441 SockAddr_remoteAddr( mSettings );
443 Settings_Copy( mSettings, &server );
444 server->mThreadMode = kMode_Server;
447 // Accept each packet,
448 // If there is no existing client, then start
449 // a new report to service the new client
450 // The listener runs in a single thread
451 Mutex_Lock( &clients_mutex );
454 while ( sInterupted == 0) {
455 server->size_peer = sizeof( iperf_sockaddr );
456 rc = recvfrom( mSettings->mSock, mBuf, mSettings->mBufLen, 0,
457 (struct sockaddr*) &server->peer, &server->size_peer );
458 WARN_errno( rc == SOCKET_ERROR, "recvfrom" );
459 if ( rc == SOCKET_ERROR ) {
464 // Handle connection for UDP sockets.
465 exist = Iperf_present( &server->peer, clients);
466 datagramID = ntohl( ((UDP_datagram*) mBuf)->id );
467 if ( datagramID >= 0 ) {
468 if ( exist != NULL ) {
469 // read the datagram ID and sentTime out of the buffer
470 reportstruct->packetID = datagramID;
471 reportstruct->sentTime.tv_sec = ntohl( ((UDP_datagram*) mBuf)->tv_sec );
472 reportstruct->sentTime.tv_usec = ntohl( ((UDP_datagram*) mBuf)->tv_usec );
474 reportstruct->packetLen = rc;
475 gettimeofday( &(reportstruct->packetTime), NULL );
477 ReportPacket( exist->server->reporthdr, reportstruct );
479 Mutex_Lock( &groupCond );
481 server->mSock = -groupID;
482 Mutex_Unlock( &groupCond );
483 server->size_local = sizeof(iperf_sockaddr);
484 getsockname( mSettings->mSock, (sockaddr*) &server->local,
485 &server->size_local );
489 if ( exist != NULL ) {
490 // read the datagram ID and sentTime out of the buffer
491 reportstruct->packetID = -datagramID;
492 reportstruct->sentTime.tv_sec = ntohl( ((UDP_datagram*) mBuf)->tv_sec );
493 reportstruct->sentTime.tv_usec = ntohl( ((UDP_datagram*) mBuf)->tv_usec );
495 reportstruct->packetLen = rc;
496 gettimeofday( &(reportstruct->packetTime), NULL );
498 ReportPacket( exist->server->reporthdr, reportstruct );
500 gettimeofday( &(reportstruct->packetTime), NULL );
501 CloseReport( exist->server->reporthdr, reportstruct );
503 if ( rc > (int) ( sizeof( UDP_datagram )
504 + sizeof( server_hdr ) ) ) {
505 UDP_datagram *UDP_Hdr;
508 UDP_Hdr = (UDP_datagram*) mBuf;
509 Transfer_Info *stats = GetReport( exist->server->reporthdr );
510 hdr = (server_hdr*) (UDP_Hdr+1);
512 hdr->flags = htonl( HEADER_VERSION1 );
513 hdr->total_len1 = htonl( (long) (stats->TotalLen >> 32) );
514 hdr->total_len2 = htonl( (long) (stats->TotalLen & 0xFFFFFFFF) );
515 hdr->stop_sec = htonl( (long) stats->endTime );
516 hdr->stop_usec = htonl( (long)((stats->endTime - (long)stats->endTime)
518 hdr->error_cnt = htonl( stats->cntError );
519 hdr->outorder_cnt = htonl( stats->cntOutofOrder );
520 hdr->datagrams = htonl( stats->cntDatagrams );
521 hdr->jitter1 = htonl( (long) stats->jitter );
522 hdr->jitter2 = htonl( (long) ((stats->jitter - (long)stats->jitter)
526 EndReport( exist->server->reporthdr );
527 exist->server->reporthdr = NULL;
528 Iperf_delete( &(exist->server->peer), &clients );
529 } else if ( rc > (int) ( sizeof( UDP_datagram )
530 + sizeof( server_hdr ) ) ) {
531 UDP_datagram *UDP_Hdr;
534 UDP_Hdr = (UDP_datagram*) mBuf;
535 hdr = (server_hdr*) (UDP_Hdr+1);
536 hdr->flags = htonl( 0 );
538 sendto( mSettings->mSock, mBuf, mSettings->mBufLen, 0,
539 (struct sockaddr*) &server->peer, server->size_peer);
542 if ( server->mSock == INVALID_SOCKET ) {
545 if ( sInterupted != 0 ) {
546 close( server->mSock );
549 // Reset Single Client Stuff
550 if ( isSingleClient( mSettings ) && clients == NULL ) {
551 mSettings->peer = server->peer;
554 // Once all the server threads exit then quit
555 // Must keep going in case this client sends
557 if ( mClients == 0 ) {
558 thread_release_nonterm( 0 );
562 // Verify that it is allowed
564 if ( !SockAddr_Hostare_Equal( (sockaddr*) &mSettings->peer,
565 (sockaddr*) &server->peer ) ) {
566 // Not allowed try again
567 connect( mSettings->mSock,
568 (sockaddr*) &server->peer,
570 close( mSettings->mSock );
571 mSettings->mSock = -1;
577 // Create an entry for the connection list
578 listtemp = new Iperf_ListEntry;
579 memcpy(listtemp, &server->peer, sizeof(iperf_sockaddr));
580 listtemp->server = server;
581 listtemp->next = NULL;
583 // See if we need to do summing
584 exist = Iperf_hostpresent( &server->peer, clients);
586 if ( exist != NULL ) {
588 listtemp->holder = exist->holder;
589 server->multihdr = exist->holder;
591 server->mThreads = 0;
592 Mutex_Lock( &groupCond );
594 listtemp->holder = InitMulti( server, groupID );
595 server->multihdr = listtemp->holder;
596 Mutex_Unlock( &groupCond );
599 // Store entry in connection list
600 Iperf_pushback( listtemp, &clients );
603 if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) {
604 Settings_GenerateClientSettings( server, &tempSettings,
609 if ( tempSettings != NULL ) {
610 client_init( tempSettings );
611 if ( tempSettings->mMode == kTest_DualTest ) {
613 thread_start( tempSettings );
615 server->runNext = tempSettings;
618 server->runNext = tempSettings;
621 server->reporthdr = InitReport( server );
623 // Prep for next connection
624 if ( !isSingleClient( mSettings ) ) {
627 Settings_Copy( mSettings, &server );
628 server->mThreadMode = kMode_Server;
629 } while ( !sInterupted && (!mCount || ( mCount && mClients > 0 )) );
630 Mutex_Unlock( &clients_mutex );
632 Settings_Destroy( server );
635 /* --------------------------------------------------------------------
636 * Run the server as a daemon
637 * --------------------------------------------------------------------*/
639 void Listener::runAsDaemon(const char *pname, int facility) {
642 /* Create a child process & if successful, exit from the parent process */
643 if ( (pid = fork()) == -1 ) {
644 fprintf( stderr, "error in first child create\n");
646 } else if ( pid != 0 ) {
650 /* Try becoming the session leader, once the parent exits */
651 if ( setsid() == -1 ) { /* Become the session leader */
652 fprintf( stderr, "Cannot change the session group leader\n");
655 signal(SIGHUP,SIG_IGN);
658 /* Now fork() and get released from the terminal */
659 if ( (pid = fork()) == -1 ) {
660 fprintf( stderr, "error\n");
662 } else if ( pid != 0 ) {
667 fprintf( stderr, "Running Iperf Server as a daemon\n");
668 fprintf( stderr, "The Iperf daemon process ID : %d\n",((int)getpid()));