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 ) && !isSingleUDP( mSettings ) ) {
127 if ( ( isUDP( mSettings ) &&
128 isMulticast( mSettings ) &&
129 !isSingleUDP( mSettings ) ) ||
130 isSingleUDP( mSettings ) ) {
134 if ( isSingleUDP( mSettings ) ) {
140 bool client = false, UDP = isUDP( mSettings ), mCount = (mSettings->mThreads != 0);
141 thread_Settings *tempSettings = NULL;
142 Iperf_ListEntry *exist, *listtemp;
143 client_hdr* hdr = ( UDP ? (client_hdr*) (((UDP_datagram*)mBuf) + 1) :
146 if ( mSettings->mHost != NULL ) {
148 SockAddr_remoteAddr( mSettings );
150 Settings_Copy( mSettings, &server );
151 server->mThreadMode = kMode_Server;
154 // Accept each packet,
155 // If there is no existing client, then start
156 // a new thread to service the new client
157 // The listener runs in a single thread
158 // Thread per client model is followed
162 if ( server->mSock == INVALID_SOCKET ) {
165 if ( sInterupted != 0 ) {
166 close( server->mSock );
169 // Reset Single Client Stuff
170 if ( isSingleClient( mSettings ) && clients == NULL ) {
171 mSettings->peer = server->peer;
174 // Once all the server threads exit then quit
175 // Must keep going in case this client sends
177 if ( mClients == 0 ) {
178 thread_release_nonterm( 0 );
182 // Verify that it is allowed
184 if ( !SockAddr_Hostare_Equal( (sockaddr*) &mSettings->peer,
185 (sockaddr*) &server->peer ) ) {
186 // Not allowed try again
187 close( server->mSock );
188 if ( isUDP( mSettings ) ) {
189 mSettings->mSock = -1;
196 // Create an entry for the connection list
197 listtemp = new Iperf_ListEntry;
198 memcpy(listtemp, &server->peer, sizeof(iperf_sockaddr));
199 listtemp->next = NULL;
201 // See if we need to do summing
202 Mutex_Lock( &clients_mutex );
203 exist = Iperf_hostpresent( &server->peer, clients);
205 if ( exist != NULL ) {
207 listtemp->holder = exist->holder;
208 server->multihdr = exist->holder;
210 server->mThreads = 0;
211 Mutex_Lock( &groupCond );
213 listtemp->holder = InitMulti( server, groupID );
214 server->multihdr = listtemp->holder;
215 Mutex_Unlock( &groupCond );
218 // Store entry in connection list
219 Iperf_pushback( listtemp, &clients );
220 Mutex_Unlock( &clients_mutex );
223 if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) {
225 // TCP does not have the info yet
226 if ( recv( server->mSock, (char*)hdr, sizeof(client_hdr), 0) > 0 ) {
227 Settings_GenerateClientSettings( server, &tempSettings,
231 Settings_GenerateClientSettings( server, &tempSettings,
237 if ( tempSettings != NULL ) {
238 client_init( tempSettings );
239 if ( tempSettings->mMode == kTest_DualTest ) {
241 server->runNow = tempSettings;
243 server->runNext = tempSettings;
246 server->runNext = tempSettings;
251 #if defined(WIN32) && defined(HAVE_THREAD)
253 // WIN32 does bad UDP handling so run single threaded
254 if ( server->runNow != NULL ) {
255 thread_start( server->runNow );
257 server_spawn( server );
258 if ( server->runNext != NULL ) {
259 thread_start( server->runNext );
263 thread_start( server );
265 // create a new socket
267 mSettings->mSock = -1;
271 // Prep for next connection
272 if ( !isSingleClient( mSettings ) ) {
275 Settings_Copy( mSettings, &server );
276 server->mThreadMode = kMode_Server;
277 } while ( !sInterupted && (!mCount || ( mCount && mClients > 0 )) );
279 Settings_Destroy( server );
283 /* -------------------------------------------------------------------
284 * Setup a socket listening on a port.
285 * For TCP, this calls bind() and listen().
286 * For UDP, this just calls bind().
287 * If inLocalhost is not null, bind to that address rather than the
288 * wildcard server address, specifying what incoming interface to
289 * accept connections on.
290 * ------------------------------------------------------------------- */
291 void Listener::Listen( ) {
294 SockAddr_localAddr( mSettings );
296 // create an internet TCP socket
297 int type = (isUDP( mSettings ) ? SOCK_DGRAM : SOCK_STREAM);
298 int domain = (SockAddr_isIPv6( &mSettings->local ) ?
307 if ( SockAddr_isMulticast( &mSettings->local ) ) {
308 // Multicast on Win32 requires special handling
309 mSettings->mSock = WSASocket( domain, type, 0, 0, 0, WSA_FLAG_MULTIPOINT_C_LEAF | WSA_FLAG_MULTIPOINT_D_LEAF );
310 WARN_errno( mSettings->mSock == INVALID_SOCKET, "socket" );
315 mSettings->mSock = socket( domain, type, 0 );
316 WARN_errno( mSettings->mSock == INVALID_SOCKET, "socket" );
319 SetSocketOptions( mSettings );
321 // reuse the address, so we can run if a former server was killed off
323 Socklen_t len = sizeof(boolean);
324 setsockopt( mSettings->mSock, SOL_SOCKET, SO_REUSEADDR, (char*) &boolean, len );
326 // bind socket to server address
328 if ( SockAddr_isMulticast( &mSettings->local ) ) {
329 // Multicast on Win32 requires special handling
330 rc = WSAJoinLeaf( mSettings->mSock, (sockaddr*) &mSettings->local, mSettings->size_local,0,0,0,0,JL_BOTH);
331 WARN_errno( rc == SOCKET_ERROR, "WSAJoinLeaf (aka bind)" );
335 rc = bind( mSettings->mSock, (sockaddr*) &mSettings->local, mSettings->size_local );
336 WARN_errno( rc == SOCKET_ERROR, "bind" );
338 // listen for connections (TCP only).
339 // default backlog traditionally 5
340 if ( !isUDP( mSettings ) ) {
341 rc = listen( mSettings->mSock, 5 );
342 WARN_errno( rc == SOCKET_ERROR, "listen" );
346 // if multicast, join the group
347 if ( SockAddr_isMulticast( &mSettings->local ) ) {
353 /* -------------------------------------------------------------------
354 * Joins the multicast group, with the default interface.
355 * ------------------------------------------------------------------- */
357 void Listener::McastJoin( ) {
358 #ifdef HAVE_MULTICAST
359 if ( !SockAddr_isIPv6( &mSettings->local ) ) {
362 memcpy( &mreq.imr_multiaddr, SockAddr_get_in_addr( &mSettings->local ),
363 sizeof(mreq.imr_multiaddr));
365 mreq.imr_interface.s_addr = htonl( INADDR_ANY );
367 int rc = setsockopt( mSettings->mSock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
368 (char*) &mreq, sizeof(mreq));
369 WARN_errno( rc == SOCKET_ERROR, "multicast join" );
371 #ifdef HAVE_IPV6_MULTICAST
373 struct ipv6_mreq mreq;
375 memcpy( &mreq.ipv6mr_multiaddr, SockAddr_get_in6_addr( &mSettings->local ),
376 sizeof(mreq.ipv6mr_multiaddr));
378 mreq.ipv6mr_interface = 0;
380 int rc = setsockopt( mSettings->mSock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
381 (char*) &mreq, sizeof(mreq));
382 WARN_errno( rc == SOCKET_ERROR, "multicast join" );
389 /* -------------------------------------------------------------------
390 * Sets the Multicast TTL for outgoing packets.
391 * ------------------------------------------------------------------- */
393 void Listener::McastSetTTL( int val ) {
394 #ifdef HAVE_MULTICAST
395 if ( !SockAddr_isIPv6( &mSettings->local ) ) {
396 int rc = setsockopt( mSettings->mSock, IPPROTO_IP, IP_MULTICAST_TTL,
397 (char*) &val, sizeof(val));
398 WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
400 #ifdef HAVE_IPV6_MULTICAST
402 int rc = setsockopt( mSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
403 (char*) &val, sizeof(val));
404 WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
411 /* -------------------------------------------------------------------
412 * After Listen() has setup mSock, this will block
413 * until a new connection arrives.
414 * ------------------------------------------------------------------- */
416 void Listener::Accept( thread_Settings *server ) {
418 server->size_peer = sizeof(iperf_sockaddr);
419 if ( isUDP( server ) ) {
420 /* -------------------------------------------------------------------
421 * Do the equivalent of an accept() call for UDP sockets. This waits
422 * on a listening UDP socket until we get a datagram.
423 * ------------------------------------------------------------------- */
425 Iperf_ListEntry *exist;
427 server->mSock = INVALID_SOCKET;
428 while ( server->mSock == INVALID_SOCKET ) {
429 rc = recvfrom( mSettings->mSock, mBuf, mSettings->mBufLen, 0,
430 (struct sockaddr*) &server->peer, &server->size_peer );
431 FAIL_errno( rc == SOCKET_ERROR, "recvfrom", mSettings );
433 Mutex_Lock( &clients_mutex );
435 // Handle connection for UDP sockets.
436 exist = Iperf_present( &server->peer, clients);
437 datagramID = ntohl( ((UDP_datagram*) mBuf)->id );
438 if ( exist == NULL && datagramID >= 0 ) {
439 server->mSock = mSettings->mSock;
440 int rc = connect( server->mSock, (struct sockaddr*) &server->peer,
442 FAIL_errno( rc == SOCKET_ERROR, "connect UDP", mSettings );
444 server->mSock = INVALID_SOCKET;
446 Mutex_Unlock( &clients_mutex );
449 // Handles interupted accepts. Returns the newly connected socket.
450 server->mSock = INVALID_SOCKET;
452 while ( server->mSock == INVALID_SOCKET ) {
453 // accept a connection
454 server->mSock = accept( mSettings->mSock,
455 (sockaddr*) &server->peer, &server->size_peer );
456 if ( server->mSock == INVALID_SOCKET && errno == EINTR ) {
461 server->size_local = sizeof(iperf_sockaddr);
462 getsockname( server->mSock, (sockaddr*) &server->local,
463 &server->size_local );
466 void Listener::UDPSingleServer( ) {
468 bool client = false, UDP = isUDP( mSettings ), mCount = (mSettings->mThreads != 0);
469 thread_Settings *tempSettings = NULL;
470 Iperf_ListEntry *exist, *listtemp;
473 client_hdr* hdr = ( UDP ? (client_hdr*) (((UDP_datagram*)mBuf) + 1) :
475 ReportStruct *reportstruct = new ReportStruct;
477 if ( mSettings->mHost != NULL ) {
479 SockAddr_remoteAddr( mSettings );
481 Settings_Copy( mSettings, &server );
482 server->mThreadMode = kMode_Server;
485 // Accept each packet,
486 // If there is no existing client, then start
487 // a new report to service the new client
488 // The listener runs in a single thread
489 Mutex_Lock( &clients_mutex );
492 while ( sInterupted == 0) {
493 server->size_peer = sizeof( iperf_sockaddr );
494 rc = recvfrom( mSettings->mSock, mBuf, mSettings->mBufLen, 0,
495 (struct sockaddr*) &server->peer, &server->size_peer );
496 WARN_errno( rc == SOCKET_ERROR, "recvfrom" );
497 if ( rc == SOCKET_ERROR ) {
502 // Handle connection for UDP sockets.
503 exist = Iperf_present( &server->peer, clients);
504 datagramID = ntohl( ((UDP_datagram*) mBuf)->id );
505 if ( datagramID >= 0 ) {
506 if ( exist != NULL ) {
507 // read the datagram ID and sentTime out of the buffer
508 reportstruct->packetID = datagramID;
509 reportstruct->sentTime.tv_sec = ntohl( ((UDP_datagram*) mBuf)->tv_sec );
510 reportstruct->sentTime.tv_usec = ntohl( ((UDP_datagram*) mBuf)->tv_usec );
512 reportstruct->packetLen = rc;
513 gettimeofday( &(reportstruct->packetTime), NULL );
515 ReportPacket( exist->server->reporthdr, reportstruct );
517 Mutex_Lock( &groupCond );
519 server->mSock = -groupID;
520 Mutex_Unlock( &groupCond );
521 server->size_local = sizeof(iperf_sockaddr);
522 getsockname( mSettings->mSock, (sockaddr*) &server->local,
523 &server->size_local );
527 if ( exist != NULL ) {
528 // read the datagram ID and sentTime out of the buffer
529 reportstruct->packetID = -datagramID;
530 reportstruct->sentTime.tv_sec = ntohl( ((UDP_datagram*) mBuf)->tv_sec );
531 reportstruct->sentTime.tv_usec = ntohl( ((UDP_datagram*) mBuf)->tv_usec );
533 reportstruct->packetLen = rc;
534 gettimeofday( &(reportstruct->packetTime), NULL );
536 ReportPacket( exist->server->reporthdr, reportstruct );
538 gettimeofday( &(reportstruct->packetTime), NULL );
539 CloseReport( exist->server->reporthdr, reportstruct );
541 if ( rc > (int) ( sizeof( UDP_datagram )
542 + sizeof( server_hdr ) ) ) {
543 UDP_datagram *UDP_Hdr;
546 UDP_Hdr = (UDP_datagram*) mBuf;
547 Transfer_Info *stats = GetReport( exist->server->reporthdr );
548 hdr = (server_hdr*) (UDP_Hdr+1);
550 hdr->flags = htonl( HEADER_VERSION1 );
551 hdr->total_len1 = htonl( (long) (stats->TotalLen >> 32) );
552 hdr->total_len2 = htonl( (long) (stats->TotalLen & 0xFFFFFFFF) );
553 hdr->stop_sec = htonl( (long) stats->endTime );
554 hdr->stop_usec = htonl( (long)((stats->endTime - (long)stats->endTime)
556 hdr->error_cnt = htonl( stats->cntError );
557 hdr->outorder_cnt = htonl( stats->cntOutofOrder );
558 hdr->datagrams = htonl( stats->cntDatagrams );
559 hdr->jitter1 = htonl( (long) stats->jitter );
560 hdr->jitter2 = htonl( (long) ((stats->jitter - (long)stats->jitter)
564 EndReport( exist->server->reporthdr );
565 exist->server->reporthdr = NULL;
566 Iperf_delete( &(exist->server->peer), &clients );
567 } else if ( rc > (int) ( sizeof( UDP_datagram )
568 + sizeof( server_hdr ) ) ) {
569 UDP_datagram *UDP_Hdr;
572 UDP_Hdr = (UDP_datagram*) mBuf;
573 hdr = (server_hdr*) (UDP_Hdr+1);
574 hdr->flags = htonl( 0 );
576 sendto( mSettings->mSock, mBuf, mSettings->mBufLen, 0,
577 (struct sockaddr*) &server->peer, server->size_peer);
580 if ( server->mSock == INVALID_SOCKET ) {
583 if ( sInterupted != 0 ) {
584 close( server->mSock );
587 // Reset Single Client Stuff
588 if ( isSingleClient( mSettings ) && clients == NULL ) {
589 mSettings->peer = server->peer;
592 // Once all the server threads exit then quit
593 // Must keep going in case this client sends
595 if ( mClients == 0 ) {
596 thread_release_nonterm( 0 );
600 // Verify that it is allowed
602 if ( !SockAddr_Hostare_Equal( (sockaddr*) &mSettings->peer,
603 (sockaddr*) &server->peer ) ) {
604 // Not allowed try again
605 connect( mSettings->mSock,
606 (sockaddr*) &server->peer,
608 close( mSettings->mSock );
609 mSettings->mSock = -1;
615 // Create an entry for the connection list
616 listtemp = new Iperf_ListEntry;
617 memcpy(listtemp, &server->peer, sizeof(iperf_sockaddr));
618 listtemp->server = server;
619 listtemp->next = NULL;
621 // See if we need to do summing
622 exist = Iperf_hostpresent( &server->peer, clients);
624 if ( exist != NULL ) {
626 listtemp->holder = exist->holder;
627 server->multihdr = exist->holder;
629 server->mThreads = 0;
630 Mutex_Lock( &groupCond );
632 listtemp->holder = InitMulti( server, groupID );
633 server->multihdr = listtemp->holder;
634 Mutex_Unlock( &groupCond );
637 // Store entry in connection list
638 Iperf_pushback( listtemp, &clients );
641 if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) {
642 Settings_GenerateClientSettings( server, &tempSettings,
647 if ( tempSettings != NULL ) {
648 client_init( tempSettings );
649 if ( tempSettings->mMode == kTest_DualTest ) {
651 thread_start( tempSettings );
653 server->runNext = tempSettings;
656 server->runNext = tempSettings;
659 server->reporthdr = InitReport( server );
661 // Prep for next connection
662 if ( !isSingleClient( mSettings ) ) {
665 Settings_Copy( mSettings, &server );
666 server->mThreadMode = kMode_Server;
667 } while ( !sInterupted && (!mCount || ( mCount && mClients > 0 )) );
668 Mutex_Unlock( &clients_mutex );
670 Settings_Destroy( server );
673 /* --------------------------------------------------------------------
674 * Run the server as a daemon
675 * --------------------------------------------------------------------*/
677 void Listener::runAsDaemon(const char *pname, int facility) {
681 /* Create a child process & if successful, exit from the parent process */
682 if ( (pid = fork()) == -1 ) {
683 fprintf( stderr, "error in first child create\n");
685 } else if ( pid != 0 ) {
689 /* Try becoming the session leader, once the parent exits */
690 if ( setsid() == -1 ) { /* Become the session leader */
691 fprintf( stderr, "Cannot change the session group leader\n");
694 signal(SIGHUP,SIG_IGN);
697 /* Now fork() and get released from the terminal */
698 if ( (pid = fork()) == -1 ) {
699 fprintf( stderr, "error\n");
701 } else if ( pid != 0 ) {
706 fprintf( stderr, "Running Iperf Server as a daemon\n");
707 fprintf( stderr, "The Iperf daemon process ID : %d\n",((int)getpid()));
712 fprintf( stderr, "Use the precompiled windows version for service (daemon) option\n");