]> sjero.net Git - iperf/commitdiff
Native IPv6 support for iperf
authorGerrit Renker <gerrit@erg.abdn.ac.uk>
Tue, 24 Feb 2009 19:31:40 +0000 (20:31 +0100)
committerGerrit Renker <gerrit@erg.abdn.ac.uk>
Tue, 24 Feb 2009 19:31:40 +0000 (20:31 +0100)
This simplifies the internal IPv6 concepts so that IPv4/IPv6 and IPv4/IPv6
hostnames can be used interchangeably, without having to use the -V flag.

That is, the type of the address (v4/v6) is automatically detected from the
input hostname given, as one would expect.

Most of the code is now Linux-specific, which is why #ifdefs have been removed.

Note on multicast-join operation for v4/v6:
-------------------------------------------
The multicast-join code has been extended to allow specifying the multicast
interface. This is clear when a host has only one interface: but on which
interface to join if the host has more than one interface?
This is now done via the
-J <interface-name>  or
-j <interface-name> flags.

Note on bidirectional/tradeoff tests (options -2 / -r):
-------------------------------------------------------
The connecting client does not longer validate the peer address when switching
from a connecting client to a listening server for the reverse connection. The
reason is that previously mSettings->peer was looked up and then tested against
the server->peer address. When using both v4/v6 addresses, this is no longer
trivial (v4/v6, v4/v4, v6/v4, v6/v6), hence I have disabled it.

15 files changed:
include/List.h
include/Reporter.h
include/Settings.hpp
include/SocketAddr.h
include/headers.h
src/Client.cpp
src/List.cpp
src/Listener.cpp
src/Locale.c
src/PerfSocket.cpp
src/ReportCSV.c
src/ReportDefault.c
src/Reporter.c
src/Settings.cpp
src/SocketAddr.c

index 13ec44cc22c18dbc63a0986563b6c1102a8572d8..326be4b5fced9bf9d3347d8599d64d233c35e425 100644 (file)
@@ -62,8 +62,6 @@
  * List handling utilities to replace STD vector
  */
 
