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 datagram-oriented protocols, spawn a new Server thread.
118 * ------------------------------------------------------------------- */
119 void Listener::Run(void)
122 if ( ( isPacketOriented( mSettings ) &&
123 isMulticast( mSettings ) &&
124 !isSingleUDP( mSettings ) ) || isSingleUDP( mSettings ) ) {
127 if ( isSingleUDP( mSettings ) ) {
132 mCount = (mSettings->mThreads != 0);
133 thread_Settings *tempSettings = NULL;
134 Iperf_ListEntry *exist, *listtemp;
138 if ( mSettings->mHost != NULL ) {
140 SockAddr_remoteAddr( mSettings );
142 Settings_Copy( mSettings, &server );
143 server->mThreadMode = kMode_Server;
146 // Accept each packet,
147 // If there is no existing client, then start
148 // a new thread to service the new client
149 // The listener runs in a single thread
150 // Thread per client model is followed
154 if ( server->mSock == INVALID_SOCKET ) {
157 if ( sInterupted != 0 ) {
158 close( server->mSock );
161 // Reset Single Client Stuff
162 if ( isSingleClient( mSettings ) && clients == NULL ) {
163 mSettings->peer = server->peer;
166 // Once all the server threads exit then quit
167 // Must keep going in case this client sends
169 if ( mClients == 0 ) {
170 thread_release_nonterm( 0 );
174 // Verify that it is allowed
176 if ( !SockAddr_Hostare_Equal( (sockaddr*) &mSettings->peer,
177 (sockaddr*) &server->peer ) ) {
178 // Not allowed try again
179 close( server->mSock );
180 if ( isConnectionLess( mSettings ) ) {
181 mSettings->mSock = -1;
188 // Create an entry for the connection list
189 listtemp = new Iperf_ListEntry;
190 memcpy(listtemp, &server->peer, sizeof(iperf_sockaddr));
191 listtemp->next = NULL;
193 // See if we need to do summing
194 Mutex_Lock( &clients_mutex );
195 exist = Iperf_hostpresent( &server->peer, clients);
197 if ( exist != NULL ) {
199 listtemp->holder = exist->holder;
200 server->multihdr = exist->holder;
202 server->mThreads = 0;
203 Mutex_Lock( &groupCond );
205 listtemp->holder = InitMulti( server, groupID );
206 server->multihdr = listtemp->holder;
207 Mutex_Unlock( &groupCond );
210 // Store entry in connection list
211 Iperf_pushback( listtemp, &clients );
212 Mutex_Unlock( &clients_mutex );
215 if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) {
216 if ( !isConnectionLess(mSettings)) {
217 hdr = (client_hdr*) mBuf;
219 // TCP/DCCP does not have the info yet
220 if ( recv( server->mSock, (char*)hdr, sizeof(client_hdr), 0) > 0 ) {
221 Settings_GenerateClientSettings( server, &tempSettings, hdr );
224 hdr = (client_hdr *) ( ((dgram_record*)mBuf) + 1 );
225 Settings_GenerateClientSettings( server, &tempSettings, hdr );
229 if ( tempSettings != NULL ) {
230 client_init( tempSettings );
231 if ( tempSettings->mMode == kTest_DualTest ) {
233 server->runNow = tempSettings;
235 server->runNext = tempSettings;
238 server->runNext = tempSettings;
243 thread_start( server );
245 // create a new socket
246 if ( isConnectionLess( mSettings ) ) {
247 mSettings->mSock = -1;
251 // Prep for next connection
252 if ( !isSingleClient( mSettings ) ) {
255 Settings_Copy( mSettings, &server );
256 server->mThreadMode = kMode_Server;
257 } while ( !sInterupted && (!mCount || ( mCount && mClients > 0 )) );
259 Settings_Destroy( server );
263 /* -------------------------------------------------------------------
264 * Setup a socket listening on a port.
265 * For connection-oriented protocols, this calls bind() and listen().
266 * For connection-less protocols, this just calls bind().
267 * If inLocalhost is not null, bind to that address rather than the
268 * wildcard server address, specifying what incoming interface to
269 * accept connections on.
270 * ------------------------------------------------------------------- */
271 void Listener::Listen()
275 Socklen_t len = sizeof(boolean);
277 SockAddr_localAddr( mSettings );
278 MakeSocket( mSettings );
280 SetSocketOptions( mSettings );
282 // reuse the address, so we can run if a former server was killed off
283 setsockopt( mSettings->mSock, SOL_SOCKET, SO_REUSEADDR, (char*) &boolean, len );
285 // listen for connections (TCP/DCCP only).
286 rc = bind( mSettings->mSock, (sockaddr*) &mSettings->local, mSettings->size_local );
287 WARN_errno( rc == SOCKET_ERROR, "bind" );
289 // default backlog traditionally 5
290 if ( !isConnectionLess( mSettings ) ) {
291 rc = listen( mSettings->mSock, 5 );
292 WARN_errno( rc == SOCKET_ERROR, "listen" );
295 // if multicast, join the group
296 if ( SockAddr_isMulticast( &mSettings->local ) ) {
301 /* -------------------------------------------------------------------
302 * Joins the multicast group, with the default interface.
303 * ------------------------------------------------------------------- */
305 void Listener::McastJoin( ) {
306 #ifdef HAVE_MULTICAST
307 if ( !SockAddr_isIPv6( &mSettings->local ) ) {
310 memcpy( &mreq.imr_multiaddr, SockAddr_get_in_addr( &mSettings->local ),
311 sizeof(mreq.imr_multiaddr));
313 mreq.imr_interface.s_addr = htonl( INADDR_ANY );
315 int rc = setsockopt( mSettings->mSock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
316 (char*) &mreq, sizeof(mreq));
317 WARN_errno( rc == SOCKET_ERROR, "multicast join" );
319 #ifdef HAVE_IPV6_MULTICAST
321 struct ipv6_mreq mreq;
323 memcpy( &mreq.ipv6mr_multiaddr, SockAddr_get_in6_addr( &mSettings->local ),
324 sizeof(mreq.ipv6mr_multiaddr));
326 mreq.ipv6mr_interface = 0;
328 int rc = setsockopt( mSettings->mSock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
329 (char*) &mreq, sizeof(mreq));
330 WARN_errno( rc == SOCKET_ERROR, "multicast join" );
337 /* -------------------------------------------------------------------
338 * Sets the Multicast TTL for outgoing packets.
339 * ------------------------------------------------------------------- */
341 void Listener::McastSetTTL( int val ) {
342 #ifdef HAVE_MULTICAST
343 if ( !SockAddr_isIPv6( &mSettings->local ) ) {
344 int rc = setsockopt( mSettings->mSock, IPPROTO_IP, IP_MULTICAST_TTL,
345 (char*) &val, sizeof(val));
346 WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
348 #ifdef HAVE_IPV6_MULTICAST
350 int rc = setsockopt( mSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
351 (char*) &val, sizeof(val));
352 WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
359 /* -------------------------------------------------------------------
360 * After Listen() has setup mSock, this will block
361 * until a new connection arrives.
362 * ------------------------------------------------------------------- */
364 void Listener::Accept( thread_Settings *server )
366 server->size_peer = sizeof(iperf_sockaddr);
368 if ( isConnectionLess( server ) ) {
369 /* -------------------------------------------------------------------
370 * Do the equivalent of an accept() call for connection-less sockets.
371 * This waits on a listening datagram socket until we get a datagram.
372 * ------------------------------------------------------------------- */
374 Iperf_ListEntry *exist;
377 server->mSock = INVALID_SOCKET;
378 while ( server->mSock == INVALID_SOCKET ) {
379 rc = recvfrom( mSettings->mSock, mBuf, mSettings->mBufLen, 0,
380 (struct sockaddr*) &server->peer, &server->size_peer );
381 FAIL_errno( rc == SOCKET_ERROR, "recvfrom", mSettings );
383 Mutex_Lock( &clients_mutex );
385 // Handle connection for datagram-based sockets.
386 exist = Iperf_present( &server->peer, clients);
387 datagramID = ntohl( ((dgram_record*) mBuf)->id );
388 if ( exist == NULL && datagramID >= 0 ) {
389 server->mSock = mSettings->mSock;
390 int rc = connect( server->mSock, (struct sockaddr*) &server->peer,
392 FAIL_errno( rc == SOCKET_ERROR, "datagram-based connect", mSettings );
394 server->mSock = INVALID_SOCKET;
396 Mutex_Unlock( &clients_mutex );
399 // Handles interupted accepts. Returns the newly connected socket.
400 server->mSock = INVALID_SOCKET;
402 while ( server->mSock == INVALID_SOCKET ) {
403 // accept a connection
404 server->mSock = accept( mSettings->mSock,
405 (sockaddr*) &server->peer, &server->size_peer );
406 if ( server->mSock == INVALID_SOCKET && errno == EINTR ) {
411 server->size_local = sizeof(iperf_sockaddr);
412 getsockname( server->mSock, (sockaddr*) &server->local,
413 &server->size_local );
416 void Listener::UDPSingleServer( ) {
418 bool client = false, mCount = (mSettings->mThreads != 0);
419 thread_Settings *tempSettings = NULL;
420 Iperf_ListEntry *exist, *listtemp;
423 ReportStruct *reportstruct = new ReportStruct;
424 dgram_record *dgram_hdr = (dgram_record *)mBuf;
427 assert( isPacketOriented( mSettings ) );
429 if ( mSettings->mHost != NULL ) {
431 SockAddr_remoteAddr( mSettings );
433 Settings_Copy( mSettings, &server );
434 server->mThreadMode = kMode_Server;
437 // Accept each packet,
438 // If there is no existing client, then start
439 // a new report to service the new client
440 // The listener runs in a single thread
441 Mutex_Lock( &clients_mutex );
444 while ( sInterupted == 0) {
445 server->size_peer = sizeof( iperf_sockaddr );
446 rc = recvfrom( mSettings->mSock, mBuf, mSettings->mBufLen, 0,
447 (struct sockaddr*) &server->peer, &server->size_peer );
448 WARN_errno( rc == SOCKET_ERROR, "recvfrom" );
449 if ( rc == SOCKET_ERROR ) {
454 // Handle connection for datagram-based sockets.
455 exist = Iperf_present( &server->peer, clients);
456 datagramID = ntohl( dgram_hdr->id );
457 if ( datagramID >= 0 ) {
458 if ( exist != NULL ) {
459 // read the datagram ID and sentTime out of the buffer
460 reportstruct->packetID = datagramID;
461 reportstruct->sentTime.tv_sec = ntohl( dgram_hdr->tv_sec );
462 reportstruct->sentTime.tv_usec = ntohl( dgram_hdr->tv_usec );
464 reportstruct->packetLen = rc;
465 gettimeofday( &(reportstruct->packetTime), NULL );
467 ReportPacket( exist->server->reporthdr, reportstruct );
469 Mutex_Lock( &groupCond );
471 server->mSock = -groupID;
472 Mutex_Unlock( &groupCond );
473 server->size_local = sizeof(iperf_sockaddr);
474 getsockname( mSettings->mSock, (sockaddr*) &server->local,
475 &server->size_local );
479 if ( exist != NULL ) {
480 // read the datagram ID and sentTime out of the buffer
481 reportstruct->packetID = -datagramID;
482 reportstruct->sentTime.tv_sec = ntohl( dgram_hdr->tv_sec );
483 reportstruct->sentTime.tv_usec = ntohl( dgram_hdr->tv_usec );
485 reportstruct->packetLen = rc;
486 gettimeofday( &(reportstruct->packetTime), NULL );
488 ReportPacket( exist->server->reporthdr, reportstruct );
490 gettimeofday( &(reportstruct->packetTime), NULL );
491 CloseReport( exist->server->reporthdr, reportstruct );
493 if ( rc > (int)(sizeof(dgram_record) + sizeof(server_hdr)) ) {
494 server_hdr *hdr = (server_hdr *)(dgram_hdr + 1);
495 Transfer_Info *stats = GetReport( exist->server->reporthdr );
497 hdr->flags = htonl( HEADER_VERSION1 );
498 hdr->total_len1 = htonl( (long) (stats->TotalLen >> 32) );
499 hdr->total_len2 = htonl( (long) (stats->TotalLen & 0xFFFFFFFF) );
500 hdr->stop_sec = htonl( (long) stats->endTime );
501 hdr->stop_usec = htonl( (long)((stats->endTime - (long)stats->endTime)
503 hdr->error_cnt = htonl( stats->cntError );
504 hdr->outorder_cnt = htonl( stats->cntOutofOrder );
505 hdr->datagrams = htonl( stats->cntDatagrams );
506 hdr->jitter1 = htonl( (long) stats->jitter );
507 hdr->jitter2 = htonl( (long) ((stats->jitter - (long)stats->jitter)
511 EndReport( exist->server->reporthdr );
512 exist->server->reporthdr = NULL;
513 Iperf_delete( &(exist->server->peer), &clients );
515 } else if ( rc > (int)(sizeof(dgram_record) + sizeof(server_hdr)) ) {
516 server_hdr *hdr = (server_hdr *) (dgram_hdr + 1);
517 hdr->flags = htonl( 0 );
519 sendto( mSettings->mSock, mBuf, mSettings->mBufLen, 0,
520 (struct sockaddr*) &server->peer, server->size_peer);
523 if ( server->mSock == INVALID_SOCKET ) {
526 if ( sInterupted != 0 ) {
527 close( server->mSock );
530 // Reset Single Client Stuff
531 if ( isSingleClient( mSettings ) && clients == NULL ) {
532 mSettings->peer = server->peer;
535 // Once all the server threads exit then quit
536 // Must keep going in case this client sends
538 if ( mClients == 0 ) {
539 thread_release_nonterm( 0 );
543 // Verify that it is allowed
545 if ( !SockAddr_Hostare_Equal( (sockaddr*) &mSettings->peer,
546 (sockaddr*) &server->peer ) ) {
547 // Not allowed try again
548 connect( mSettings->mSock,
549 (sockaddr*) &server->peer,
551 close( mSettings->mSock );
552 mSettings->mSock = -1;
558 // Create an entry for the connection list
559 listtemp = new Iperf_ListEntry;
560 memcpy(listtemp, &server->peer, sizeof(iperf_sockaddr));
561 listtemp->server = server;
562 listtemp->next = NULL;
564 // See if we need to do summing
565 exist = Iperf_hostpresent( &server->peer, clients);
567 if ( exist != NULL ) {
569 listtemp->holder = exist->holder;
570 server->multihdr = exist->holder;
572 server->mThreads = 0;
573 Mutex_Lock( &groupCond );
575 listtemp->holder = InitMulti( server, groupID );
576 server->multihdr = listtemp->holder;
577 Mutex_Unlock( &groupCond );
580 // Store entry in connection list
581 Iperf_pushback( listtemp, &clients );
584 if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) {
585 client_hdr* hdr = (client_hdr *)(dgram_hdr + 1);
587 Settings_GenerateClientSettings(server, &tempSettings, hdr);
591 if ( tempSettings != NULL ) {
592 client_init( tempSettings );
593 if ( tempSettings->mMode == kTest_DualTest ) {
595 thread_start( tempSettings );
597 server->runNext = tempSettings;
600 server->runNext = tempSettings;
603 server->reporthdr = InitReport( server );
605 // Prep for next connection
606 if ( !isSingleClient( mSettings ) ) {
609 Settings_Copy( mSettings, &server );
610 server->mThreadMode = kMode_Server;
611 } while ( !sInterupted && (!mCount || ( mCount && mClients > 0 )) );
612 Mutex_Unlock( &clients_mutex );
614 Settings_Destroy( server );
617 /* --------------------------------------------------------------------
618 * Run the server as a daemon
619 * --------------------------------------------------------------------*/
621 void Listener::runAsDaemon(const char *pname, int facility) {
624 /* Create a child process & if successful, exit from the parent process */
625 if ( (pid = fork()) == -1 ) {
626 fprintf( stderr, "error in first child create\n");
628 } else if ( pid != 0 ) {
632 /* Try becoming the session leader, once the parent exits */
633 if ( setsid() == -1 ) { /* Become the session leader */
634 fprintf( stderr, "Cannot change the session group leader\n");
637 signal(SIGHUP,SIG_IGN);
640 /* Now fork() and get released from the terminal */
641 if ( (pid = fork()) == -1 ) {
642 fprintf( stderr, "error\n");
644 } else if ( pid != 0 ) {
649 fprintf( stderr, "Running Iperf Server as a daemon\n");
650 fprintf( stderr, "The Iperf daemon process ID : %d\n",((int)getpid()));