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 ) ) {
131 bool client = (mSettings->mHost != NULL),
132 mCount = (mSettings->mThreads != 0);
133 thread_Settings *tempSettings = NULL;
134 Iperf_ListEntry *exist, *listtemp;
137 Settings_Copy( mSettings, &server );
138 server->mThreadMode = kMode_Server;
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
148 if ( server->mSock == INVALID_SOCKET ) {
151 if ( sInterupted != 0 ) {
152 close( server->mSock );
156 // Reset Single Client Stuff
157 if ( isSingleClient( mSettings ) && clients == NULL ) {
160 // Once all the server threads exit then quit
161 // Must keep going in case this client sends
163 if ( mClients == 0 ) {
164 thread_release_nonterm( 0 );
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
173 mSettings->peer = server->peer;
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;
180 // See if we need to do summing
181 Mutex_Lock( &clients_mutex );
182 exist = Iperf_hostpresent( &server->peer, clients);
184 if ( exist != NULL ) {
186 listtemp->holder = exist->holder;
187 server->multihdr = exist->holder;
189 server->mThreads = 0;
190 Mutex_Lock( &groupCond );
192 listtemp->holder = InitMulti( server, groupID );
193 server->multihdr = listtemp->holder;
194 Mutex_Unlock( &groupCond );
197 // Store entry in connection list
198 Iperf_pushback( listtemp, &clients );
199 Mutex_Unlock( &clients_mutex );
202 if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) {
203 if ( !isConnectionLess(mSettings)) {
204 hdr = (client_hdr*) mBuf;
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 );
211 hdr = (client_hdr *) ( ((dgram_record*)mBuf) + 1 );
212 Settings_GenerateClientSettings( server, &tempSettings, hdr );
216 if ( tempSettings != NULL ) {
217 client_init( tempSettings );
218 if ( tempSettings->mMode == kTest_DualTest ) {
220 server->runNow = tempSettings;
222 server->runNext = tempSettings;
225 server->runNext = tempSettings;
230 thread_start( server );
232 // create a new socket
233 if ( isConnectionLess( mSettings ) ) {
234 mSettings->mSock = -1;
238 // Prep for next connection
239 if ( !isSingleClient( mSettings ) ) {
242 Settings_Copy( mSettings, &server );
243 server->mThreadMode = kMode_Server;
244 } while ( !sInterupted && (!mCount || ( mCount && mClients > 0 )) );
246 Settings_Destroy( server );
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()
260 MakeSocket(mSettings);
262 // default backlog traditionally 5
263 if (!isConnectionLess(mSettings) && listen(mSettings->mSock, 5) < 0)
264 WARN_errno( 1, "listen" );
266 // if multicast, join the group specified earlier via -B
267 if (isMulticast(mSettings))
271 /* -------------------------------------------------------------------
272 * Joins the multicast group, with the default interface.
273 * ------------------------------------------------------------------- */
275 void Listener::McastJoin()
277 if (SockAddr_isIPv6(&mSettings->local)) {
278 struct ipv6_mreq mreq;
280 memset(&mreq, 0, sizeof(mreq));
281 memcpy(&mreq.ipv6mr_multiaddr, &((struct sockaddr_in6*)&mSettings->local)->sin6_addr,
282 sizeof(mreq.ipv6mr_multiaddr));
284 mreq.ipv6mr_interface = mSettings->mMcastIface;
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?");
291 struct ip_mreqn mreq;
293 memset(&mreq, 0, sizeof(mreq));
294 memcpy(&mreq.imr_multiaddr, &((struct sockaddr_in*)&mSettings->local)->sin_addr,
295 sizeof(mreq.imr_multiaddr));
297 mreq.imr_ifindex = mSettings->mMcastIface;
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?");
305 /* -------------------------------------------------------------------
306 * Sets the Multicast TTL for outgoing packets.
307 * ------------------------------------------------------------------- */
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" );
316 #ifdef HAVE_IPV6_MULTICAST
318 int rc = setsockopt( mSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
319 (char*) &val, sizeof(val));
320 WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
327 /* -------------------------------------------------------------------
328 * After Listen() has setup mSock, this will block
329 * until a new connection arrives.
330 * ------------------------------------------------------------------- */
332 void Listener::Accept( thread_Settings *server )
334 socklen_t socklen = sizeof(server->peer);
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 * ------------------------------------------------------------------- */
342 Iperf_ListEntry *exist;
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 );
351 Mutex_Lock( &clients_mutex );
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 );
361 server->mSock = INVALID_SOCKET;
363 Mutex_Unlock( &clients_mutex );
366 // Handles interupted accepts. Returns the newly connected socket.
368 server->mSock = accept(mSettings->mSock, (struct sockaddr *)&server->peer, &socklen);
369 } while (server->mSock == INVALID_SOCKET);
371 // find out which local interface the connection uses
372 socklen = sizeof(server->local);
373 getsockname(server->mSock, (struct sockaddr *)&server->local, &socklen);
376 void Listener::UDPSingleServer( ) {
378 bool client = (mSettings->mHost != NULL),
379 mCount = (mSettings->mThreads != 0);
380 thread_Settings *tempSettings = NULL;
381 Iperf_ListEntry *exist, *listtemp;
384 socklen_t socklen = sizeof(server->peer);
385 ReportStruct *reportstruct = new ReportStruct;
386 dgram_record *dgram_hdr = (dgram_record *)mBuf;
388 assert( isPacketOriented( mSettings ) );
390 Settings_Copy( mSettings, &server );
391 server->mThreadMode = kMode_Server;
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 );
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" );
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 );
418 reportstruct->packetLen = rc;
419 gettimeofday( &(reportstruct->packetTime), NULL );
421 ReportPacket( exist->server->reporthdr, reportstruct );
423 Mutex_Lock( &groupCond );
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);
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 );
440 reportstruct->packetLen = rc;
441 gettimeofday( &(reportstruct->packetTime), NULL );
443 ReportPacket( exist->server->reporthdr, reportstruct );
445 gettimeofday( &(reportstruct->packetTime), NULL );
446 CloseReport( exist->server->reporthdr, reportstruct );
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 );
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)
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)
466 EndReport( exist->server->reporthdr );
467 exist->server->reporthdr = NULL;
468 Iperf_delete( &(exist->server->peer), &clients );
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 );
474 if (write(mSettings->mSock, mBuf, mSettings->mBufLen) < 0)
475 WARN_errno(1, "UDPSingleServer write");
478 if ( server->mSock == INVALID_SOCKET ) {
481 if ( sInterupted != 0 ) {
482 close( server->mSock );
485 // Reset Single Client Stuff
486 if ( isSingleClient( mSettings ) && clients == NULL ) {
489 // Once all the server threads exit then quit
490 // Must keep going in case this client sends
492 if ( mClients == 0 ) {
493 thread_release_nonterm( 0 );
497 // Copy peer address (see note in Listener::Run())
499 mSettings->peer = server->peer;
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;
507 // See if we need to do summing
508 exist = Iperf_hostpresent( &server->peer, clients);
510 if ( exist != NULL ) {
512 listtemp->holder = exist->holder;
513 server->multihdr = exist->holder;
515 server->mThreads = 0;
516 Mutex_Lock( &groupCond );
518 listtemp->holder = InitMulti( server, groupID );
519 server->multihdr = listtemp->holder;
520 Mutex_Unlock( &groupCond );
523 // Store entry in connection list
524 Iperf_pushback( listtemp, &clients );
527 if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) {
528 client_hdr* hdr = (client_hdr *)(dgram_hdr + 1);
530 Settings_GenerateClientSettings(server, &tempSettings, hdr);
534 if ( tempSettings != NULL ) {
535 client_init( tempSettings );
536 if ( tempSettings->mMode == kTest_DualTest ) {
538 thread_start( tempSettings );
540 server->runNext = tempSettings;
543 server->runNext = tempSettings;
546 server->reporthdr = InitReport( server );
548 // Prep for next connection
549 if ( !isSingleClient( mSettings ) ) {
552 Settings_Copy( mSettings, &server );
553 server->mThreadMode = kMode_Server;
554 } while ( !sInterupted && (!mCount || ( mCount && mClients > 0 )) );
555 Mutex_Unlock( &clients_mutex );
557 Settings_Destroy( server );
560 /* --------------------------------------------------------------------
561 * Run the server as a daemon
562 * --------------------------------------------------------------------*/
564 void Listener::runAsDaemon(const char *pname, int facility) {
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");
571 } else if ( pid != 0 ) {
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");
580 signal(SIGHUP,SIG_IGN);
583 /* Now fork() and get released from the terminal */
584 if ( (pid = fork()) == -1 ) {
585 fprintf( stderr, "error\n");
587 } else if ( pid != 0 ) {
592 fprintf( stderr, "Running Iperf Server as a daemon\n");
593 fprintf( stderr, "The Iperf daemon process ID : %d\n",((int)getpid()));