From a5a03e6ab0fdf9c4d7d4c085fcf01436f9ba3202 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Tue, 24 Feb 2009 20:31:40 +0100 Subject: [PATCH] Native IPv6 support for iperf 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 or -j 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. --- include/List.h | 10 +- include/Reporter.h | 7 +- include/Settings.hpp | 12 +- include/SocketAddr.h | 48 ++---- include/headers.h | 12 +- src/Client.cpp | 33 +--- src/List.cpp | 29 ++-- src/Listener.cpp | 187 +++++++------------- src/Locale.c | 2 +- src/PerfSocket.cpp | 155 +++++++++++++---- src/ReportCSV.c | 55 ++---- src/ReportDefault.c | 80 +++------ src/Reporter.c | 11 +- src/Settings.cpp | 63 ++----- src/SocketAddr.c | 403 +++++++------------------------------------ 15 files changed, 357 insertions(+), 750 deletions(-) diff --git a/include/List.h b/include/List.h index 13ec44c..326be4b 100644 --- a/include/List.h +++ b/include/List.h @@ -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 diff --git a/include/Reporter.h b/include/Reporter.h index 174055f..db3defe 100644 --- a/include/Reporter.h +++ b/include/Reporter.h @@ -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; diff --git a/include/Settings.hpp b/include/Settings.hpp index bf125cb..3a9fa2a 100644 --- a/include/Settings.hpp +++ b/include/Settings.hpp @@ -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; diff --git a/include/SocketAddr.h b/include/SocketAddr.h index feffdc7..37a7359 100644 --- a/include/SocketAddr.h +++ b/include/SocketAddr.h @@ -55,48 +55,34 @@ #include "headers.h" #include "Settings.hpp" +#include #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 diff --git a/include/headers.h b/include/headers.h index 5b534f7..0bc12db 100644 --- a/include/headers.h +++ b/include/headers.h @@ -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 diff --git a/src/Client.cpp b/src/Client.cpp index 2195c4f..b1dda9c 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -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) { diff --git a/src/List.cpp b/src/List.cpp index 8cba006..4b850a6 100644 --- a/src/List.cpp +++ b/src/List.cpp @@ -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); } diff --git a/src/Listener.cpp b/src/Listener.cpp index 1cc0de9..8afd6aa 100644 --- a/src/Listener.cpp +++ b/src/Listener.cpp @@ -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; diff --git a/src/Locale.c b/src/Locale.c index b2e8d1f..acad49c 100644 --- a/src/Locale.c +++ b/src/Locale.c @@ -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"; diff --git a/src/PerfSocket.cpp b/src/PerfSocket.cpp index d149d59..2c16901 100644 --- a/src/PerfSocket.cpp +++ b/src/PerfSocket.cpp @@ -76,23 +76,6 @@ #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)); } diff --git a/src/ReportCSV.c b/src/ReportCSV.c index b9ea604..7319255 100644 --- a/src/ReportCSV.c +++ b/src/ReportCSV.c @@ -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; } diff --git a/src/ReportDefault.c b/src/ReportDefault.c index a3ecc97..dcb3852 100644 --- a/src/ReportDefault.c +++ b/src/ReportDefault.c @@ -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) diff --git a/src/Reporter.c b/src/Reporter.c index db9418e..c5eff5c 100644 --- a/src/Reporter.c +++ b/src/Reporter.c @@ -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 /* diff --git a/src/Settings.cpp b/src/Settings.cpp index 4cde826..549a9cc 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -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; } diff --git a/src/SocketAddr.c b/src/SocketAddr.c index 0278bdf..6e96e81 100644 --- a/src/SocketAddr.c +++ b/src/SocketAddr.c @@ -59,364 +59,85 @@ #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 -- 2.39.2