-struct Iperf_ListEntry;
-
 /*
  * A List entry that consists of a sockaddr
  * a pointer to the Audience that sockaddr is
@@ -71,7 +69,7 @@ struct Iperf_ListEntry;
  * entry
  */
 struct Iperf_ListEntry {
-    iperf_sockaddr data;
+    struct sockaddr_storage data;
     MultiHeader *holder;
     thread_Settings *server;
     Iperf_ListEntry *next;
@@ -85,12 +83,12 @@ extern Iperf_ListEntry *clients;
  */
 void Iperf_pushback ( Iperf_ListEntry *add, Iperf_ListEntry **root );
 
-void Iperf_delete ( iperf_sockaddr *del, Iperf_ListEntry **root );
+void Iperf_delete ( struct sockaddr_storage *del, Iperf_ListEntry **root );
 
 void Iperf_destroy ( Iperf_ListEntry **root );
 
-Iperf_ListEntry* Iperf_present ( iperf_sockaddr *find, Iperf_ListEntry *root );
+Iperf_ListEntry* Iperf_present ( struct sockaddr_storage *find, Iperf_ListEntry *root );
 
-Iperf_ListEntry* Iperf_hostpresent ( iperf_sockaddr *find, Iperf_ListEntry *root );
+Iperf_ListEntry* Iperf_hostpresent ( struct sockaddr_storage *find, Iperf_ListEntry *root );
 
 #endif
index 174055ff893b0f1a1e5c41d24da4f3f248409599..db3defe5ca605ffc8e996d5469b8dedfb6013ff3 100644 (file)
@@ -109,10 +109,8 @@ typedef struct Transfer_Info {
 } Transfer_Info;
 
 typedef struct Connection_Info {
-    iperf_sockaddr peer;
-    Socklen_t size_peer;
-    iperf_sockaddr local;
-    Socklen_t size_local;
+    struct sockaddr_storage peer;
+    struct sockaddr_storage local;
 } Connection_Info;
 
 typedef struct ReporterData {
@@ -155,6 +153,7 @@ typedef struct ReporterData {
     double lastTransit;
     // shorts
     unsigned short mPort;           // -p
+    unsigned short mMcastIface;     // -j or -J
     // structs or miscellaneous
     Transfer_Info info;
     Connection_Info connection;
index bf125cbeadbc886b829c82a85a59a0584e207f13..3a9fa2a9592302c8eccfd35e412555f73b0c20b3 100644 (file)
@@ -151,7 +151,8 @@ typedef struct thread_Settings {
     // int's
     int mThreads;                   // -P
     int mTOS;                       // -S
-    int mSock;
+    int mSock;                      // socket descriptor
+    int mSockAF;                    // type of @mSock
     int Extractor_size;
     int mBufLen;                    // -l
     int mMSS;                       // -M
@@ -193,15 +194,14 @@ typedef struct thread_Settings {
     // shorts
     unsigned short mListenPort;     // -L
     unsigned short mPort;           // -p
+    unsigned short mMcastIface;     // -j or -J
     // chars
     char   mFormat;                 // -f
-    int mTTL;                    // -T
+    int    mTTL;                    // -T
     char pad1[2];
     // structs or miscellaneous
-    iperf_sockaddr peer;
-    Socklen_t size_peer;
-    iperf_sockaddr local;
-    Socklen_t size_local;
+    struct sockaddr_storage peer;   // remote part of socket
+    struct sockaddr_storage local;  // local part of socket
     nthread_t mTID;
 } thread_Settings;
 
index feffdc780603c359c4a0ecda8569694d9823f0cf..37a735977d6f14aaaa9060810f4d8d6fe8984f0c 100644 (file)
 
 #include "headers.h"
 #include "Settings.hpp"
+#include <net/if.h>
 
 
 #ifdef __cplusplus
 extern "C" {
 #endif
-/* ------------------------------------------------------------------- */
-    void SockAddr_localAddr( thread_Settings *inSettings );
-    void SockAddr_remoteAddr( thread_Settings *inSettings );
 
-    void SockAddr_setHostname( const char* inHostname,
-                               iperf_sockaddr *inSockAddr, 
-                               int isIPv6 );          // DNS lookup
-    void SockAddr_getHostname( iperf_sockaddr *inSockAddr,
-                               char* outHostname, 
-                               size_t len );   // reverse DNS lookup
-    void SockAddr_getHostAddress( iperf_sockaddr *inSockAddr, 
-                                  char* outAddress, 
-                                  size_t len ); // dotted decimal
+static inline bool SockAddr_isIPv6(const struct sockaddr_storage *ss)
+{
+       return ss->ss_family == AF_INET6;
+}
 
-    void SockAddr_setPort( iperf_sockaddr *inSockAddr, unsigned short inPort );
-    void SockAddr_setPortAny( iperf_sockaddr *inSockAddr );
-    unsigned short SockAddr_getPort( iperf_sockaddr *inSockAddr );
+static inline int SockAddr_isMulticast(const struct sockaddr *sa)
+{
+    if (sa->sa_family == AF_INET6)
+       return IN6_IS_ADDR_MULTICAST(&(((struct sockaddr_in6 *)sa)->sin6_addr));
 
-    void SockAddr_setAddressAny( iperf_sockaddr *inSockAddr );
+    // IPv4 multicast: 224.0.0.0 to 239.255.255.255 (e0.00.00.00 to ef.ff.ff.ff)
+    return sa->sa_family == AF_INET &&
+          (ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr) >> 28) == 0xe;
+}
 
-    // return pointer to the struct in_addr
-    struct in_addr* SockAddr_get_in_addr( iperf_sockaddr *inSockAddr );
-#ifdef HAVE_IPV6
-    // return pointer to the struct in_addr
-    struct in6_addr* SockAddr_get_in6_addr( iperf_sockaddr *inSockAddr );
-#endif
-    // return the sizeof the addess structure (struct sockaddr_in)
-    Socklen_t SockAddr_get_sizeof_sockaddr( iperf_sockaddr *inSockAddr );
-
-    int SockAddr_isMulticast( iperf_sockaddr *inSockAddr );
-
-    int SockAddr_isIPv6( iperf_sockaddr *inSockAddr );
+int SockAddr_Hostare_Equal(struct sockaddr_storage *first, struct sockaddr_storage *second);
+int SockAddr_are_Equal(struct sockaddr_storage *first, struct sockaddr_storage *second);
 
-    int SockAddr_are_Equal( struct sockaddr *first, struct sockaddr *second );
-    int SockAddr_Hostare_Equal( struct sockaddr *first, struct sockaddr *second );
+int         SockAddr_port(struct sockaddr_storage *ss);
+const char *SockAddr_name(struct sockaddr_storage *ss, char *hbuf, size_t hbuflen);
 
-    void SockAddr_zeroAddress( iperf_sockaddr *inSockAddr );
 #ifdef __cplusplus
 } /* end extern "C" */
 #endif
index 5b534f742a3ac978bba8a7f6277565ca5f71a729..0bc12dbf745550483f9f1d03cb479c9780efce5e 100644 (file)
@@ -126,15 +126,9 @@ SPECIAL_OSF1_EXTERN_C_STOP
     #define INET_ADDRSTRLEN 15
 #endif
 
-//#ifdef __cplusplus
-    #ifdef HAVE_IPV6
-        #define REPORT_ADDRLEN (INET6_ADDRSTRLEN + 1)
-typedef struct sockaddr_storage iperf_sockaddr;
-    #else
-        #define REPORT_ADDRLEN (INET_ADDRSTRLEN + 1)
-typedef struct sockaddr_in iperf_sockaddr;
-    #endif
-//#endif
+// All addresses use struct sockaddr_storage, for compatibility with both v4/v6
+// The maximum report length is the maximum host name length for getnameinfo(3).
+#define REPORT_ADDRLEN (NI_MAXHOST + 1)
 
 // Rationalize stdint definitions and sizeof, thanks to ac_create_stdint_h.m4
 // from the gnu archive
index 2195c4fd3027c8b34f9da14273af7ffcca75b310..b1dda9c2e99b592cd0bb834a292b1da35e392a21 100644 (file)
@@ -90,10 +90,7 @@ Client::Client( thread_Settings *inSettings ) {
         ReportSettings( inSettings );
         if ( mSettings->multihdr && isMultipleReport( inSettings ) ) {
             mSettings->multihdr->report->connection.peer = mSettings->peer;
-            mSettings->multihdr->report->connection.size_peer = mSettings->size_peer;
             mSettings->multihdr->report->connection.local = mSettings->local;
-            SockAddr_setPortAny( &mSettings->multihdr->report->connection.local );
-            mSettings->multihdr->report->connection.size_local = mSettings->size_local;
         }
     }
 
@@ -308,34 +305,12 @@ void Client::InitiateServer()
  * ------------------------------------------------------------------- */
 void Client::Connect()
 {
-    int rc;
-
-    assert( mSettings->inHostname != NULL );
-
-    // The local socket needs to be filled in first, since the
-    // IPv6 address testing for the peer depends on the type of
-    // the local socket
-    SockAddr_localAddr( mSettings );
-    SockAddr_remoteAddr( mSettings );
-    MakeSocket( mSettings);
-    SetSocketOptions( mSettings );
-
-    if ( mSettings->mLocalhost != NULL ) {
-        // bind socket to local address
-        rc = bind( mSettings->mSock, (sockaddr*) &mSettings->local, 
-                   SockAddr_get_sizeof_sockaddr( &mSettings->local ) );
-        WARN_errno( rc == SOCKET_ERROR, "bind" );
-    }
+    socklen_t size_local = sizeof(mSettings->local);
 
-    // connect socket
-    rc = connect( mSettings->mSock, (sockaddr*) &mSettings->peer, 
-                  SockAddr_get_sizeof_sockaddr( &mSettings->peer ));
-    WARN_errno( rc == SOCKET_ERROR, "connect" );
+    MakeSocket(mSettings);
 
-    getsockname( mSettings->mSock, (sockaddr*) &mSettings->local, 
-                 &mSettings->size_local );
-    getpeername( mSettings->mSock, (sockaddr*) &mSettings->peer,
-                 &mSettings->size_peer );
+    /* determine local interface after establishing the connection */
+    getsockname(mSettings->mSock, (struct sockaddr *)&mSettings->local, &size_local);
 
     /* The DCCP packet size must not exceed the MPS (RFC 4340, 14.) */
     if (mSettings->mProtocol == kProto_DCCP) {
index 8cba006dcd9c0ee7016439d70d17b3d223a0f6ed..4b850a6dcbf38c2befcd5997b44af1af7f3c6999 100644 (file)
@@ -71,7 +71,8 @@ void Iperf_pushback ( Iperf_ListEntry *add, Iperf_ListEntry **root ) {
 /*
  * Delete Entry del from the List
  */
-void Iperf_delete ( iperf_sockaddr *del, Iperf_ListEntry **root ) {
+void Iperf_delete (struct sockaddr_storage *del, Iperf_ListEntry **root)
+{
     Iperf_ListEntry *temp = Iperf_present( del, *root );
     if ( temp != NULL ) {
         if ( temp == *root ) {
@@ -106,14 +107,13 @@ void Iperf_destroy ( Iperf_ListEntry **root ) {
 /*
  * Check if the exact Entry find is present
  */
-Iperf_ListEntry* Iperf_present ( iperf_sockaddr *find, Iperf_ListEntry *root ) {
-    Iperf_ListEntry *itr = root;
-    while ( itr != NULL ) {
-        if ( SockAddr_are_Equal( (sockaddr*)itr, (sockaddr*)find ) ) {
+Iperf_ListEntry* Iperf_present(struct sockaddr_storage *find, Iperf_ListEntry *root)
+{
+    Iperf_ListEntry *itr;
+
+    for (itr = root; itr != NULL; itr = itr->next)
+        if (SockAddr_are_Equal(find, &itr->data))
             return itr;
-        }
-        itr = itr->next;
-    }
     return NULL;
 }
 
@@ -121,14 +121,9 @@ Iperf_ListEntry* Iperf_present ( iperf_sockaddr *find, Iperf_ListEntry *root ) {
  * Check if a Entry find is in the List or if any
  * Entry exists that has the same host as the 
  * Entry find
+ * FIXME: The `or' condition was not implemented
  */
-Iperf_ListEntry* Iperf_hostpresent ( iperf_sockaddr *find, Iperf_ListEntry *root ) {
-    Iperf_ListEntry *itr = root;
-    while ( itr != NULL ) {
-        if ( SockAddr_Hostare_Equal( (sockaddr*)itr, (sockaddr*)find ) ) {
-            return itr;
-        }
-        itr = itr->next;
-    }
-    return NULL;
+Iperf_ListEntry* Iperf_hostpresent(struct sockaddr_storage *find, Iperf_ListEntry *root)
+{
+    return Iperf_present(find, root);
 }
index 1cc0de944c232a316954cf712e78c36e95f2693b..8afd6aa12c58707716a53a2e04d2b382e09a7f92 100644 (file)
@@ -128,21 +128,15 @@ void Listener::Run(void)
         UDPSingleServer();
 #endif
     } else {
-        bool client = false,
+        bool client = (mSettings->mHost != NULL),
              mCount = (mSettings->mThreads != 0);
         thread_Settings *tempSettings = NULL;
         Iperf_ListEntry *exist, *listtemp;
         client_hdr* hdr;
 
-        
-        if ( mSettings->mHost != NULL ) {
-            client = true;
-            SockAddr_remoteAddr( mSettings );
-        }
         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 
@@ -158,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
@@ -171,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 ( isConnectionLess( 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
@@ -270,69 +257,50 @@ void Listener::Run(void)
  * ------------------------------------------------------------------- */
 void Listener::Listen()
 {
-    int rc;
-    int boolean = 1;
-    Socklen_t len = sizeof(boolean);
-
-    SockAddr_localAddr( mSettings );
-    MakeSocket( mSettings );
-
-    SetSocketOptions( mSettings );
-
-    // reuse the address, so we can run if a former server was killed off
-    setsockopt( mSettings->mSock, SOL_SOCKET, SO_REUSEADDR, (char*) &boolean, len );
-
-    // listen for connections (TCP/DCCP only).
-    rc = bind( mSettings->mSock, (sockaddr*) &mSettings->local, mSettings->size_local );
-    WARN_errno( rc == SOCKET_ERROR, "bind" );
+    MakeSocket(mSettings);
 
     // default backlog traditionally 5
-    if ( !isConnectionLess( 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;
 
-        mreq.ipv6mr_interface = 0;
+       memset(&mreq, 0, sizeof(mreq));
+        memcpy(&mreq.imr_multiaddr, &((struct sockaddr_in*)&mSettings->local)->sin_addr,
+                sizeof(mreq.imr_multiaddr));
+
+        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.
@@ -363,7 +331,7 @@ void Listener::McastSetTTL( int val ) {
 
 void Listener::Accept( thread_Settings *server )
 {
-    server->size_peer = sizeof(iperf_sockaddr); 
+    socklen_t socklen = sizeof(server->peer);
 
     if ( isConnectionLess( server ) ) {
         /* ------------------------------------------------------------------- 
@@ -377,7 +345,7 @@ void Listener::Accept( thread_Settings *server )
         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 );
@@ -387,8 +355,7 @@ void Listener::Accept( thread_Settings *server )
             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 );
+                int rc = connect(server->mSock, (struct sockaddr *)&server->peer, socklen);
                 FAIL_errno( rc == SOCKET_ERROR, "datagram-based connect", mSettings );
             } else {
                 server->mSock = INVALID_SOCKET;
@@ -397,43 +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, 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;
+    socklen_t socklen = sizeof(server->peer);
     ReportStruct *reportstruct = new ReportStruct;
     dgram_record *dgram_hdr = (dgram_record *)mBuf;
     
-
     assert( isPacketOriented( mSettings ) );
 
-    if ( mSettings->mHost != NULL ) {
-        client = true;
-        SockAddr_remoteAddr( 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 
@@ -442,15 +398,13 @@ 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 datagram-based sockets.
             exist = Iperf_present( &server->peer, clients);
             datagramID = ntohl( dgram_hdr->id );
@@ -470,9 +424,10 @@ 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 {
@@ -516,8 +471,8 @@ void Listener::UDPSingleServer( ) {
                     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 ) {
@@ -529,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
@@ -540,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;
 
index b2e8d1f8093a1f75366af8587e88c8e84c6a18f4..acad49c7809257dccb2d5f2fb0e22e640783be3e 100644 (file)
@@ -148,7 +148,7 @@ const char multicast_ttl[] =
 "Setting multicast TTL to %d\n";
 
 const char join_multicast[] =
-"Joining multicast group  %s\n";
+"Joining multicast group %s on %s\n";
 
 const char client_datagram_size[] =
 "Sending %d byte datagrams\n";
index d149d596dd6ed2b9fd6d7d94693510c06b1f778d..2c169014b080d8bff8fb0bf9416427a6d2d646c1 100644 (file)
 #include "SocketAddr.h"
 #include "util.h"
 
-// create an internet socket
-void MakeSocket(thread_Settings *inSettings)
-{
-   int type = 0, proto = 0, domain =
-                SockAddr_isIPv6(&inSettings->local) ? AF_INET6 :
-                AF_INET;
-
-    switch (inSettings->mProtocol) {
-        case kProto_TCP:     type  = SOCK_STREAM;     break;
-        case kProto_UDP:     type  = SOCK_DGRAM;      break;
-        case kProto_DCCP:    type  = SOCK_DCCP;       break;
-    }
-    inSettings->mSock = socket( domain, type, proto );
-
-    WARN_errno( inSettings->mSock == INVALID_SOCKET, "socket" );
-}
-
 /* -------------------------------------------------------------------
  * Set socket options before the listen() or connect() calls.
  * These are optional performance tuning factors.
@@ -103,23 +86,16 @@ void SetSocketOptions( thread_Settings *inSettings )
     Socklen_t len = sizeof(int);
 
     // check if we're sending multicast, and set TTL
-    if ( isMulticast( inSettings ) && ( inSettings->mTTL > 0 ) ) {
+    if (isMulticast(inSettings) && inSettings->mTTL > 0) {
         val = inSettings->mTTL;
-#ifdef HAVE_MULTICAST
-       if ( !SockAddr_isIPv6( &inSettings->local ) ) {
-            rc = setsockopt( inSettings->mSock, IPPROTO_IP, IP_MULTICAST_TTL,
-                             &val, len);
+       if (SockAddr_isIPv6(&inSettings->local))
+            rc = setsockopt(inSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+                            &val, len);
+       else
+            rc = setsockopt(inSettings->mSock, IPPROTO_IP, IP_MULTICAST_TTL,
+                            &val, len);
 
-           WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
-       }
-#ifdef HAVE_IPV6_MULTICAST
-       else {
-            rc = setsockopt( inSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
-                             &val, len);
-           WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
-       }
-#endif /* IPV6_MULTICAST */
-#endif /* MULTICAST      */
+       WARN_errno( rc == SOCKET_ERROR, "multicast ttl" );
     }
 
 
@@ -166,4 +142,119 @@ void SetSocketOptions( thread_Settings *inSettings )
                          &val, len );
         WARN_errno( rc == SOCKET_ERROR, "setsockopt DCCP_SOCKOPT_SERVICE" );
     }
+
+    // reuse the address, so we can run if a former server was killed off
+    if (inSettings->mThreadMode == kMode_Listener) {
+       val = 1;
+       rc = setsockopt(inSettings->mSock, SOL_SOCKET, SO_REUSEADDR, &val, len);
+        WARN_errno( rc == SOCKET_ERROR, "setsockopt SO_REUSEADDR" );
+    }
+}
+
+void MakeSocket(thread_Settings *inSettings)
+{
+       struct addrinfo *local = NULL, *src,
+                       *remote = NULL, *dst, hints;
+       char            port[6];
+       int             rc, socktype = sockType(inSettings->mProtocol);
+
+       assert(inSettings->mLocalhost || inSettings->mHost);
+
+        memset(&inSettings->local, 0, sizeof(inSettings->local));
+        memset(&inSettings->peer,  0, sizeof(inSettings->peer));
+       sprintf(port, "%u", inSettings->mPort);
+
+       /*
+        *      Set up address hint structure
+        */
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family   = inSettings->mSockAF;
+       hints.ai_socktype = socktype;
+       /*
+        * CHEAT: getaddrinfo does not support SOCK_DCCP and using a zero
+        * ai_socktype will return IPv4 addresses first (which is bad on
+        * a dual-stack host). Pretend here to be UDP - this usually works.
+        */
+       if (inSettings->mProtocol == IPPROTO_DCCP)
+               hints.ai_socktype = SOCK_DGRAM;
+
+       /* only use addresses available on the host */
+       hints.ai_flags = AI_ADDRCONFIG;
+       if (inSettings->mSockAF == AF_INET6)
+               /* use v4-mapped-v6 if no v6 addresses found */
+               hints.ai_flags |= AI_V4MAPPED | AI_ALL;
+
+       /*
+        *      Obtain local/remote address information
+        */
+       if (inSettings->mLocalhost || inSettings->mThreadMode == kMode_Listener) {
+               if (inSettings->mLocalhost == NULL)
+                       hints.ai_flags |= AI_PASSIVE;
+               if ((rc = getaddrinfo(inSettings->mLocalhost, port, &hints, &local)))
+                       die("Can not resolve local address %s#%s: %s",
+                           inSettings->mLocalhost ? : "(local)", port, gai_strerror(rc));
+       }
+
+       if (inSettings->mHost && inSettings->mThreadMode != kMode_Listener) {
+               if ((rc = getaddrinfo(inSettings->mHost, port, &hints, &remote)))
+                       die("Can not resolve peer address %s#%s: %s",
+                           inSettings->mHost, port, gai_strerror(rc));
+       }
+
+       /*
+        *      Iterate over all src/dst combination, exhausting dst first
+        */
+       for (src = local, dst = remote; src != NULL || dst != NULL; /* no op */ ) {
+               if (src && src->ai_family == AF_INET &&
+                   dst && dst->ai_family == AF_INET6)
+                       goto get_next_dst; /* v4 -> v6 is not possible */
+
+               inSettings->mSockAF = src ? src->ai_family : dst->ai_family;
+               inSettings->mSock   = socket(inSettings->mSockAF, socktype,
+                                            inSettings->mProtocol);
+               if (inSettings->mSock < 0)
+                       goto get_next_dst;
+
+               SetSocketOptions(inSettings);
+
+               if (src) {
+                       if (bind(inSettings->mSock, src->ai_addr, src->ai_addrlen) < 0) {
+                               close(inSettings->mSock);
+                               goto get_next_src;
+                       }
+                       if (!dst)
+                               break;  /* bind-only completed successfully */
+               }
+
+               if (dst && connect(inSettings->mSock, dst->ai_addr, dst->ai_addrlen) == 0)
+                       break;          /* connection completed successfully */
+               close(inSettings->mSock);
+get_next_dst:
+               if (dst && (dst = dst->ai_next))
+                       continue;
+get_next_src:
+               if (src && (src = src->ai_next))
+                       dst = remote;   /* restart inner loop */
+       }
+
+       if (src == NULL && dst == NULL)
+               die("Can not create %s socket", protoName(inSettings->mProtocol));
+       if (src) {
+               if (SockAddr_isMulticast(src->ai_addr))
+                       setMulticast(inSettings);
+               memcpy(&inSettings->local, src->ai_addr, src->ai_addrlen);
+       }
+       if (dst) {
+               if (SockAddr_isMulticast(dst->ai_addr))
+                       setMulticast(inSettings);
+               memcpy(&inSettings->peer, dst->ai_addr, dst->ai_addrlen);
+       }
+
+       if (local)
+               freeaddrinfo(local);
+       if (remote)
+               freeaddrinfo(remote);
+
+       if (isMulticast(inSettings) && !isConnectionLess(inSettings))
+               die("Can not use %s with multicast.", protoName(inSettings->mProtocol));
 }
index b9ea604b1cc33478b0cb1d647c323e8561c5ed83..731925587faa98c2b324f622284d4f46ac60cbee 100644 (file)
@@ -93,52 +93,19 @@ void CSV_stats( Transfer_Info *stats ) {
     }
 }
 
-void *CSV_peer( Connection_Info *stats, int ID ) {
-    
-    // copy the inet_ntop into temp buffers, to avoid overwriting
-    char local_addr[ REPORT_ADDRLEN ];
-    char remote_addr[ REPORT_ADDRLEN ];
-    char *buf = malloc( REPORT_ADDRLEN*2 + 10 );
-    struct sockaddr *local = ((struct sockaddr*)&stats->local);
-    struct sockaddr *peer = ((struct sockaddr*)&stats->peer);
+void *CSV_peer(Connection_Info *stats, int ID)
+{
+    char      laddr[REPORT_ADDRLEN],
+              daddr[REPORT_ADDRLEN],
+             *buf = malloc(2 * REPORT_ADDRLEN + 10);
+    unsigned  lport = SockAddr_port(&stats->local),
+              dport = SockAddr_port(&stats->peer);
 
-    if ( local->sa_family == AF_INET ) {
-        inet_ntop( AF_INET, &((struct sockaddr_in*)local)->sin_addr, 
-                   local_addr, REPORT_ADDRLEN);
-    }
-#ifdef HAVE_IPV6
-      else {
-        inet_ntop( AF_INET6, &((struct sockaddr_in6*)local)->sin6_addr, 
-                   local_addr, REPORT_ADDRLEN);
-    }
-#endif
-
-    if ( peer->sa_family == AF_INET ) {
-        inet_ntop( AF_INET, &((struct sockaddr_in*)peer)->sin_addr, 
-                   remote_addr, REPORT_ADDRLEN);
-    }
-#ifdef HAVE_IPV6
-      else {
-        inet_ntop( AF_INET6, &((struct sockaddr_in6*)peer)->sin6_addr, 
-                   remote_addr, REPORT_ADDRLEN);
-    }
-#endif
+    SockAddr_name(&stats->local, laddr, sizeof(laddr));
+    SockAddr_name(&stats->peer,  daddr, sizeof(daddr));
 
-    snprintf(buf, REPORT_ADDRLEN*2+10, reportCSV_peer, 
-             local_addr, ( local->sa_family == AF_INET ?
-                          ntohs(((struct sockaddr_in*)local)->sin_port) :
-#ifdef HAVE_IPV6
-                          ntohs(((struct sockaddr_in6*)local)->sin6_port)),
-#else
-                          0),
-#endif
-            remote_addr, ( peer->sa_family == AF_INET ?
-                          ntohs(((struct sockaddr_in*)peer)->sin_port) :
-#ifdef HAVE_IPV6
-                          ntohs(((struct sockaddr_in6*)peer)->sin6_port)));
-#else
-                          0));
-#endif
+    snprintf(buf, 2 * REPORT_ADDRLEN, reportCSV_peer,
+                  laddr, lport, daddr, dport);
     return buf;
 }
 
index a3ecc979060091c31dc30242c9cbd946668ce274..dcb38527476d9ed3c71d14580409568ff39cf8eb 100644 (file)
@@ -140,6 +140,17 @@ void reporter_serverstats( Connection_Info *nused, Transfer_Info *stats ) {
     reporter_printstats( stats );
 }
 
+static char *McastIface(const unsigned if_index)
+{
+       static char ifnam[IF_NAMESIZE];
+
+       if (!if_index)
+               return "default interface";
+       if (if_indextoname(if_index, ifnam))
+               return ifnam;
+       return "unknown interface";
+}
+
 /*
  * Report the client or listener Settings in default style
  */
@@ -162,19 +173,18 @@ void reporter_reportsettings( ReporterData *data ) {
                 data->mPort );
     }
     if ( data->mLocalhost != NULL ) {
-        printf( bind_address, data->mLocalhost );
-        if ( SockAddr_isMulticast( &data->connection.local ) ) {
-            printf( join_multicast, data->mLocalhost );
-        }
+        if (SockAddr_isMulticast((struct sockaddr *)&data->connection.local))
+            printf(join_multicast, data->mLocalhost, McastIface(data->mMcastIface));
+       else
+           printf(bind_address, data->mLocalhost);
     }
 
     if ( isPacketOriented( data ) ) {
         printf( (data->mThreadMode == kMode_Listener ? 
                  server_datagram_size : client_datagram_size),
                 data->mBufLen );
-        if ( SockAddr_isMulticast( &data->connection.peer ) ) {
+        if (SockAddr_isMulticast((struct sockaddr *)&data->connection.peer))
             printf( multicast_ttl, data->info.mTTL);
-        }
     } else if (data->mProtocol == kProto_DCCP)
             printf("NOTE: running in bytestream-mode (maximum speed)\n");
 
@@ -200,56 +210,20 @@ void reporter_reportsettings( ReporterData *data ) {
 /*
  * Report a socket's peer IP address in default style
  */
-void *reporter_reportpeer( Connection_Info *stats, int ID ) {
-    if ( ID > 0 ) {
-        // copy the inet_ntop into temp buffers, to avoid overwriting
-        char local_addr[ REPORT_ADDRLEN ];
-        char remote_addr[ REPORT_ADDRLEN ];
-        struct sockaddr *local = ((struct sockaddr*)&stats->local);
-        struct sockaddr *peer = ((struct sockaddr*)&stats->peer);
-    
-        if ( local->sa_family == AF_INET ) {
-            inet_ntop( AF_INET, &((struct sockaddr_in*)local)->sin_addr, 
-                       local_addr, REPORT_ADDRLEN);
-        }
-#ifdef HAVE_IPV6
-          else {
-            inet_ntop( AF_INET6, &((struct sockaddr_in6*)local)->sin6_addr, 
-                       local_addr, REPORT_ADDRLEN);
-        }
-#endif
-    
-        if ( peer->sa_family == AF_INET ) {
-            inet_ntop( AF_INET, &((struct sockaddr_in*)peer)->sin_addr, 
-                       remote_addr, REPORT_ADDRLEN);
-        }
-#ifdef HAVE_IPV6
-          else {
-            inet_ntop( AF_INET6, &((struct sockaddr_in6*)peer)->sin6_addr, 
-                       remote_addr, REPORT_ADDRLEN);
-        }
-#endif
-    
-        printf( report_peer,
-                ID,
-                local_addr,  ( local->sa_family == AF_INET ?
-                              ntohs(((struct sockaddr_in*)local)->sin_port) :
-#ifdef HAVE_IPV6
-                              ntohs(((struct sockaddr_in6*)local)->sin6_port)),
-#else
-                              0),
-#endif
-                remote_addr, ( peer->sa_family == AF_INET ?
-                              ntohs(((struct sockaddr_in*)peer)->sin_port) :
-#ifdef HAVE_IPV6
-                              ntohs(((struct sockaddr_in6*)peer)->sin6_port)));
-#else
-                              0));
-#endif
+void *reporter_reportpeer(Connection_Info *stats, int ID)
+{
+    if (ID > 0) {
+        char    laddr[REPORT_ADDRLEN],
+                daddr[REPORT_ADDRLEN];
+       unsigned lport = SockAddr_port(&stats->local),
+                dport = SockAddr_port(&stats->peer);
+
+       SockAddr_name(&stats->local, laddr, sizeof(laddr));
+       SockAddr_name(&stats->peer,  daddr, sizeof(daddr));
+        printf(report_peer, ID, laddr, lport, daddr, dport);
     }
     return NULL;
 }
-// end ReportPeer
 
 /* -------------------------------------------------------------------
  * Report the MSS and MTU, given the MSS (or a guess thereof)
index db9418e3bb97baecd1754950589a574d9f7b662f..c5eff5c35ba5a48e3610073272ea588db7a9a7dd 100644 (file)
@@ -169,11 +169,7 @@ MultiHeader* InitMulti( thread_Settings *agent, int inID ) {
                 if ( isConnectionReport( agent ) ) {
                     data->type |= CONNECTION_REPORT;
                     data->connection.peer = agent->peer;
-                    data->connection.size_peer = agent->size_peer;
-                    SockAddr_setPortAny( &data->connection.peer );
                     data->connection.local = agent->local;
-                    data->connection.size_local = agent->size_local;
-                    SockAddr_setPortAny( &data->connection.local );
                 }
             }
         } else {
@@ -271,9 +267,7 @@ ReportHeader* InitReport( thread_Settings *agent ) {
         if ( reporthdr != NULL ) {
             data->type |= CONNECTION_REPORT;
             data->connection.peer = agent->peer;
-            data->connection.size_peer = agent->size_peer;
             data->connection.local = agent->local;
-            data->connection.size_local = agent->size_local;
         } else {
             FAIL(1, "Out of Memory!!\n", agent);
         }
@@ -448,12 +442,11 @@ void ReportSettings( thread_Settings *agent ) {
             data->mProtocol = agent->mProtocol;
             data->mThreadMode = agent->mThreadMode;
             data->mPort = agent->mPort;
+            data->mMcastIface = agent->mMcastIface;
             data->info.mFormat = agent->mFormat;
             data->info.mTTL = agent->mTTL;
             data->connection.peer = agent->peer;
-            data->connection.size_peer = agent->size_peer;
             data->connection.local = agent->local;
-            data->connection.size_local = agent->size_local;
     
     #ifdef HAVE_THREAD
             /*
@@ -513,9 +506,7 @@ void ReportServerUDP( thread_Settings *agent, server_hdr *server ) {
             stats->cntDatagrams = ntohl( server->datagrams );
             stats->mUDP = (char)kMode_Server;
             reporthdr->report.connection.peer = agent->local;
-            reporthdr->report.connection.size_peer = agent->size_local;
             reporthdr->report.connection.local = agent->peer;
-            reporthdr->report.connection.size_local = agent->size_peer;
             
 #ifdef HAVE_THREAD
             /*
index 4cde826405af41db1b7fb6ef7021d1f85adc7044..549a9cca9d7523afb8c88e31e72901e3b16f8124 100644 (file)
@@ -91,6 +91,7 @@ const struct option long_options[] =
 {"format",     required_argument, NULL, 'f'},
 {"help",             no_argument, NULL, 'h'},
 {"interval",   required_argument, NULL, 'i'},
+{"mcast_iface",required_argument, NULL, 'j'},
 {"len",        required_argument, NULL, 'l'},
 {"print_mss",        no_argument, NULL, 'm'},
 {"num",        required_argument, NULL, 'n'},
@@ -136,6 +137,7 @@ const struct option env_options[] =
 {"IPERF_FORMAT",     required_argument, NULL, 'f'},
 // skip help
 {"IPERF_INTERVAL",   required_argument, NULL, 'i'},
+{"IPERF_MCASTIFACE", required_argument, NULL, 'j'},
 {"IPERF_LEN",        required_argument, NULL, 'l'},
 {"IPERF_PRINT_MSS",        no_argument, NULL, 'm'},
 {"IPERF_NUM",        required_argument, NULL, 'n'},
@@ -170,7 +172,7 @@ const struct option env_options[] =
 #define SHORT_OPTIONS()
 
 const char short_options[] =
-      "12b::c:df:hi:l:mn:o:p:rst:uvw:x:y:B:CDF:IL:M:NP:RS:T:UVW";
+      "12b::c:df:hi:j:l:mn:o:p:rst:uvw:x:y:B:CDF:IJ:L:M:NP:RS:T:UV:W";
 
 /* -------------------------------------------------------------------
  * defaults
@@ -196,6 +198,7 @@ void Settings_Initialize( thread_Settings *main ) {
     // below.
     memset( main, 0, sizeof(thread_Settings) );
     main->mSock         = INVALID_SOCKET;
+    //main->mSockAF     = AF_UNSPEC
     main->mReportMode   = kReport_Default;
     // option, defaults
     main->flags         = FLAG_MODETIME | FLAG_STDOUT; // Default time and stdout
@@ -226,6 +229,7 @@ void Settings_Initialize( thread_Settings *main ) {
     //main->mFileInput  = false;         // -F,
     //main->mFileName   = NULL;          // -F,  filename
     //main->mStdin      = false;         // -I,  default not stdin
+    //main->mMcastIface = 0;             // -J,  default: host chooses interface
     //main->mListenPort = 0;             // -L,  listen port
     //main->mMSS        = 0;             // -M,  ie. don't set MSS
     //main->mNodelay    = false;         // -N,  don't set nodelay
@@ -355,13 +359,6 @@ void Settings_Interpret( char option, const char *optarg, thread_Settings *mExtS
             strcpy( mExtSettings->mHost, optarg );
 
             if ( mExtSettings->mThreadMode == kMode_Unknown ) {
-                // Test for Multicast
-                iperf_sockaddr temp;
-                SockAddr_setHostname( mExtSettings->mHost, &temp,
-                                      (isIPV6( mExtSettings ) ? 1 : 0 ));
-                if ( SockAddr_isMulticast( &temp ) ) {
-                    setMulticast( mExtSettings );
-                }
                 mExtSettings->mThreadMode = kMode_Client;
                 mExtSettings->mThreads = 1;
             }
@@ -392,6 +389,13 @@ void Settings_Interpret( char option, const char *optarg, thread_Settings *mExtS
             }
             break;
 
+        case 'j':
+        case 'J': // multicast interface to join multicast address on
+            mExtSettings->mMcastIface = if_nametoindex(optarg);
+            if (!mExtSettings->mMcastIface)
+                   die("Interface \"%s\" does not work for -j/-J.", optarg);
+            break;
+
         case 'l': // length of each buffer
             Settings_GetUpperCaseArg(optarg,outarg);
             mExtSettings->mBufLen = byte_atoi( outarg );
@@ -541,13 +545,6 @@ void Settings_Interpret( char option, const char *optarg, thread_Settings *mExtS
         case 'B': // specify bind address
             mExtSettings->mLocalhost = new char[ strlen( optarg ) + 1 ];
             strcpy( mExtSettings->mLocalhost, optarg );
-            // Test for Multicast
-            iperf_sockaddr temp;
-            SockAddr_setHostname( mExtSettings->mLocalhost, &temp,
-                                  (isIPV6( mExtSettings ) ? 1 : 0 ));
-            if ( SockAddr_isMulticast( &temp ) ) {
-                setMulticast( mExtSettings );
-            }
             break;
 
         case 'C': // Run in Compatibility Mode
@@ -633,30 +630,15 @@ void Settings_Interpret( char option, const char *optarg, thread_Settings *mExtS
             mExtSettings->mTTL = atoi( optarg );
             break;
 
+        case 'V': // IP Domain: the optional ar
+            mExtSettings->mSockAF = AF_INET6;
+            if ( optarg  && optarg[0] == '4' )
+                mExtSettings->mSockAF = AF_INET;
+            break;
         case 'U': // single threaded UDP server
             setSingleUDP( mExtSettings );
             break;
 
-        case 'V': // IPv6 Domain
-            setIPV6( mExtSettings );
-            if ( mExtSettings->mThreadMode == kMode_Server 
-                 && mExtSettings->mLocalhost != NULL ) {
-                // Test for Multicast
-                iperf_sockaddr temp;
-                SockAddr_setHostname( mExtSettings->mLocalhost, &temp, 1);
-                if ( SockAddr_isMulticast( &temp ) ) {
-                    setMulticast( mExtSettings );
-                }
-            } else if ( mExtSettings->mThreadMode == kMode_Client ) {
-                // Test for Multicast
-                iperf_sockaddr temp;
-                SockAddr_setHostname( mExtSettings->mHost, &temp, 1 );
-                if ( SockAddr_isMulticast( &temp ) ) {
-                    setMulticast( mExtSettings );
-                }
-            }
-            break;
-
         case 'W' :
             setSuggestWin( mExtSettings );
             fprintf( stderr, "The -W option is not available in this release\n");
@@ -774,16 +756,7 @@ void Settings_GenerateClientSettings( thread_Settings *server,
             strcpy( (*client)->mLocalhost, server->mLocalhost );
         }
         (*client)->mHost = new char[REPORT_ADDRLEN];
-        if ( ((sockaddr*)&server->peer)->sa_family == AF_INET ) {
-            inet_ntop( AF_INET, &((sockaddr_in*)&server->peer)->sin_addr, 
-                       (*client)->mHost, REPORT_ADDRLEN);
-        }
-#ifdef HAVE_IPV6
-          else {
-            inet_ntop( AF_INET6, &((sockaddr_in6*)&server->peer)->sin6_addr, 
-                       (*client)->mHost, REPORT_ADDRLEN);
-        }
-#endif
+       SockAddr_name(&server->peer, (*client)->mHost, REPORT_ADDRLEN);
     } else {
         *client = NULL;
     }
index 0278bdf935260ccb37cdf02526220bd8d4380f4b..6e96e81a6d27261b91c25198314c46a7020f3409 100644 (file)
 #ifdef __cplusplus
 extern "C" {
 #endif
-/* -------------------------------------------------------------------
- * Create a socket address. If inHostname is not null, resolve that
- * address and fill it in. Fill in the port number. Use IPv6 ADDR_ANY
- * if that is what is desired.
- * ------------------------------------------------------------------- */
-
-void SockAddr_remoteAddr( thread_Settings *inSettings ) {
-    SockAddr_zeroAddress( &inSettings->peer );
-    if ( inSettings->mHost != NULL ) {
-        SockAddr_setHostname( inSettings->mHost, &inSettings->peer, 
-                              isIPV6( inSettings ) );
-    } else {
-#ifdef HAVE_IPV6
-        if ( isIPV6( inSettings ) ) {
-            ((struct sockaddr*)&inSettings->peer)->sa_family = AF_INET6;
-        } else {
-            ((struct sockaddr*)&inSettings->peer)->sa_family = AF_INET;
-        }
-    }
-
-    if ( SockAddr_isIPv6( &inSettings->peer ) ) {
-        inSettings->size_peer = sizeof( struct sockaddr_in6 );
-    } else {
-        inSettings->size_peer = sizeof( struct sockaddr_in );
-    }
-#else
-        ((struct sockaddr*)&inSettings->peer)->sa_family = AF_INET;
-    }
-    inSettings->size_peer = sizeof( struct sockaddr_in );
-#endif
-    SockAddr_setPort( &inSettings->peer, inSettings->mPort );
-}
-// end SocketAddr
-
-void SockAddr_localAddr( thread_Settings *inSettings ) {
-    SockAddr_zeroAddress( &inSettings->local );
-    if ( inSettings->mLocalhost != NULL ) {
-        SockAddr_setHostname( inSettings->mLocalhost, &inSettings->local, 
-                              isIPV6( inSettings ) );
-    } else {
-#ifdef HAVE_IPV6
-        if ( isIPV6( inSettings ) ) {
-            ((struct sockaddr*)&inSettings->local)->sa_family = AF_INET6;
-        } else {
-            ((struct sockaddr*)&inSettings->local)->sa_family = AF_INET;
-        }
-    }
-
-    if ( SockAddr_isIPv6( &inSettings->local ) ) {
-        inSettings->size_local = sizeof( struct sockaddr_in6 );
-    } else {
-        inSettings->size_local = sizeof( struct sockaddr_in );
-    }
-#else
-        ((struct sockaddr*)&inSettings->local)->sa_family = AF_INET;
-    }
-        inSettings->size_local = sizeof( struct sockaddr_in );
-#endif
-    SockAddr_setPort( &inSettings->local, inSettings->mPort );
-}
-// end SocketAddr
-
-/* -------------------------------------------------------------------
- * Resolve the hostname address and fill it in.
- * ------------------------------------------------------------------- */
-
-void SockAddr_setHostname( const char* inHostname, 
-                           iperf_sockaddr *inSockAddr, 
-                           int isIPv6 ) {
-
-    // ..I think this works for both ipv6 & ipv4... we'll see
-#if defined(HAVE_IPV6)
-    {
-        struct addrinfo *res, *itr;
-        int ret_ga;
-
-        ret_ga = getaddrinfo(inHostname, NULL, NULL, &res);
-        if ( ret_ga ) {
-            fprintf(stderr, "error: %s\n", gai_strerror(ret_ga));
-            exit(1);
-        }
-        if ( !res->ai_addr ) {
-            fprintf(stderr, "getaddrinfo failed to get an address... target was '%s'\n", inHostname);
-            exit(1);
-        }
-
-        // Check address type before filling in the address
-        // ai_family = PF_xxx; ai_protocol = IPPROTO_xxx, see netdb.h
-        // ...but AF_INET6 == PF_INET6
-        itr = res;
-        if ( isIPv6 ) {
-            // First check all results for a IPv6 Address
-            while ( itr != NULL ) {
-                if ( itr->ai_family == AF_INET6 ) {
-                    memcpy(inSockAddr, (itr->ai_addr),
-                           (itr->ai_addrlen));
-                    freeaddrinfo(res);
-                    return;
-                } else {
-                    itr = itr->ai_next;
-                }
-            }
-        }
-        itr = res;
-        // Now find a IPv4 Address
-        while ( itr != NULL ) {
-            if ( itr->ai_family == AF_INET ) {
-                memcpy(inSockAddr, (itr->ai_addr),
-                       (itr->ai_addrlen));
-                freeaddrinfo(res);
-                return;
-            } else {
-                itr = itr->ai_next;
-            }
-        }
-    }
-#else
-    // first try just converting dotted decimal
-    // on Windows gethostbyname doesn't understand dotted decimal
-    int rc = inet_pton( AF_INET, inHostname, 
-                        (unsigned char*)&(((struct sockaddr_in*)inSockAddr)->sin_addr) );
-    inSockAddr->sin_family = AF_INET;
-    if ( rc == 0 ) {
-        struct hostent *hostP = gethostbyname( inHostname );
-        if ( hostP == NULL ) {
-            /* this is the same as herror() but works on more systems */
-            const char* format;
-            switch ( h_errno ) {
-                case HOST_NOT_FOUND:
-                    format = "%s: Unknown host\n";
-                    break;
-                case NO_ADDRESS:
-                    format = "%s: No address associated with name\n";
-                    break;
-                case NO_RECOVERY:
-                    format = "%s: Unknown server error\n";
-                    break;
-                case TRY_AGAIN:
-                    format = "%s: Host name lookup failure\n";
-                    break;
-
-                default:
-                    format = "%s: Unknown resolver error\n";
-                    break;
-            }
-            fprintf( stderr, format, inHostname );
-            exit(1);
-
-            return; // TODO throw
-        }
-
-        memcpy(&(((struct sockaddr_in*)inSockAddr)->sin_addr), *(hostP->h_addr_list),
-               (hostP->h_length));
-    }
-#endif
-}
-// end setHostname
-
-/* -------------------------------------------------------------------
- * Copy the IP address into the string.
- * ------------------------------------------------------------------- */
-void SockAddr_getHostAddress( iperf_sockaddr *inSockAddr, char* outAddress, 
-                                size_t len ) {
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET ) {
-        inet_ntop( AF_INET, &(((struct sockaddr_in*) inSockAddr)->sin_addr), 
-                   outAddress, len);
-    }
-#ifdef HAVE_IPV6
-    else {
-        inet_ntop( AF_INET6, &(((struct sockaddr_in6*) inSockAddr)->sin6_addr), 
-                   outAddress, len);
-    }
-#endif
-}
-// end getHostAddress
 
-/* -------------------------------------------------------------------
- * Set the address to any (generally all zeros).
- * ------------------------------------------------------------------- */
-
-void SockAddr_setAddressAny( iperf_sockaddr *inSockAddr ) {
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET )
-        memset( &(((struct sockaddr_in*) inSockAddr)->sin_addr), 0, 
-                sizeof( struct in_addr ));
-#if defined(HAVE_IPV6)  
-    else
-        memset( &(((struct sockaddr_in6*) inSockAddr)->sin6_addr), 0, 
-                sizeof( struct in6_addr ));
-#endif
+/* return the port of @ss, -1 if undefined */
+int SockAddr_port(struct sockaddr_storage *ss)
+{
+       if (ss->ss_family == AF_INET)
+               return ntohs(((struct sockaddr_in *)ss)->sin_port);
+       else if (ss->ss_family == AF_INET6)
+               return ntohs(((struct sockaddr_in6 *)ss)->sin6_port);
+       return -1;
 }
-// end setAddressAny
 
 /* -------------------------------------------------------------------
- * Set the port to the given port. Handles the byte swapping.
- * ------------------------------------------------------------------- */
-
-void SockAddr_setPort( iperf_sockaddr *inSockAddr, unsigned short inPort ) {
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET )
-        ((struct sockaddr_in*) inSockAddr)->sin_port = htons( inPort );
-#if defined(HAVE_IPV6)  
-    else
-        ((struct sockaddr_in6*) inSockAddr)->sin6_port = htons( inPort );
+ * Print socket name into output (must be INET6_ADDRSTRLEN long).
+ * This one works around the quirk of inet_ntop/getaddrinfo to represent
+ * print IPv4-mapped-IPv6 addresses as sockaddr_in6 (::FFFF:x.x.x.x).
+ * Make the #ifndef an `#ifdef' to disable this conversion.
+ * ------------------------------------------------------------------- */
+const char *SockAddr_name(struct sockaddr_storage *ss, char *hbuf, size_t hbuflen)
+{
+       int af = ss->ss_family;
+       const void *ptr;
+
+       if (ss->ss_family == AF_INET)
+               ptr = &((struct sockaddr_in *)ss)->sin_addr;
+       else if (ss->ss_family == AF_INET6) {
+               const struct in6_addr *v6_addr;
+
+               ptr = v6_addr = &((struct sockaddr_in6 *)ss)->sin6_addr;
+#ifndef I_WANT_V4_MAPPED_IN_MY_OUTPUT
+               if (IN6_IS_ADDR_V4MAPPED(v6_addr)) {
+                       struct in_addr ia;
+
+                       // extract the IPv4 address from the last 4 bytes
+                       memcpy(&ia.s_addr, &v6_addr->s6_addr[12], 4);
+                       ptr = &ia;
+                       af  = AF_INET;
+               }
 #endif
-
-}
-// end setPort
-
-/* -------------------------------------------------------------------
- * Set the port to zero, which lets the OS pick the port.
- * ------------------------------------------------------------------- */
-
-void SockAddr_setPortAny( iperf_sockaddr *inSockAddr ) {
-    SockAddr_setPort( inSockAddr, 0 );
-}
-// end setPortAny
-
-/* -------------------------------------------------------------------
- * Return the port. Handles the byte swapping.
- * ------------------------------------------------------------------- */
-
-unsigned short SockAddr_getPort( iperf_sockaddr *inSockAddr ) {
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET )
-        return ntohs( ((struct sockaddr_in*) inSockAddr)->sin_port );
-#if defined(HAVE_IPV6)
-    else
-        return ntohs( ((struct sockaddr_in6*) inSockAddr)->sin6_port);
-#endif
-    return 0;
-
-}
-// end getPort
-
-/* -------------------------------------------------------------------
- * Return the IPv4 Internet Address from the sockaddr_in structure
- * ------------------------------------------------------------------- */
-
-struct in_addr* SockAddr_get_in_addr( iperf_sockaddr *inSockAddr ) {
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET )
-        return &(((struct sockaddr_in*) inSockAddr)->sin_addr);
-
-    fprintf(stderr, "FATAL: get_in_addr called on IPv6 address\n");
-    return NULL;
-}
-
-/* -------------------------------------------------------------------
- * Return the IPv6 Internet Address from the sockaddr_in6 structure
- * ------------------------------------------------------------------- */
-#ifdef HAVE_IPV6
-struct in6_addr* SockAddr_get_in6_addr( iperf_sockaddr *inSockAddr ) {
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET6 )
-        return &(((struct sockaddr_in6*) inSockAddr)->sin6_addr);
-
-    fprintf(stderr, "FATAL: get_in6_addr called on IPv4 address\n");
-    return NULL;
-}
-#endif
-
-
-/* -------------------------------------------------------------------
- * Return the size of the appropriate address structure.
- * ------------------------------------------------------------------- */
-
-Socklen_t SockAddr_get_sizeof_sockaddr( iperf_sockaddr *inSockAddr ) {
-
-#if defined(HAVE_IPV6)
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET6 ) {
-        return(sizeof(struct sockaddr_in6));
-    }
-#endif
-    return(sizeof(struct sockaddr_in));
-}
-// end get_sizeof_sockaddr
-
-
-/* -------------------------------------------------------------------
- * Return if IPv6 socket
- * ------------------------------------------------------------------- */
-
-int SockAddr_isIPv6( iperf_sockaddr *inSockAddr ) {
-
-#if defined(HAVE_IPV6)
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET6 ) {
-        return 1;
-    }
-#endif
-    return 0;
+        } else {
+               return NULL;    // undetermined family (e.g. AF_UNSPEC)
+       }
+       return inet_ntop(af, ptr, hbuf, hbuflen);
 }
-// end get_sizeof_sockaddr
-
-/* -------------------------------------------------------------------
- * Return true if the address is a IPv4 multicast address.
- * ------------------------------------------------------------------- */
 
-int SockAddr_isMulticast( iperf_sockaddr *inSockAddr ) {
-
-#if defined(HAVE_IPV6)
-    if ( ((struct sockaddr*)inSockAddr)->sa_family == AF_INET6 ) {
-        return( IN6_IS_ADDR_MULTICAST(&(((struct sockaddr_in6*) inSockAddr)->sin6_addr) ));
-    } else
-#endif
-    {
-        // 224.0.0.0 to 239.255.255.255 (e0.00.00.00 to ef.ff.ff.ff)
-        const unsigned long kMulticast_Mask = 0xe0000000L;
-
-        return(kMulticast_Mask ==
-               (ntohl( ((struct sockaddr_in*) inSockAddr)->sin_addr.s_addr) & kMulticast_Mask));
-    }
+static int SockAddr_AF_are_Equal(struct sockaddr_storage *first, struct sockaddr_storage *second)
+{
+       return first->ss_family == second->ss_family;
 }
-// end isMulticast
 
 /* -------------------------------------------------------------------
- * Zero out the address structure.
+ * Compare two sockaddrs and return true if the hosts are equal
  * ------------------------------------------------------------------- */
-
-void SockAddr_zeroAddress( iperf_sockaddr *inSockAddr ) {
-    memset( inSockAddr, 0, sizeof( iperf_sockaddr ));
+int SockAddr_Hostare_Equal(struct sockaddr_storage *first, struct sockaddr_storage *second)
+{
+       if (!SockAddr_AF_are_Equal(first, second))
+               return 0;
+       if (first->ss_family == AF_INET)
+               return !memcmp(&((struct sockaddr_in *)first)->sin_addr,
+                              &((struct sockaddr_in *)second)->sin_addr, sizeof(struct in_addr));
+       if (first->ss_family == AF_INET6)
+               return !memcmp(&((struct sockaddr_in6 *)first)->sin6_addr,
+                              &((struct sockaddr_in6 *)second)->sin6_addr, sizeof(struct in6_addr));
+       return 0;
 }
-// zeroAddress
 
 /* -------------------------------------------------------------------
- * Compare two sockaddrs and return true if they are equal
+ * Compare two socket structs and return true if they are equal
  * ------------------------------------------------------------------- */
-int SockAddr_are_Equal( struct sockaddr* first, struct sockaddr* second ) {
-    if ( first->sa_family == AF_INET && second->sa_family == AF_INET ) {
-        // compare IPv4 adresses
-        return( ((long) ((struct sockaddr_in*)first)->sin_addr.s_addr == (long) ((struct sockaddr_in*)second)->sin_addr.s_addr)
-                && ( ((struct sockaddr_in*)first)->sin_port == ((struct sockaddr_in*)second)->sin_port) );
-    }
-#if defined(HAVE_IPV6)      
-    if ( first->sa_family == AF_INET6 && second->sa_family == AF_INET6 ) {
-        // compare IPv6 addresses
-        return( !memcmp(((struct sockaddr_in6*)first)->sin6_addr.s6_addr, ((struct sockaddr_in6*)second)->sin6_addr.s6_addr, sizeof(struct in6_addr)) 
-                && (((struct sockaddr_in6*)first)->sin6_port == ((struct sockaddr_in6*)second)->sin6_port) );
-    }
-#endif 
-    return 0;
-
+int SockAddr_are_Equal(struct sockaddr_storage *first, struct sockaddr_storage *second)
+{
+       if (!SockAddr_Hostare_Equal(first, second))
+               return 0;
+       if (first->ss_family == AF_INET)
+               return ((struct sockaddr_in*)first)->sin_port == ((struct sockaddr_in*)second)->sin_port;
+       if (first->ss_family == AF_INET6)
+               return (((struct sockaddr_in6*)first)->sin6_port == ((struct sockaddr_in6*)second)->sin6_port);
+       return 0;
 }
 
-/* -------------------------------------------------------------------
- * Compare two sockaddrs and return true if the hosts are equal
- * ------------------------------------------------------------------- */
-int SockAddr_Hostare_Equal( struct sockaddr* first, struct sockaddr* second ) {
-    if ( first->sa_family == AF_INET && second->sa_family == AF_INET ) {
-        // compare IPv4 adresses
-        return( (long) ((struct sockaddr_in*)first)->sin_addr.s_addr == 
-                (long) ((struct sockaddr_in*)second)->sin_addr.s_addr);
-    }
-#if defined(HAVE_IPV6)      
-    if ( first->sa_family == AF_INET6 && second->sa_family == AF_INET6 ) {
-        // compare IPv6 addresses
-        return( !memcmp(((struct sockaddr_in6*)first)->sin6_addr.s6_addr, 
-                        ((struct sockaddr_in6*)second)->sin6_addr.s6_addr, sizeof(struct in6_addr)));
-    }
-#endif 
-    return 0;
-
-}
 #ifdef __cplusplus
 } /* end extern "C" */
 #endif