]> sjero.net Git - iperf/blobdiff - src/Listener.cpp
Native IPv6 support for iperf
[iperf] / src / Listener.cpp
index cf3c1596c790cbd03c659e7ca19ff3c7ca4585ea..8afd6aa12c58707716a53a2e04d2b382e09a7f92 100644 (file)
@@ -114,37 +114,29 @@ Listener::~Listener() {
 /* ------------------------------------------------------------------- 
  * Listens for connections and starts Servers to handle data. 
  * For TCP, each accepted connection spawns a Server thread. 
- * For UDP, handle all data in this thread for Win32 Only, otherwise
- *          spawn a new Server thread. 
+ * For datagram-oriented protocols, spawn a new Server thread.
  * ------------------------------------------------------------------- */ 
-void Listener::Run( void ) {
+void Listener::Run(void)
+{
 #ifdef sun
-    if ( ( isUDP( mSettings ) && 
-           isMulticast( mSettings ) && 
-           !isSingleUDP( mSettings ) ) ||
-         isSingleUDP( mSettings ) ) {
+    if ( ( isPacketOriented( mSettings ) &&
+           isMulticast( mSettings )      &&
+          !isSingleUDP( mSettings )    ) || isSingleUDP( mSettings ) ) {
         UDPSingleServer();
-    } else
 #else
     if ( isSingleUDP( mSettings ) ) {
         UDPSingleServer();
-    } else
 #endif
-    {
-        bool client = false, UDP = isUDP( mSettings ), mCount = (mSettings->mThreads != 0);
+    } else {
+        bool client = (mSettings->mHost != NULL),
+             mCount = (mSettings->mThreads != 0);
         thread_Settings *tempSettings = NULL;
         Iperf_ListEntry *exist, *listtemp;
-        client_hdr* hdr = ( UDP ? (client_hdr*) (((UDP_datagram*)mBuf) + 1) : 
-                                  (client_hdr*) mBuf);
-        
-        if ( mSettings->mHost != NULL ) {
-            client = true;
-            SockAddr_remoteAddr( mSettings );
-        }
+        client_hdr* hdr;
+
         Settings_Copy( mSettings, &server );
         server->mThreadMode = kMode_Server;
-    
-    
+
         // Accept each packet, 
         // If there is no existing client, then start  
         // a new thread to service the new client 
@@ -160,9 +152,9 @@ void Listener::Run( void ) {
                 close( server->mSock );
                 break;
             }
+
             // Reset Single Client Stuff
             if ( isSingleClient( mSettings ) && clients == NULL ) {
-                mSettings->peer = server->peer;
                 mClients--;
                 client = true;
                 // Once all the server threads exit then quit
@@ -173,23 +165,16 @@ void Listener::Run( void ) {
                     mClients = 1;
                 }
             }
-            // Verify that it is allowed
-            if ( client ) {
-                if ( !SockAddr_Hostare_Equal( (sockaddr*) &mSettings->peer, 
-                                              (sockaddr*) &server->peer ) ) {
-                    // Not allowed try again
-                    close( server->mSock );
-                    if ( isUDP( mSettings ) ) {
-                        mSettings->mSock = -1;
-                        Listen();
-                    }
-                    continue;
-                }
-            }
+           // Copy peer address for individual/bidirectional dual-test
+           // NOTE: There is no longer a reverse-lookup for the peer to
+           //       check if it is allowed. The reason is that it simply
+           //       gets too complex: compare v4/v6, v4/v4, v6/v4, v6/v6
+            if (client)
+                mSettings->peer = server->peer;
     
             // Create an entry for the connection list
             listtemp = new Iperf_ListEntry;
-            memcpy(listtemp, &server->peer, sizeof(iperf_sockaddr));
+            memcpy(listtemp, &server->peer, sizeof(server->peer));
             listtemp->next = NULL;
     
             // See if we need to do summing
@@ -215,19 +200,19 @@ void Listener::Run( void ) {
     
             tempSettings = NULL;
             if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) {
-                if ( !UDP ) {
-                    // TCP does not have the info yet
+                if ( !isConnectionLess(mSettings)) {
+                    hdr = (client_hdr*) mBuf;
+
+                    // TCP/DCCP does not have the info yet
                     if ( recv( server->mSock, (char*)hdr, sizeof(client_hdr), 0) > 0 ) {
-                        Settings_GenerateClientSettings( server, &tempSettings, 
-                                                          hdr );
+                        Settings_GenerateClientSettings( server, &tempSettings, hdr );
                     }
                 } else {
-                    Settings_GenerateClientSettings( server, &tempSettings, 
-                                                      hdr );
+                    hdr = (client_hdr *) ( ((dgram_record*)mBuf) + 1 );
+                    Settings_GenerateClientSettings( server, &tempSettings, hdr );
                 }
             }
     
-    
             if ( tempSettings != NULL ) {
                 client_init( tempSettings );
                 if ( tempSettings->mMode == kTest_DualTest ) {
@@ -245,7 +230,7 @@ void Listener::Run( void ) {
             thread_start( server );
     
             // create a new socket
-            if ( UDP ) {
+            if ( isConnectionLess( mSettings ) ) {
                 mSettings->mSock = -1; 
                 Listen( );
             }
@@ -264,89 +249,58 @@ void Listener::Run( void ) {
 
 /* -------------------------------------------------------------------
  * Setup a socket listening on a port.
- * For TCP, this calls bind() and listen().
- * For UDP, this just calls bind().
+ * For connection-oriented protocols, this calls bind() and listen().
+ * For connection-less protocols, this just calls bind().
  * If inLocalhost is not null, bind to that address rather than the
  * wildcard server address, specifying what incoming interface to
  * accept connections on.
  * ------------------------------------------------------------------- */
-void Listener::Listen( ) {
-    int rc;
-
-    SockAddr_localAddr( mSettings );
-
-    // create an internet TCP socket
-    int type = (isUDP( mSettings )  ?  SOCK_DGRAM  :  SOCK_STREAM);
-    int domain = (SockAddr_isIPv6( &mSettings->local ) ? 
-#ifdef HAVE_IPV6
-                  AF_INET6
-#else
-                  AF_INET
-#endif
-                  : AF_INET);
-
-    mSettings->mSock = socket( domain, type, 0 );
-    WARN_errno( mSettings->mSock == INVALID_SOCKET, "socket" );
-
-    SetSocketOptions( mSettings );
-
-    // reuse the address, so we can run if a former server was killed off
-    int boolean = 1;
-    Socklen_t len = sizeof(boolean);
-    setsockopt( mSettings->mSock, SOL_SOCKET, SO_REUSEADDR, (char*) &boolean, len );
+void Listener::Listen()
+{
+    MakeSocket(mSettings);
 
-    // bind socket to server address
-    rc = bind( mSettings->mSock, (sockaddr*) &mSettings->local, mSettings->size_local );
-    WARN_errno( rc == SOCKET_ERROR, "bind" );
-
-    // listen for connections (TCP only).
     // default backlog traditionally 5
-    if ( !isUDP( mSettings ) ) {
-        rc = listen( mSettings->mSock, 5 );
-        WARN_errno( rc == SOCKET_ERROR, "listen" );
-    }
+    if (!isConnectionLess(mSettings) && listen(mSettings->mSock, 5) < 0)
+        WARN_errno( 1, "listen" );
 
-    // if multicast, join the group
-    if ( SockAddr_isMulticast( &mSettings->local ) ) {
-        McastJoin( );
-    }
-} // end Listen
+    // if multicast, join the group specified earlier via -B
+    if (isMulticast(mSettings))
+        McastJoin();
+}
 
 /* -------------------------------------------------------------------
  * Joins the multicast group, with the default interface.
  * ------------------------------------------------------------------- */
 
-void Listener::McastJoin( ) {
-#ifdef HAVE_MULTICAST
-    if ( !SockAddr_isIPv6( &mSettings->local ) ) {
-        struct ip_mreq mreq;
+void Listener::McastJoin()
+{
+    if (SockAddr_isIPv6(&mSettings->local)) {
+        struct ipv6_mreq mreq;
 
-        memcpy( &mreq.imr_multiaddr, SockAddr_get_in_addr( &mSettings->local ), 
-                sizeof(mreq.imr_multiaddr));
+       memset(&mreq, 0, sizeof(mreq));
+        memcpy(&mreq.ipv6mr_multiaddr, &((struct sockaddr_in6*)&mSettings->local)->sin6_addr,
+               sizeof(mreq.ipv6mr_multiaddr));
 
-        mreq.imr_interface.s_addr = htonl( INADDR_ANY );
+        mreq.ipv6mr_interface = mSettings->mMcastIface;
 
-        int rc = setsockopt( mSettings->mSock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
-                             (char*) &mreq, sizeof(mreq));
-        WARN_errno( rc == SOCKET_ERROR, "multicast join" );
-    }
-#ifdef HAVE_IPV6_MULTICAST
-      else {
-        struct ipv6_mreq mreq;
+        if (setsockopt(mSettings->mSock, IPPROTO_IPV6,
+                                         IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
+               die("IPv6 multicast join failed: did you use -j/-J?");
 
-        memcpy( &mreq.ipv6mr_multiaddr, SockAddr_get_in6_addr( &mSettings->local ), 
-                sizeof(mreq.ipv6mr_multiaddr));
+    } else {
+        struct ip_mreqn mreq;
+
+       memset(&mreq, 0, sizeof(mreq));
+        memcpy(&mreq.imr_multiaddr, &((struct sockaddr_in*)&mSettings->local)->sin_addr,
+                sizeof(mreq.imr_multiaddr));
 
-        mreq.ipv6mr_interface = 0;
+        mreq.imr_ifindex = mSettings->mMcastIface;
 
-        int rc = setsockopt( mSettings->mSock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
-                             (char*) &mreq, sizeof(mreq));
-        WARN_errno( rc == SOCKET_ERROR, "multicast join" );
+        if (setsockopt(mSettings->mSock, IPPROTO_IP,
+                                        IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
+               die("IPv4 multicast join failed: did you use -j/-J?");
     }
-#endif
-#endif
 }
-// end McastJoin
 
 /* -------------------------------------------------------------------
  * Sets the Multicast TTL for outgoing packets.
@@ -375,33 +329,34 @@ void Listener::McastSetTTL( int val ) {
  * until a new connection arrives.
  * ------------------------------------------------------------------- */
 
-void Listener::Accept( thread_Settings *server ) {
+void Listener::Accept( thread_Settings *server )
+{
+    socklen_t socklen = sizeof(server->peer);
 
-    server->size_peer = sizeof(iperf_sockaddr); 
-    if ( isUDP( server ) ) {
+    if ( isConnectionLess( server ) ) {
         /* ------------------------------------------------------------------- 
-         * Do the equivalent of an accept() call for UDP sockets. This waits 
-         * on a listening UDP socket until we get a datagram. 
+         * Do the equivalent of an accept() call for connection-less sockets.
+         * This waits on a listening datagram socket until we get a datagram.
          * ------------------------------------------------------------------- */
         int rc;
         Iperf_ListEntry *exist;
         int32_t datagramID;
+
         server->mSock = INVALID_SOCKET;
         while ( server->mSock == INVALID_SOCKET ) {
             rc = recvfrom( mSettings->mSock, mBuf, mSettings->mBufLen, 0, 
-                           (struct sockaddr*) &server->peer, &server->size_peer );
+                           (struct sockaddr *)&server->peer, &socklen );
             FAIL_errno( rc == SOCKET_ERROR, "recvfrom", mSettings );
 
             Mutex_Lock( &clients_mutex );
     
-            // Handle connection for UDP sockets.
+            // Handle connection for datagram-based sockets.
             exist = Iperf_present( &server->peer, clients);
-            datagramID = ntohl( ((UDP_datagram*) mBuf)->id ); 
+            datagramID = ntohl( ((dgram_record*) mBuf)->id );
             if ( exist == NULL && datagramID >= 0 ) {
                 server->mSock = mSettings->mSock;
-                int rc = connect( server->mSock, (struct sockaddr*) &server->peer,
-                                  server->size_peer );
-                FAIL_errno( rc == SOCKET_ERROR, "connect UDP", mSettings );
+                int rc = connect(server->mSock, (struct sockaddr *)&server->peer, socklen);
+                FAIL_errno( rc == SOCKET_ERROR, "datagram-based connect", mSettings );
             } else {
                 server->mSock = INVALID_SOCKET;
             }
@@ -409,41 +364,32 @@ void Listener::Accept( thread_Settings *server ) {
         }
     } else {
         // Handles interupted accepts. Returns the newly connected socket.
-        server->mSock = INVALID_SOCKET;
-    
-        while ( server->mSock == INVALID_SOCKET ) {
-            // accept a connection
-            server->mSock = accept( mSettings->mSock, 
-                                    (sockaddr*) &server->peer, &server->size_peer );
-            if ( server->mSock == INVALID_SOCKET &&  errno == EINTR ) {
-                continue;
-            }
-        }
+       do {
+            server->mSock = accept(mSettings->mSock, (struct sockaddr *)&server->peer, &socklen);
+       } while (server->mSock == INVALID_SOCKET);
     }
-    server->size_local = sizeof(iperf_sockaddr); 
-    getsockname( server->mSock, (sockaddr*) &server->local, 
-                 &server->size_local );
-} // end Accept
+    // find out which local interface the connection uses
+    socklen = sizeof(server->local);
+    getsockname(server->mSock, (struct sockaddr *)&server->local, &socklen);
+}
 
 void Listener::UDPSingleServer( ) {
     
-    bool client = false, UDP = isUDP( mSettings ), mCount = (mSettings->mThreads != 0);
+    bool client = (mSettings->mHost != NULL),
+         mCount = (mSettings->mThreads != 0);
     thread_Settings *tempSettings = NULL;
     Iperf_ListEntry *exist, *listtemp;
     int rc;
     int32_t datagramID;
-    client_hdr* hdr = ( UDP ? (client_hdr*) (((UDP_datagram*)mBuf) + 1) : 
-                              (client_hdr*) mBuf);
+    socklen_t socklen = sizeof(server->peer);
     ReportStruct *reportstruct = new ReportStruct;
+    dgram_record *dgram_hdr = (dgram_record *)mBuf;
     
-    if ( mSettings->mHost != NULL ) {
-        client = true;
-        SockAddr_remoteAddr( mSettings );
-    }
+    assert( isPacketOriented( mSettings ) );
+
     Settings_Copy( mSettings, &server );
     server->mThreadMode = kMode_Server;
 
-
     // Accept each packet, 
     // If there is no existing client, then start  
     // a new report to service the new client 
@@ -452,24 +398,22 @@ void Listener::UDPSingleServer( ) {
     do {
         // Get next packet
         while ( sInterupted == 0) {
-            server->size_peer = sizeof( iperf_sockaddr );
-            rc = recvfrom( mSettings->mSock, mBuf, mSettings->mBufLen, 0, 
-                           (struct sockaddr*) &server->peer, &server->size_peer );
-            WARN_errno( rc == SOCKET_ERROR, "recvfrom" );
-            if ( rc == SOCKET_ERROR ) {
+            rc = recvfrom(mSettings->mSock, mBuf, mSettings->mBufLen, 0,
+                          (struct sockaddr *)&server->peer, &socklen);
+            if (rc == SOCKET_ERROR) {
+                WARN_errno( 1, "recvfrom" );
                 return;
             }
         
-        
-            // Handle connection for UDP sockets.
+            // Handle connection for datagram-based sockets.
             exist = Iperf_present( &server->peer, clients);
-            datagramID = ntohl( ((UDP_datagram*) mBuf)->id ); 
+            datagramID = ntohl( dgram_hdr->id );
             if ( datagramID >= 0 ) {
                 if ( exist != NULL ) {
                     // read the datagram ID and sentTime out of the buffer 
                     reportstruct->packetID = datagramID; 
-                    reportstruct->sentTime.tv_sec = ntohl( ((UDP_datagram*) mBuf)->tv_sec  );
-                    reportstruct->sentTime.tv_usec = ntohl( ((UDP_datagram*) mBuf)->tv_usec ); 
+                    reportstruct->sentTime.tv_sec = ntohl( dgram_hdr->tv_sec  );
+                    reportstruct->sentTime.tv_usec = ntohl( dgram_hdr->tv_usec );
         
                     reportstruct->packetLen = rc;
                     gettimeofday( &(reportstruct->packetTime), NULL );
@@ -480,17 +424,18 @@ void Listener::UDPSingleServer( ) {
                     groupID--;
                     server->mSock = -groupID;
                     Mutex_Unlock( &groupCond );
-                    server->size_local = sizeof(iperf_sockaddr); 
-                    getsockname( mSettings->mSock, (sockaddr*) &server->local, 
-                                 &server->size_local );
+                    // make this a connected socket and determine local interface
+                    connect(mSettings->mSock, (sockaddr *)&server->peer, socklen);
+                    socklen = sizeof(server->local);
+                    getsockname(mSettings->mSock, (sockaddr *)&server->local, &socklen);
                     break;
                 }
             } else {
                 if ( exist != NULL ) {
                     // read the datagram ID and sentTime out of the buffer 
                     reportstruct->packetID = -datagramID; 
-                    reportstruct->sentTime.tv_sec = ntohl( ((UDP_datagram*) mBuf)->tv_sec  );
-                    reportstruct->sentTime.tv_usec = ntohl( ((UDP_datagram*) mBuf)->tv_usec ); 
+                    reportstruct->sentTime.tv_sec = ntohl( dgram_hdr->tv_sec  );
+                    reportstruct->sentTime.tv_usec = ntohl( dgram_hdr->tv_usec );
         
                     reportstruct->packetLen = rc;
                     gettimeofday( &(reportstruct->packetTime), NULL );
@@ -500,14 +445,9 @@ void Listener::UDPSingleServer( ) {
                     gettimeofday( &(reportstruct->packetTime), NULL );
                     CloseReport( exist->server->reporthdr, reportstruct );
         
-                    if ( rc > (int) ( sizeof( UDP_datagram )
-                                                      + sizeof( server_hdr ) ) ) {
-                        UDP_datagram *UDP_Hdr;
-                        server_hdr *hdr;
-        
-                        UDP_Hdr = (UDP_datagram*) mBuf;
+                    if ( rc > (int)(sizeof(dgram_record) + sizeof(server_hdr)) ) {
+                        server_hdr *hdr = (server_hdr *)(dgram_hdr + 1);
                         Transfer_Info *stats = GetReport( exist->server->reporthdr );
-                        hdr = (server_hdr*) (UDP_Hdr+1);
         
                         hdr->flags        = htonl( HEADER_VERSION1 );
                         hdr->total_len1   = htonl( (long) (stats->TotalLen >> 32) );
@@ -526,17 +466,13 @@ void Listener::UDPSingleServer( ) {
                     EndReport( exist->server->reporthdr );
                     exist->server->reporthdr = NULL;
                     Iperf_delete( &(exist->server->peer), &clients );
-                } else if ( rc > (int) ( sizeof( UDP_datagram )
-                                                  + sizeof( server_hdr ) ) ) {
-                    UDP_datagram *UDP_Hdr;
-                    server_hdr *hdr;
-        
-                    UDP_Hdr = (UDP_datagram*) mBuf;
-                    hdr = (server_hdr*) (UDP_Hdr+1);
+
+                } else if ( rc > (int)(sizeof(dgram_record) + sizeof(server_hdr)) ) {
+                    server_hdr *hdr = (server_hdr *) (dgram_hdr + 1);
                     hdr->flags = htonl( 0 );
                 }
-                sendto( mSettings->mSock, mBuf, mSettings->mBufLen, 0,
-                        (struct sockaddr*) &server->peer, server->size_peer);
+                if (write(mSettings->mSock, mBuf, mSettings->mBufLen) < 0)
+                       WARN_errno(1, "UDPSingleServer write");
             }
         }
         if ( server->mSock == INVALID_SOCKET ) {
@@ -548,7 +484,6 @@ void Listener::UDPSingleServer( ) {
         }
         // Reset Single Client Stuff
         if ( isSingleClient( mSettings ) && clients == NULL ) {
-            mSettings->peer = server->peer;
             mClients--;
             client = true;
             // Once all the server threads exit then quit
@@ -559,24 +494,13 @@ void Listener::UDPSingleServer( ) {
                 mClients = 1;
             }
         }
-        // Verify that it is allowed
-        if ( client ) {
-            if ( !SockAddr_Hostare_Equal( (sockaddr*) &mSettings->peer, 
-                                          (sockaddr*) &server->peer ) ) {
-                // Not allowed try again
-                connect( mSettings->mSock, 
-                         (sockaddr*) &server->peer, 
-                         server->size_peer );
-                close( mSettings->mSock );
-                mSettings->mSock = -1; 
-                Listen( );
-                continue;
-            }
-        }
+       // Copy peer address (see note in Listener::Run())
+       if (client)
+            mSettings->peer = server->peer;
 
         // Create an entry for the connection list
         listtemp = new Iperf_ListEntry;
-        memcpy(listtemp, &server->peer, sizeof(iperf_sockaddr));
+        memcpy(listtemp, &server->peer, sizeof(server->peer));
         listtemp->server = server;
         listtemp->next = NULL;
 
@@ -601,8 +525,9 @@ void Listener::UDPSingleServer( ) {
 
         tempSettings = NULL;
         if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) {
-            Settings_GenerateClientSettings( server, &tempSettings, 
-                                              hdr );
+            client_hdr* hdr = (client_hdr *)(dgram_hdr + 1);
+
+            Settings_GenerateClientSettings(server, &tempSettings, hdr);
         }