From 90fc1e2c0c74319759b21d4a177c32691b88fdf3 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Tue, 24 Feb 2009 20:31:15 +0100 Subject: [PATCH] DCCP support for iperf This adds DCCP support for iperf, based on a patch developed by Ian and later much modified and extended by myself. When applied, DCCP can be used as transport protocol via the `-d' option. It knows two modes: (1) Bytestream Mode This is the default and works as in TCP. The application tries to stuff as many bytes into the socket as possible. This is good for performance tests with DCCP CCID2, but detrimental in combination with DCCP CCID3, since it invariably pushes the sending rate beyond controllable limits. (2) Packet-oriented mode This is enabled by setting the `-b' option in addition to `-d' and sets up, as before, a constant bitrate datagram stream. This option (also as before) understands the format specifiers `k' for kilobits/sec and `m' for megabits/sec. If the optional argument to `-b' is omitted, then a default of 1 megabit/sec is used. Note that when using this mode, it needs to be enabled both on the sender and the receiver. The changes I added were: * made counting of packets work consistently across UDP and DCCP; * enabled UDP-like reporting of statistics also for DCCP; * rewrote the algorithm to compute the inter-packet-gap in CBR mode: - in DCCP it tried to stuff the pipe without accounting for the time it spent, thus producing more packets than specifie - in tests this was observed to cause overflow - the algorithm now measures the actual inter-packet gap of each packet and compares it to the target; adjusting for each send time - it is more accurate than the previous implementation, in particular so for DCCP * fixed a bug which resulted in disabling IPv6 (the order of statements in Client::Connect() is important; the local socket must be initialised first for IPv6 to work). --- compat/error.c | 16 +++ include/Client.hpp | 2 +- include/Locale.h | 4 +- include/PerfSocket.hpp | 1 + include/Reporter.h | 5 +- include/Server.hpp | 2 +- include/Settings.hpp | 62 +++++++-- include/headers.h | 17 ++- include/util.h | 11 +- include/version.h | 4 +- src/Client.cpp | 300 ++++++++++++++++++++++------------------- src/Extractor.c | 2 +- src/Listener.cpp | 134 ++++++++---------- src/Locale.c | 34 ++--- src/PerfSocket.cpp | 88 +++++++----- src/ReportDefault.c | 33 +++-- src/Reporter.c | 82 ++++++----- src/Server.cpp | 34 ++--- src/Settings.cpp | 151 +++++++++++---------- src/main.cpp | 14 ++ src/sockets.c | 16 +++ src/stdio.c | 2 +- src/tcp_window_size.c | 92 ++++++------- 23 files changed, 615 insertions(+), 491 deletions(-) diff --git a/compat/error.c b/compat/error.c index 1d3647e..6ba9ab3 100644 --- a/compat/error.c +++ b/compat/error.c @@ -101,6 +101,22 @@ void warn_errno( const char *inMessage, const char *inFile, int inLine ) { #endif } /* end warn_errno */ +/* fatal error */ +void die(const char *fmt, ...) +{ + va_list ap; + + fflush(stdout); /* in case stdout and stderr are the same */ + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + if (errno) + fprintf(stderr, " (%s)", strerror(errno)); + fputc('\n', stderr); + fflush(stderr); + exit(1); +} + #ifdef __cplusplus } /* end extern "C" */ #endif diff --git a/include/Client.hpp b/include/Client.hpp index e5d2e29..2d4364d 100644 --- a/include/Client.hpp +++ b/include/Client.hpp @@ -74,7 +74,7 @@ public: // UDP / TCP void Send( void ); - void write_UDP_FIN( ); + void write_dgram_FIN( ); // client connect void Connect( ); diff --git a/include/Locale.h b/include/Locale.h index 01f55b5..5365016 100644 --- a/include/Locale.h +++ b/include/Locale.h @@ -87,7 +87,7 @@ extern const char server_datagram_size[]; extern const char tcp_window_size[]; -extern const char udp_buffer_size[]; +extern const char dgram_buffer_size[]; extern const char window_default[]; @@ -165,8 +165,6 @@ extern const char warn_invalid_client_option[]; extern const char warn_invalid_compatibility_option[]; -extern const char warn_implied_udp[]; - extern const char warn_implied_compatibility[]; extern const char warn_buffer_too_small[]; diff --git a/include/PerfSocket.hpp b/include/PerfSocket.hpp index 2bfe3de..aa8f3b1 100644 --- a/include/PerfSocket.hpp +++ b/include/PerfSocket.hpp @@ -59,6 +59,7 @@ #include "Mutex.h" #include "Settings.hpp" + void MakeSocket( thread_Settings *inSettings ); void SetSocketOptions( thread_Settings *inSettings ); // handle interupts diff --git a/include/Reporter.h b/include/Reporter.h index 91343f4..174055f 100644 --- a/include/Reporter.h +++ b/include/Reporter.h @@ -129,7 +129,7 @@ typedef struct ReporterData { int PacketID; int mBufLen; // -l int mMSS; // -M - int mTCPWin; // -w + int mWinSize; // -w /* flags is a BitMask of old bools bool mBufLenSet; // -l bool mCompat; // -C @@ -144,7 +144,8 @@ typedef struct ReporterData { bool mSuggestWin; // -W bool mUDP; bool mMode_time;*/ - int flags; + int flags; + Protocol mProtocol; // enums (which should be special int's) ThreadMode mThreadMode; // -s or -c ReportMode mode; diff --git a/include/Server.hpp b/include/Server.hpp index c11d54b..06ac698 100644 --- a/include/Server.hpp +++ b/include/Server.hpp @@ -71,7 +71,7 @@ public: // accepts connection and receives data void Run( void ); - void write_UDP_AckFIN( ); + void write_dgram_AckFin( ); static void Sig_Int( int inSigno ); diff --git a/include/Settings.hpp b/include/Settings.hpp index 4c3aede..bf125cb 100644 --- a/include/Settings.hpp +++ b/include/Settings.hpp @@ -69,6 +69,37 @@ extern "C" { #endif +// Convention: odd=connection-oriented, even=connection-less +typedef enum Protocol { + kProto_TCP = IPPROTO_TCP, + kProto_DCCP = IPPROTO_DCCP, + kProto_UDP = IPPROTO_UDP, +} Protocol; + +static inline const char *protoName(const unsigned proto) +{ + switch(proto) { + case kProto_TCP: return "TCP"; + case kProto_DCCP: return "DCCP"; + case kProto_UDP: return "UDP"; + default: return "(unknown)"; + } +} + +static inline unsigned sockType(const Protocol p) +{ + switch (p) { + case kProto_TCP: return SOCK_STREAM; + case kProto_UDP: return SOCK_DGRAM; + case kProto_DCCP: return SOCK_DCCP; + } +} + +static inline bool is_connectionless(const Protocol p) +{ + return p == kProto_UDP; +} + // server/client mode typedef enum ThreadMode { kMode_Unknown = 0, @@ -124,7 +155,7 @@ typedef struct thread_Settings { int Extractor_size; int mBufLen; // -l int mMSS; // -M - int mTCPWin; // -w + int mWinSize; // -w /* flags is a BitMask of old bools bool mBufLenSet; // -l bool mCompat; // -C @@ -147,13 +178,15 @@ typedef struct thread_Settings { bool mNoServerReport; // -x bool mNoMultReport; // -x m bool mSinlgeClient; // -1 */ - int flags; + int flags; + Protocol mProtocol; + // enums (which should be special int's) ThreadMode mThreadMode; // -s or -c ReportMode mReportMode; - TestMode mMode; // -r or -d + TestMode mMode; // -r or -d // Hopefully int64_t's - max_size_t mUDPRate; // -b or -u + max_size_t mDgramRate; // -b max_size_t mAmount; // -n or -t // doubles double mInterval; // -i @@ -194,7 +227,6 @@ typedef struct thread_Settings { #define FLAG_STDIN 0x00000100 #define FLAG_STDOUT 0x00000200 #define FLAG_SUGGESTWIN 0x00000400 -#define FLAG_UDP 0x00000800 #define FLAG_MODETIME 0x00001000 #define FLAG_REPORTSETTINGS 0x00002000 #define FLAG_MULTICAST 0x00004000 @@ -205,6 +237,8 @@ typedef struct thread_Settings { #define FLAG_NOMULTREPORT 0x00080000 #define FLAG_SINGLECLIENT 0x00100000 #define FLAG_SINGLEUDP 0x00200000 +#define FLAG_PACKETORIENTED 0x01000000 + #define isBuflenSet(settings) ((settings->flags & FLAG_BUFLENSET) != 0) #define isCompat(settings) ((settings->flags & FLAG_COMPAT) != 0) @@ -217,7 +251,6 @@ typedef struct thread_Settings { #define isSTDIN(settings) ((settings->flags & FLAG_STDIN) != 0) #define isSTDOUT(settings) ((settings->flags & FLAG_STDOUT) != 0) #define isSuggestWin(settings) ((settings->flags & FLAG_SUGGESTWIN) != 0) -#define isUDP(settings) ((settings->flags & FLAG_UDP) != 0) #define isModeTime(settings) ((settings->flags & FLAG_MODETIME) != 0) #define isReport(settings) ((settings->flags & FLAG_REPORTSETTINGS) != 0) #define isMulticast(settings) ((settings->flags & FLAG_MULTICAST) != 0) @@ -230,6 +263,8 @@ typedef struct thread_Settings { // end Active Low #define isSingleClient(settings) ((settings->flags & FLAG_SINGLECLIENT) != 0) #define isSingleUDP(settings) ((settings->flags & FLAG_SINGLEUDP) != 0) +#define isConnectionLess(settings) is_connectionless((settings)->mProtocol) +#define isPacketOriented(settings) (((settings)->flags & FLAG_PACKETORIENTED) != 0) #define setBuflenSet(settings) settings->flags |= FLAG_BUFLENSET #define setCompat(settings) settings->flags |= FLAG_COMPAT @@ -242,7 +277,6 @@ typedef struct thread_Settings { #define setSTDIN(settings) settings->flags |= FLAG_STDIN #define setSTDOUT(settings) settings->flags |= FLAG_STDOUT #define setSuggestWin(settings) settings->flags |= FLAG_SUGGESTWIN -#define setUDP(settings) settings->flags |= FLAG_UDP #define setModeTime(settings) settings->flags |= FLAG_MODETIME #define setReport(settings) settings->flags |= FLAG_REPORTSETTINGS #define setMulticast(settings) settings->flags |= FLAG_MULTICAST @@ -253,6 +287,7 @@ typedef struct thread_Settings { #define setNoMultReport(settings) settings->flags |= FLAG_NOMULTREPORT #define setSingleClient(settings) settings->flags |= FLAG_SINGLECLIENT #define setSingleUDP(settings) settings->flags |= FLAG_SINGLEUDP +#define setPacketOriented(settings) settings->flags |= FLAG_PACKETORIENTED #define unsetBuflenSet(settings) settings->flags &= ~FLAG_BUFLENSET #define unsetCompat(settings) settings->flags &= ~FLAG_COMPAT @@ -265,7 +300,6 @@ typedef struct thread_Settings { #define unsetSTDIN(settings) settings->flags &= ~FLAG_STDIN #define unsetSTDOUT(settings) settings->flags &= ~FLAG_STDOUT #define unsetSuggestWin(settings) settings->flags &= ~FLAG_SUGGESTWIN -#define unsetUDP(settings) settings->flags &= ~FLAG_UDP #define unsetModeTime(settings) settings->flags &= ~FLAG_MODETIME #define unsetReport(settings) settings->flags &= ~FLAG_REPORTSETTINGS #define unsetMulticast(settings) settings->flags &= ~FLAG_MULTICAST @@ -276,14 +310,18 @@ typedef struct thread_Settings { #define unsetNoMultReport(settings) settings->flags &= ~FLAG_NOMULTREPORT #define unsetSingleClient(settings) settings->flags &= ~FLAG_SINGLECLIENT #define unsetSingleUDP(settings) settings->flags &= ~FLAG_SINGLEUDP +#define unsetPacketOriented(settings) settings->flags &= ~FLAG_PACKETORIENTED #define HEADER_VERSION1 0x80000000 #define RUN_NOW 0x00000001 -// used to reference the 4 byte ID number we place in UDP datagrams -// use int32_t if possible, otherwise a 32 bit bitfield (e.g. on J90) -typedef struct UDP_datagram { +/* + * Datagram record for record-oriented applications + * used to reference the 4 byte ID number we place in UDP datagrams + * use int32_t if possible, otherwise a 32 bit bitfield (e.g. on J90) + */ +typedef struct dgram_record { #ifdef HAVE_INT32_T int32_t id; u_int32_t tv_sec; @@ -293,7 +331,7 @@ typedef struct UDP_datagram { unsigned int tv_sec : 32; unsigned int tv_usec : 32; #endif -} UDP_datagram; +} dgram_record; /* * The client_hdr structure is sent from clients diff --git a/include/headers.h b/include/headers.h index 6bf6da8..5b534f7 100644 --- a/include/headers.h +++ b/include/headers.h @@ -153,10 +153,15 @@ typedef uintmax_t max_size_t; #define SHUT_RDWR 2 #endif // SHUT_RD +/* DCCP-specific definitions */ +#include +#ifndef SOCK_DCCP +#define SOCK_DCCP 6 /* include/linux/net.h */ +#endif +#ifndef IPPROTO_DCCP +#define IPPROTO_DCCP 33 /* include/linux/in.h */ +#endif +#ifndef SOL_DCCP +#define SOL_DCCP 269 /* include/linux/socket.h */ +#endif #endif /* HEADERS_H */ - - - - - - diff --git a/include/util.h b/include/util.h index d23618c..0f2d425 100644 --- a/include/util.h +++ b/include/util.h @@ -52,7 +52,7 @@ #ifndef UTIL_H #define UTIL_H - +#include #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -65,12 +65,15 @@ extern "C" { * set/getsockopt wrappers for SO_RCVBUF and SO_SNDBUF; TCP_MAXSEG * socket.c * ------------------------------------------------------------------- */ -int setsock_tcp_windowsize( int inSock, int inTCPWin, int inSend ); -int getsock_tcp_windowsize( int inSock, int inSend ); +int setsock_tcp_windowsize(int inSock, int inTCPWin, int inSend); +int set_buffer_sock_size(int inSock, int inWinSize, bool inSend); +int get_buffer_sock_size(int inSock, int inSend); void setsock_tcp_mss( int inSock, int inTCPWin ); int getsock_tcp_mss( int inSock ); +int getsock_dccp_mps( int inSock ); + /* ------------------------------------------------------------------- * signal handlers * signal.c @@ -86,6 +89,7 @@ SigfuncPtr my_signal( int inSigno, SigfuncPtr inFunc ); * error handlers * error.c * ------------------------------------------------------------------- */ +void die(const char *fmt, ...); void warn ( const char *inMessage, const char *inFile, int inLine ); void warn_errno( const char *inMessage, const char *inFile, int inLine ); @@ -180,6 +184,5 @@ void redirect(const char *inOutputFileName); #ifdef __cplusplus } /* end extern "C" */ #endif - #endif /* UTIL_H */ diff --git a/include/version.h b/include/version.h index 59d3ada..f8f2c7d 100644 --- a/include/version.h +++ b/include/version.h @@ -1,2 +1,2 @@ -#define IPERF_VERSION "2.0.2" -#define IPERF_VERSION_DATE "03 May 2005" +#define IPERF_VERSION "2.0.2 with support for DCCP" +#define IPERF_VERSION_DATE "20th Jan 2009" diff --git a/src/Client.cpp b/src/Client.cpp index d6324d9..2195c4f 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -69,6 +69,9 @@ Client::Client( thread_Settings *inSettings ) { mSettings = inSettings; mBuf = NULL; + // connect + Connect( ); + // initialize buffer mBuf = new char[ mSettings->mBufLen ]; pattern( mBuf, mSettings->mBufLen ); @@ -83,9 +86,6 @@ Client::Client( thread_Settings *inSettings ) { } } - // connect - Connect( ); - if ( isReport( inSettings ) ) { ReportSettings( inSettings ); if ( mSettings->multihdr && isMultipleReport( inSettings ) ) { @@ -112,8 +112,8 @@ Client::~Client() { DELETE_ARRAY( mBuf ); } // end ~Client -const double kSecs_to_usecs = 1e6; -const int kBytes_to_Bits = 8; +const double kSecs_to_usecs = 1.0e6; +const double kBytes_to_Bits = 8.0; /* ------------------------------------------------------------------- * Send data using the connected UDP/TCP socket, @@ -122,17 +122,12 @@ const int kBytes_to_Bits = 8; * ------------------------------------------------------------------- */ void Client::Run( void ) { - struct UDP_datagram* mBuf_UDP = (struct UDP_datagram*) mBuf; - long currLen = 0; - - int delay_target = 0; - int delay = 0; - int adjust = 0; - + struct dgram_record* mBuf_Dgram = (struct dgram_record*) mBuf; + long currLen = 0, packet_gap = 0, delta, loop_time = 0, adjust = 0; char* readAt = mBuf; - - // Indicates if the stream is readable - bool canRead = true, mMode_Time = isModeTime( mSettings ); + bool canRead = true, // Indicates if the stream is readable + mMode_Time = isModeTime( mSettings ); + ReportStruct *reportstruct = NULL; // setup termination variables if ( mMode_Time ) { @@ -140,43 +135,46 @@ void Client::Run( void ) { mEndTime.add( mSettings->mAmount / 100.0 ); } - if ( isUDP( mSettings ) ) { - // Due to the UDP timestamps etc, included - // reduce the read size by an amount - // equal to the header size - + if ( isPacketOriented( mSettings ) ) { // compute delay for bandwidth restriction, constrained to [0,1] seconds - delay_target = (int) ( mSettings->mBufLen * ((kSecs_to_usecs * kBytes_to_Bits) - / mSettings->mUDPRate) ); - if ( delay_target < 0 || - delay_target > (int) 1 * kSecs_to_usecs ) { - fprintf( stderr, warn_delay_large, delay_target / kSecs_to_usecs ); - delay_target = (int) kSecs_to_usecs * 1; + packet_gap = (long)((double)(mSettings->mBufLen * kSecs_to_usecs * kBytes_to_Bits) / + (double)mSettings->mDgramRate); + + if (packet_gap < 0 || packet_gap > kSecs_to_usecs) { + fprintf( stderr, warn_delay_large, packet_gap / kSecs_to_usecs ); + packet_gap = (long)kSecs_to_usecs; } + // Initialise adjustment variable: in this way, the first adjustment will be + // the latency for sending the first packet, and all sending times are + // synchronised with regard to the first timestamp. + adjust = -packet_gap; + + // Due to the included timestamps etc, + // reduce the read size by an amount equal to the header size if ( isFileInput( mSettings ) ) { - if ( isCompat( mSettings ) ) { - Extractor_reduceReadSize( sizeof(struct UDP_datagram), mSettings ); - readAt += sizeof(struct UDP_datagram); - } else { - Extractor_reduceReadSize( sizeof(struct UDP_datagram) + - sizeof(struct client_hdr), mSettings ); - readAt += sizeof(struct UDP_datagram) + - sizeof(struct client_hdr); - } + size_t offset = sizeof(struct dgram_record); + + if (!isCompat(mSettings)) + offset += sizeof(struct client_hdr); + + Extractor_reduceReadSize(offset, mSettings); + readAt += offset; } } - ReportStruct *reportstruct = NULL; - // InitReport handles Barrier for multiple Streams mSettings->reporthdr = InitReport( mSettings ); reportstruct = new ReportStruct; - reportstruct->packetID = 0; + // Connectionless protocols use the first message as "connect" message (which is + // counted, but their FIN is not counted. For connection-oriented protocols we + // start at message #1 instead of at #0; and the FIN is not counted. + reportstruct->packetID = (!isConnectionLess(mSettings) && isPacketOriented(mSettings)); + + // Set a timestamp now corresponding to an imaginary zero-th send time. lastPacketTime.setnow(); - - do { + do { // Test case: drop 17 packets and send 2 out-of-order: // sequence 51, 52, 70, 53, 54, 71, 72 //switch( datagramID ) { @@ -185,100 +183,122 @@ void Client::Run( void ) { // case 55: datagramID = 71; break; // default: break; //} - gettimeofday( &(reportstruct->packetTime), NULL ); - if ( isUDP( mSettings ) ) { - // store datagram ID into buffer - mBuf_UDP->id = htonl( (reportstruct->packetID)++ ); - mBuf_UDP->tv_sec = htonl( reportstruct->packetTime.tv_sec ); - mBuf_UDP->tv_usec = htonl( reportstruct->packetTime.tv_usec ); - - // delay between writes - // make an adjustment for how long the last loop iteration took - // TODO this doesn't work well in certain cases, like 2 parallel streams - adjust = delay_target + lastPacketTime.subUsec( reportstruct->packetTime ); - lastPacketTime.set( reportstruct->packetTime.tv_sec, - reportstruct->packetTime.tv_usec ); - - if ( adjust > 0 || delay > 0 ) { - delay += adjust; - } - } + // Note that the timestamp does not account for the time-to-wire of the packet. + // This plays a role when looking at jitter/delay values. + gettimeofday( &(reportstruct->packetTime), NULL ); - // Read the next data block from - // the file if it's file input + if ( isPacketOriented( mSettings ) ) { + // Increment packet ID after sending and before reporting + // UDP: sends from 0..n-1, counts from 1..n + // DCCP: sends from 1..n, counts from 2..n+1 + // This difference is of interest for the client only (the server sees + // 1..n). The client uses cntDatagrams, so that the count is correct. + // (Consider the trick in CloseReport() which resetts packetID.) + mBuf_Dgram->id = htonl( reportstruct->packetID++ ); + mBuf_Dgram->tv_sec = htonl( reportstruct->packetTime.tv_sec ); + mBuf_Dgram->tv_usec = htonl( reportstruct->packetTime.tv_usec ); + } else if (!mMode_Time && mSettings->mAmount < mSettings->mBufLen) + mSettings->mBufLen = mSettings->mAmount; + + // Read the next data block from the file if it's file input if ( isFileInput( mSettings ) ) { Extractor_getNextDataBlock( readAt, mSettings ); canRead = Extractor_canRead( mSettings ) != 0; - } else - canRead = true; - - // perform write - currLen = write( mSettings->mSock, mBuf, mSettings->mBufLen ); - if ( currLen < 0 ) { - WARN_errno( currLen < 0, "write2" ); - break; } - // report packets + // Put the packet onto the wire. + // When EAGAIN is returned (DCCP), the internal TX buffer is full: due to + // congestion-control issues the packet can not be transported now. + do { + currLen = write( mSettings->mSock, mBuf, mSettings->mBufLen ); + } while (currLen < 0 && errno == EAGAIN); + + if (currLen < 0) { + WARN_errno(1, "client write"); + break; + } + // update statistics reportstruct->packetLen = currLen; ReportPacket( mSettings->reporthdr, reportstruct ); - - if ( delay > 0 ) { - delay_loop( delay ); - } - if ( !mMode_Time ) { + + if (mMode_Time) { + // time-oriented mode (-t): termination + canRead = mEndTime.after(reportstruct->packetTime); + } else { + // amount-oriented mode (-n): mAmount is unsigned + if (currLen >= mSettings->mAmount) + break; mSettings->mAmount -= currLen; } - } while ( ! (sInterupted || - (mMode_Time && mEndTime.before( reportstruct->packetTime )) || - (!mMode_Time && 0 >= mSettings->mAmount)) && canRead ); + if (isPacketOriented(mSettings)) { + // Adjust the inter-packet gap using the following variables: + // delta is the gap in between calls to send() + // adjust acts as a token bucket whenever delta != packet_gap + // loop_time equals packet_gap if adjust==0, it is corrected otherwise + // + // TODO this doesn't work well in certain cases, like 2 parallel streams + // + delta = lastPacketTime.delta_usec(); + adjust += packet_gap - delta; + loop_time = packet_gap + adjust; + + if (loop_time > 0) + delay_loop(loop_time); + } + + } while (canRead && !sInterupted); // stop timing gettimeofday( &(reportstruct->packetTime), NULL ); CloseReport( mSettings->reporthdr, reportstruct ); - if ( isUDP( mSettings ) ) { + if ( isPacketOriented( mSettings ) ) { // send a final terminating datagram - // Don't count in the mTotalLen. The server counts this one, - // but didn't count our first datagram, so we're even now. - // The negative datagram ID signifies termination to the server. - + // For connectionless protocols, don't count in the mTotalLen. + // The server counts this one, but didn't count our first datagram + // (connect message), so we're even now. + // store datagram ID into buffer - mBuf_UDP->id = htonl( -(reportstruct->packetID) ); - mBuf_UDP->tv_sec = htonl( reportstruct->packetTime.tv_sec ); - mBuf_UDP->tv_usec = htonl( reportstruct->packetTime.tv_usec ); + // The negative datagram ID signifies termination to the server. + mBuf_Dgram->id = htonl( -(reportstruct->packetID) ); + mBuf_Dgram->tv_sec = htonl( reportstruct->packetTime.tv_sec ); + mBuf_Dgram->tv_usec = htonl( reportstruct->packetTime.tv_usec ); - if ( isMulticast( mSettings ) ) { + if ( isMulticast( mSettings ) ) write( mSettings->mSock, mBuf, mSettings->mBufLen ); - } else { - write_UDP_FIN( ); - } + else + write_dgram_FIN( ); } DELETE_PTR( reportstruct ); EndReport( mSettings->reporthdr ); } -// end Run - -void Client::InitiateServer() { - if ( !isCompat( mSettings ) ) { - int currLen; - client_hdr* temp_hdr; - if ( isUDP( mSettings ) ) { - UDP_datagram *UDPhdr = (UDP_datagram *)mBuf; - temp_hdr = (client_hdr*)(UDPhdr + 1); - } else { - temp_hdr = (client_hdr*)mBuf; - } - Settings_GenerateClientHdr( mSettings, temp_hdr ); - if ( !isUDP( mSettings ) ) { - currLen = send( mSettings->mSock, mBuf, sizeof(client_hdr), 0 ); - if ( currLen < 0 ) { - WARN_errno( currLen < 0, "write1" ); - } - } - } + +void Client::InitiateServer() +{ + client_hdr *temp_hdr = (client_hdr*)mBuf; + + if (isCompat(mSettings)) + return; + // connection-less protocols communicate their settings in the first + // packet sent to the server; this packet is not counted by the server + if (isConnectionLess(mSettings)) { + dgram_record *record_hdr = (dgram_record *)mBuf; + temp_hdr = (client_hdr*)(record_hdr + 1); + } + + Settings_GenerateClientHdr( mSettings, temp_hdr ); + + // connection-oriented protocols use a short "init" message + if ( !isConnectionLess( mSettings ) ) { + int rc; + + do { + rc = send(mSettings->mSock, mBuf, sizeof(client_hdr), 0); + } while (rc < 0 && errno == EAGAIN); + WARN_errno(rc < 0, "write failed in InitiateServer()"); + } } /* ------------------------------------------------------------------- @@ -286,31 +306,20 @@ void Client::InitiateServer() { * If inLocalhost is not null, bind to that address, specifying * which outgoing interface to use. * ------------------------------------------------------------------- */ - -void Client::Connect( ) { +void Client::Connect() +{ int rc; - SockAddr_remoteAddr( mSettings ); assert( mSettings->inHostname != NULL ); - // create an internet socket - int type = ( isUDP( mSettings ) ? SOCK_DGRAM : SOCK_STREAM); - - int domain = (SockAddr_isIPv6( &mSettings->peer ) ? -#ifdef HAVE_IPV6 - AF_INET6 -#else - AF_INET -#endif - : AF_INET); - - mSettings->mSock = socket( domain, type, 0 ); - WARN_errno( mSettings->mSock == INVALID_SOCKET, "socket" ); - + // 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 ); - - SockAddr_localAddr( mSettings ); if ( mSettings->mLocalhost != NULL ) { // bind socket to local address rc = bind( mSettings->mSock, (sockaddr*) &mSettings->local, @@ -327,25 +336,38 @@ void Client::Connect( ) { &mSettings->size_local ); getpeername( mSettings->mSock, (sockaddr*) &mSettings->peer, &mSettings->size_peer ); -} // end Connect + + /* The DCCP packet size must not exceed the MPS (RFC 4340, 14.) */ + if (mSettings->mProtocol == kProto_DCCP) { + unsigned mps = getsock_dccp_mps(mSettings->mSock); + + if (mSettings->mBufLen > mps) + die("Buffer length %d exceeds DCCP MPS %d. Use a smaller buffer size " + "(-l) on server/client.", mSettings->mBufLen, mps); + } +} + /* ------------------------------------------------------------------- * Send a datagram on the socket. The datagram's contents should signify * a FIN to the application. Keep re-transmitting until an * acknowledgement datagram is received. * ------------------------------------------------------------------- */ - -void Client::write_UDP_FIN( ) { - int rc; +void Client::write_dgram_FIN( ) { + int rc, count = 0; fd_set readSet; struct timeval timeout; + size_t len = mSettings->mBufLen; + + // we don't need the full buffer size here - it is not counted + if (!isConnectionLess(mSettings)) + len = sizeof(dgram_record); - int count = 0; while ( count < 10 ) { - count++; + count++; // write data - write( mSettings->mSock, mBuf, mSettings->mBufLen ); + write( mSettings->mSock, mBuf, len); // wait until the socket is readable, or our timeout expires FD_ZERO( &readSet ); @@ -365,14 +387,12 @@ void Client::write_UDP_FIN( ) { WARN_errno( rc < 0, "read" ); if ( rc < 0 ) { break; - } else if ( rc >= (int) (sizeof(UDP_datagram) + sizeof(server_hdr)) ) { - ReportServerUDP( mSettings, (server_hdr*) ((UDP_datagram*)mBuf + 1) ); + } else if ( rc >= (int) (sizeof(dgram_record) + sizeof(server_hdr)) ) { + ReportServerUDP( mSettings, (server_hdr*) ((dgram_record*)mBuf + 1) ); } - return; } } - fprintf( stderr, warn_no_ack, mSettings->mSock, count ); } -// end write_UDP_FIN +// end write_dgram_FIN diff --git a/src/Extractor.c b/src/Extractor.c index 2571ebf..6b4abda 100644 --- a/src/Extractor.c +++ b/src/Extractor.c @@ -128,7 +128,7 @@ int Extractor_canRead ( thread_Settings *mSettings ) { /** * This is used to reduce the read size - * Used in UDP transfer to accomodate the + * Used in datagram transfer to accommodate the * the header (timestamp) * @arg delta Size to reduce */ diff --git a/src/Listener.cpp b/src/Listener.cpp index cf3c159..1cc0de9 100644 --- a/src/Listener.cpp +++ b/src/Listener.cpp @@ -114,28 +114,26 @@ Listener::~Listener() { /* ------------------------------------------------------------------- * Listens for connections and starts Servers to handle data. * For TCP, each accepted connection spawns a Server thread. - * For UDP, handle all data in this thread for Win32 Only, otherwise - * spawn a new Server thread. + * For datagram-oriented protocols, spawn a new Server thread. * ------------------------------------------------------------------- */ -void Listener::Run( void ) { +void Listener::Run(void) +{ #ifdef sun - if ( ( isUDP( mSettings ) && - isMulticast( mSettings ) && - !isSingleUDP( mSettings ) ) || - isSingleUDP( mSettings ) ) { + if ( ( isPacketOriented( mSettings ) && + isMulticast( mSettings ) && + !isSingleUDP( mSettings ) ) || isSingleUDP( mSettings ) ) { UDPSingleServer(); - } else #else if ( isSingleUDP( mSettings ) ) { UDPSingleServer(); - } else #endif - { - bool client = false, UDP = isUDP( mSettings ), mCount = (mSettings->mThreads != 0); + } else { + bool client = false, + mCount = (mSettings->mThreads != 0); thread_Settings *tempSettings = NULL; Iperf_ListEntry *exist, *listtemp; - client_hdr* hdr = ( UDP ? (client_hdr*) (((UDP_datagram*)mBuf) + 1) : - (client_hdr*) mBuf); + client_hdr* hdr; + if ( mSettings->mHost != NULL ) { client = true; @@ -179,7 +177,7 @@ void Listener::Run( void ) { (sockaddr*) &server->peer ) ) { // Not allowed try again close( server->mSock ); - if ( isUDP( mSettings ) ) { + if ( isConnectionLess( mSettings ) ) { mSettings->mSock = -1; Listen(); } @@ -215,19 +213,19 @@ void Listener::Run( void ) { tempSettings = NULL; if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) { - if ( !UDP ) { - // TCP does not have the info yet + if ( !isConnectionLess(mSettings)) { + hdr = (client_hdr*) mBuf; + + // TCP/DCCP does not have the info yet if ( recv( server->mSock, (char*)hdr, sizeof(client_hdr), 0) > 0 ) { - Settings_GenerateClientSettings( server, &tempSettings, - hdr ); + Settings_GenerateClientSettings( server, &tempSettings, hdr ); } } else { - Settings_GenerateClientSettings( server, &tempSettings, - hdr ); + hdr = (client_hdr *) ( ((dgram_record*)mBuf) + 1 ); + Settings_GenerateClientSettings( server, &tempSettings, hdr ); } } - if ( tempSettings != NULL ) { client_init( tempSettings ); if ( tempSettings->mMode == kTest_DualTest ) { @@ -245,7 +243,7 @@ void Listener::Run( void ) { thread_start( server ); // create a new socket - if ( UDP ) { + if ( isConnectionLess( mSettings ) ) { mSettings->mSock = -1; Listen( ); } @@ -264,44 +262,32 @@ void Listener::Run( void ) { /* ------------------------------------------------------------------- * Setup a socket listening on a port. - * For TCP, this calls bind() and listen(). - * For UDP, this just calls bind(). + * For connection-oriented protocols, this calls bind() and listen(). + * For connection-less protocols, this just calls bind(). * If inLocalhost is not null, bind to that address rather than the * wildcard server address, specifying what incoming interface to * accept connections on. * ------------------------------------------------------------------- */ -void Listener::Listen( ) { +void Listener::Listen() +{ int rc; + int boolean = 1; + Socklen_t len = sizeof(boolean); SockAddr_localAddr( mSettings ); - - // create an internet TCP socket - int type = (isUDP( mSettings ) ? SOCK_DGRAM : SOCK_STREAM); - int domain = (SockAddr_isIPv6( &mSettings->local ) ? -#ifdef HAVE_IPV6 - AF_INET6 -#else - AF_INET -#endif - : AF_INET); - - mSettings->mSock = socket( domain, type, 0 ); - WARN_errno( mSettings->mSock == INVALID_SOCKET, "socket" ); + MakeSocket( mSettings ); SetSocketOptions( mSettings ); // reuse the address, so we can run if a former server was killed off - int boolean = 1; - Socklen_t len = sizeof(boolean); setsockopt( mSettings->mSock, SOL_SOCKET, SO_REUSEADDR, (char*) &boolean, len ); - // bind socket to server address + // listen for connections (TCP/DCCP only). rc = bind( mSettings->mSock, (sockaddr*) &mSettings->local, mSettings->size_local ); WARN_errno( rc == SOCKET_ERROR, "bind" ); - // listen for connections (TCP only). // default backlog traditionally 5 - if ( !isUDP( mSettings ) ) { + if ( !isConnectionLess( mSettings ) ) { rc = listen( mSettings->mSock, 5 ); WARN_errno( rc == SOCKET_ERROR, "listen" ); } @@ -375,17 +361,19 @@ void Listener::McastSetTTL( int val ) { * until a new connection arrives. * ------------------------------------------------------------------- */ -void Listener::Accept( thread_Settings *server ) { - +void Listener::Accept( thread_Settings *server ) +{ server->size_peer = sizeof(iperf_sockaddr); - if ( isUDP( server ) ) { + + if ( isConnectionLess( server ) ) { /* ------------------------------------------------------------------- - * Do the equivalent of an accept() call for UDP sockets. This waits - * on a listening UDP socket until we get a datagram. + * Do the equivalent of an accept() call for connection-less sockets. + * This waits on a listening datagram socket until we get a datagram. * ------------------------------------------------------------------- */ int rc; Iperf_ListEntry *exist; int32_t datagramID; + server->mSock = INVALID_SOCKET; while ( server->mSock == INVALID_SOCKET ) { rc = recvfrom( mSettings->mSock, mBuf, mSettings->mBufLen, 0, @@ -394,14 +382,14 @@ void Listener::Accept( thread_Settings *server ) { Mutex_Lock( &clients_mutex ); - // Handle connection for UDP sockets. + // Handle connection for datagram-based sockets. exist = Iperf_present( &server->peer, clients); - datagramID = ntohl( ((UDP_datagram*) mBuf)->id ); + datagramID = ntohl( ((dgram_record*) mBuf)->id ); if ( exist == NULL && datagramID >= 0 ) { server->mSock = mSettings->mSock; int rc = connect( server->mSock, (struct sockaddr*) &server->peer, server->size_peer ); - FAIL_errno( rc == SOCKET_ERROR, "connect UDP", mSettings ); + FAIL_errno( rc == SOCKET_ERROR, "datagram-based connect", mSettings ); } else { server->mSock = INVALID_SOCKET; } @@ -427,15 +415,17 @@ void Listener::Accept( thread_Settings *server ) { void Listener::UDPSingleServer( ) { - bool client = false, UDP = isUDP( mSettings ), mCount = (mSettings->mThreads != 0); + bool client = false, mCount = (mSettings->mThreads != 0); thread_Settings *tempSettings = NULL; Iperf_ListEntry *exist, *listtemp; int rc; int32_t datagramID; - client_hdr* hdr = ( UDP ? (client_hdr*) (((UDP_datagram*)mBuf) + 1) : - (client_hdr*) mBuf); ReportStruct *reportstruct = new ReportStruct; + dgram_record *dgram_hdr = (dgram_record *)mBuf; + + assert( isPacketOriented( mSettings ) ); + if ( mSettings->mHost != NULL ) { client = true; SockAddr_remoteAddr( mSettings ); @@ -461,15 +451,15 @@ void Listener::UDPSingleServer( ) { } - // Handle connection for UDP sockets. + // Handle connection for datagram-based sockets. exist = Iperf_present( &server->peer, clients); - datagramID = ntohl( ((UDP_datagram*) mBuf)->id ); + datagramID = ntohl( dgram_hdr->id ); if ( datagramID >= 0 ) { if ( exist != NULL ) { // read the datagram ID and sentTime out of the buffer reportstruct->packetID = datagramID; - reportstruct->sentTime.tv_sec = ntohl( ((UDP_datagram*) mBuf)->tv_sec ); - reportstruct->sentTime.tv_usec = ntohl( ((UDP_datagram*) mBuf)->tv_usec ); + reportstruct->sentTime.tv_sec = ntohl( dgram_hdr->tv_sec ); + reportstruct->sentTime.tv_usec = ntohl( dgram_hdr->tv_usec ); reportstruct->packetLen = rc; gettimeofday( &(reportstruct->packetTime), NULL ); @@ -489,8 +479,8 @@ void Listener::UDPSingleServer( ) { if ( exist != NULL ) { // read the datagram ID and sentTime out of the buffer reportstruct->packetID = -datagramID; - reportstruct->sentTime.tv_sec = ntohl( ((UDP_datagram*) mBuf)->tv_sec ); - reportstruct->sentTime.tv_usec = ntohl( ((UDP_datagram*) mBuf)->tv_usec ); + reportstruct->sentTime.tv_sec = ntohl( dgram_hdr->tv_sec ); + reportstruct->sentTime.tv_usec = ntohl( dgram_hdr->tv_usec ); reportstruct->packetLen = rc; gettimeofday( &(reportstruct->packetTime), NULL ); @@ -500,14 +490,9 @@ void Listener::UDPSingleServer( ) { gettimeofday( &(reportstruct->packetTime), NULL ); CloseReport( exist->server->reporthdr, reportstruct ); - if ( rc > (int) ( sizeof( UDP_datagram ) - + sizeof( server_hdr ) ) ) { - UDP_datagram *UDP_Hdr; - server_hdr *hdr; - - UDP_Hdr = (UDP_datagram*) mBuf; + if ( rc > (int)(sizeof(dgram_record) + sizeof(server_hdr)) ) { + server_hdr *hdr = (server_hdr *)(dgram_hdr + 1); Transfer_Info *stats = GetReport( exist->server->reporthdr ); - hdr = (server_hdr*) (UDP_Hdr+1); hdr->flags = htonl( HEADER_VERSION1 ); hdr->total_len1 = htonl( (long) (stats->TotalLen >> 32) ); @@ -526,13 +511,9 @@ void Listener::UDPSingleServer( ) { EndReport( exist->server->reporthdr ); exist->server->reporthdr = NULL; Iperf_delete( &(exist->server->peer), &clients ); - } else if ( rc > (int) ( sizeof( UDP_datagram ) - + sizeof( server_hdr ) ) ) { - UDP_datagram *UDP_Hdr; - server_hdr *hdr; - - UDP_Hdr = (UDP_datagram*) mBuf; - hdr = (server_hdr*) (UDP_Hdr+1); + + } else if ( rc > (int)(sizeof(dgram_record) + sizeof(server_hdr)) ) { + server_hdr *hdr = (server_hdr *) (dgram_hdr + 1); hdr->flags = htonl( 0 ); } sendto( mSettings->mSock, mBuf, mSettings->mBufLen, 0, @@ -601,8 +582,9 @@ void Listener::UDPSingleServer( ) { tempSettings = NULL; if ( !isCompat( mSettings ) && !isMulticast( mSettings ) ) { - Settings_GenerateClientSettings( server, &tempSettings, - hdr ); + client_hdr* hdr = (client_hdr *)(dgram_hdr + 1); + + Settings_GenerateClientSettings(server, &tempSettings, hdr); } diff --git a/src/Locale.c b/src/Locale.c index d7e71f8..b2e8d1f 100644 --- a/src/Locale.c +++ b/src/Locale.c @@ -78,7 +78,8 @@ Client/Server:\n\ -l, --len #[KM] length of buffer to read or write (default 8 KB)\n\ -m, --print_mss print TCP maximum segment size (MTU - TCP/IP header)\n\ -p, --port # server port to listen on/connect to\n\ - -u, --udp use UDP rather than TCP\n\ + -u, --udp use UDP as transport protocol\n\ + -d, --dccp use DCCP as transport protocol\n\ -w, --window #[KM] TCP window size (socket buffer size)\n\ -B, --bind bind to , an interface or multicast address\n\ -C, --compatibility for use with older versions does not sent extra msgs\n\ @@ -92,16 +93,16 @@ Server specific:\n\ -D, --daemon run the server as a daemon\n\ \n\ Client specific:\n\ - -b, --bandwidth #[KM] for UDP, bandwidth to send at in bits/sec\n\ + -b, --bandwidth #[KM] for UDP/DCCP, bandwidth to send at in bits/sec\n\ (default 1 Mbit/sec, implies -u)\n\ -c, --client run in client mode, connecting to \n\ - -d, --dualtest Do a bidirectional test simultaneously\n\ - -n, --num #[KM] number of bytes to transmit (instead of -t)\n\ + -2, --dualtest Do a bidirectional test simultaneously\n\ -r, --tradeoff Do a bidirectional test individually\n\ -t, --time # time in seconds to transmit for (default 10 secs)\n\ + -n, --num #[KM] number of bytes to transmit (instead of -t)\n\ -F, --fileinput input the data to be transmitted from a file\n\ -I, --stdin input the data to be transmitted from stdin\n\ - -L, --listenport # port to recieve bidirectional tests back on\n\ + -L, --listenport # port to receive bidirectional tests back on\n\ -P, --parallel # number of parallel client threads to run\n\ -T, --ttl # time-to-live, for multicast (default 1)\n\ \n\ @@ -115,7 +116,7 @@ The TCP window size option can be set by the environment variable\n\ TCP_WINDOW_SIZE. Most other options can be set by an environment variable\n\ IPERF_, such as IPERF_BANDWIDTH.\n\ \n\ -Report bugs to \n"; +Report bugs to "; // include a description of the threading in the version #if defined( HAVE_POSIX_THREAD ) @@ -125,7 +126,7 @@ Report bugs to \n"; #endif const char version[] = -"iperf version " IPERF_VERSION " (" IPERF_VERSION_DATE ") " IPERF_THREADS "\n"; +"iperf version " IPERF_VERSION " (" IPERF_VERSION_DATE ") " IPERF_THREADS; /* ------------------------------------------------------------------- * settings @@ -158,8 +159,8 @@ const char server_datagram_size[] = const char tcp_window_size[] = "TCP window size"; -const char udp_buffer_size[] = -"UDP buffer size"; +const char dgram_buffer_size[] = +"datagram buffer size"; const char window_default[] = "(default)"; @@ -191,10 +192,10 @@ const char report_bw_jitter_loss_header[] = Datagrams\n"; const char report_bw_jitter_loss_format[] = -"[%3d] %4.1f-%4.1f sec %ss %ss/sec %5.3f ms %4d/%5d (%.2g%%)\n"; +"[%3d] %4.1f-%4.1f sec %ss %ss/sec %6.3f ms %4d/%5d (%.2g%%)\n"; const char report_sum_bw_jitter_loss_format[] = -"[SUM] %4.1f-%4.1f sec %ss %ss/sec %5.3f ms %4d/%5d (%.2g%%)\n"; +"[SUM] %4.1f-%4.1f sec %ss %ss/sec %6.3f ms %4d/%5d (%.2g%%)\n"; const char report_outoforder[] = "[%3d] %4.1f-%4.1f sec %d datagrams received out-of-order\n"; @@ -206,7 +207,7 @@ const char report_peer[] = "[%3d] local %s port %u connected with %s port %u\n"; const char report_mss_unsupported[] = -"[%3d] MSS and MTU size unknown (TCP_MAXSEG not supported by OS?)\n"; +"[%3d] MSS and MTU size unknown (socket option not supported by OS?)\n"; const char report_mss[] = "[%3d] MSS size %d bytes (MTU %d bytes, %s)\n"; @@ -251,10 +252,6 @@ const char reportCSV_bw_jitter_loss_format[] = const char warn_window_requested[] = " (WARNING: requested %s)"; -const char warn_window_small[] = "\ -WARNING: TCP window size set to %d bytes. A small window size\n\ -will give poor performance. See the Iperf documentation.\n"; - const char warn_delay_large[] = "WARNING: delay too large, reducing from %.1f to 1.0 seconds.\n"; @@ -289,14 +286,11 @@ const char warn_invalid_client_option[] = const char warn_invalid_compatibility_option[] = "WARNING: option -%c is not valid in compatibility mode\n"; -const char warn_implied_udp[] = -"WARNING: option -%c implies udp testing\n"; - const char warn_implied_compatibility[] = "WARNING: option -%c has implied compatibility mode\n"; const char warn_buffer_too_small[] = -"WARNING: the UDP buffer was increased to %d for proper operation\n"; +"WARNING: the buffer was increased to %d for proper operation\n"; const char warn_invalid_single_threaded[] = "WARNING: option -%c is not valid in single threaded versions\n"; diff --git a/src/PerfSocket.cpp b/src/PerfSocket.cpp index c25a0b6..d149d59 100644 --- a/src/PerfSocket.cpp +++ b/src/PerfSocket.cpp @@ -68,8 +68,6 @@ * * * ------------------------------------------------------------------- */ - - #define HEADERS() #include "headers.h" @@ -78,66 +76,94 @@ #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. * ------------------------------------------------------------------- */ - -void SetSocketOptions( thread_Settings *inSettings ) { - // set the TCP window size (socket buffer sizes) - // also the UDP buffer size - // must occur before call to accept() for large window sizes - setsock_tcp_windowsize( inSettings->mSock, inSettings->mTCPWin, - (inSettings->mThreadMode == kMode_Client ? 1 : 0) ); +void SetSocketOptions( thread_Settings *inSettings ) +{ + int rc, val; + Socklen_t len = sizeof(int); // check if we're sending multicast, and set TTL if ( isMulticast( inSettings ) && ( inSettings->mTTL > 0 ) ) { - int val = inSettings->mTTL; + val = inSettings->mTTL; #ifdef HAVE_MULTICAST if ( !SockAddr_isIPv6( &inSettings->local ) ) { - int rc = setsockopt( inSettings->mSock, IPPROTO_IP, IP_MULTICAST_TTL, - (const void*) &val, (Socklen_t) sizeof(val)); + rc = setsockopt( inSettings->mSock, IPPROTO_IP, IP_MULTICAST_TTL, + &val, len); WARN_errno( rc == SOCKET_ERROR, "multicast ttl" ); } #ifdef HAVE_IPV6_MULTICAST else { - int rc = setsockopt( inSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, - (const void*) &val, (Socklen_t) sizeof(val)); + rc = setsockopt( inSettings->mSock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, + &val, len); WARN_errno( rc == SOCKET_ERROR, "multicast ttl" ); } -#endif -#endif +#endif /* IPV6_MULTICAST */ +#endif /* MULTICAST */ } -#ifdef IP_TOS - // set IP TOS (type-of-service) field +#ifdef IP_TOS if ( inSettings->mTOS > 0 ) { - int tos = inSettings->mTOS; - Socklen_t len = sizeof(tos); - int rc = setsockopt( inSettings->mSock, IPPROTO_IP, IP_TOS, - (char*) &tos, len ); + val = inSettings->mTOS; + rc = setsockopt( inSettings->mSock, IPPROTO_IP, IP_TOS, &val, len ); WARN_errno( rc == SOCKET_ERROR, "setsockopt IP_TOS" ); } #endif - if ( !isUDP( inSettings ) ) { - // set the TCP maximum segment size + + // TCP-specific options + if ( inSettings->mProtocol == kProto_TCP ) { + + // set the TCP window size (socket buffer sizes) + // must occur before call to accept() for large window sizes + setsock_tcp_windowsize(inSettings->mSock, inSettings->mWinSize, + inSettings->mThreadMode == kMode_Client); setsock_tcp_mss( inSettings->mSock, inSettings->mMSS ); #ifdef TCP_NODELAY - - // set TCP nodelay option if ( isNoDelay( inSettings ) ) { - int nodelay = 1; - Socklen_t len = sizeof(nodelay); - int rc = setsockopt( inSettings->mSock, IPPROTO_TCP, TCP_NODELAY, - (char*) &nodelay, len ); + val = 1; + rc = setsockopt( inSettings->mSock, IPPROTO_TCP, TCP_NODELAY, + &val, len ); WARN_errno( rc == SOCKET_ERROR, "setsockopt TCP_NODELAY" ); } #endif + } else { + rc = set_buffer_sock_size(inSettings->mSock, inSettings->mWinSize, + inSettings->mThreadMode == kMode_Client); + WARN_errno( rc < 0 , "setsockopt for buffer size" ); + } + // DCCP-specific options + if ( inSettings->mProtocol == kProto_DCCP ) { + /* + * We use the service code SC:PERF (0x50455246) from + * draft-fairhurst-dccp-serv-codes to identify this service. + */ + val = htonl(0x50455246); /* ALWAYS use htonl */ + rc = setsockopt( inSettings->mSock, SOL_DCCP, DCCP_SOCKOPT_SERVICE, + &val, len ); + WARN_errno( rc == SOCKET_ERROR, "setsockopt DCCP_SOCKOPT_SERVICE" ); } } -// end SetSocketOptions diff --git a/src/ReportDefault.c b/src/ReportDefault.c index 7944e03..a3ecc97 100644 --- a/src/ReportDefault.c +++ b/src/ReportDefault.c @@ -75,12 +75,12 @@ void reporter_printstats( Transfer_Info *stats ) { stats->mFormat); if ( stats->mUDP != (char)kMode_Server ) { - // TCP Reporting + // TCP-like Reporting printf( report_bw_format, stats->transferID, stats->startTime, stats->endTime, buffer, &buffer[sizeof(buffer)/2] ); } else { - // UDP Reporting + // UDP-like Reporting printf( report_bw_jitter_loss_format, stats->transferID, stats->startTime, stats->endTime, buffer, &buffer[sizeof(buffer)/2], @@ -110,12 +110,12 @@ void reporter_multistats( Transfer_Info *stats ) { stats->mFormat); if ( stats->mUDP != (char)kMode_Server ) { - // TCP Reporting + // TCP-like Reporting printf( report_sum_bw_format, stats->startTime, stats->endTime, buffer, &buffer[sizeof(buffer)/2] ); } else { - // UDP Reporting + // UDP-like Reporting printf( report_sum_bw_jitter_loss_format, stats->startTime, stats->endTime, buffer, &buffer[sizeof(buffer)/2], @@ -146,19 +146,19 @@ void reporter_serverstats( Connection_Info *nused, Transfer_Info *stats ) { void reporter_reportsettings( ReporterData *data ) { int win, win_requested; - win = getsock_tcp_windowsize( data->info.transferID, - (data->mThreadMode == kMode_Listener ? 0 : 1) ); - win_requested = data->mTCPWin; + win = get_buffer_sock_size(data->info.transferID, + data->mThreadMode != kMode_Listener); + win_requested = data->mWinSize; printf( seperator_line ); if ( data->mThreadMode == kMode_Listener ) { printf( server_port, - (isUDP( data ) ? "UDP" : "TCP"), + protoName(data->mProtocol), data->mPort ); } else { printf( client_port, data->mHost, - (isUDP( data ) ? "UDP" : "TCP"), + protoName(data->mProtocol), data->mPort ); } if ( data->mLocalhost != NULL ) { @@ -168,18 +168,23 @@ void reporter_reportsettings( ReporterData *data ) { } } - if ( isUDP( data ) ) { + if ( isPacketOriented( data ) ) { printf( (data->mThreadMode == kMode_Listener ? - server_datagram_size : client_datagram_size), + server_datagram_size : client_datagram_size), data->mBufLen ); if ( SockAddr_isMulticast( &data->connection.peer ) ) { printf( multicast_ttl, data->info.mTTL); } - } + } else if (data->mProtocol == kProto_DCCP) + printf("NOTE: running in bytestream-mode (maximum speed)\n"); + byte_snprintf( buffer, sizeof(buffer), win, toupper( data->info.mFormat)); - printf( "%s: %s", (isUDP( data ) ? - udp_buffer_size : tcp_window_size), buffer ); + + if (data->mProtocol == kProto_TCP) + printf( "%s: %s", tcp_window_size, buffer); + else + printf( "%s %s: %s", protoName(data->mProtocol), dgram_buffer_size, buffer); if ( win_requested == 0 ) { printf( " %s", window_default ); diff --git a/src/Reporter.c b/src/Reporter.c index e34763e..db9418e 100644 --- a/src/Reporter.c +++ b/src/Reporter.c @@ -156,13 +156,14 @@ MultiHeader* InitMulti( thread_Settings *agent, int inID ) { data->mLocalhost = agent->mLocalhost; data->mBufLen = agent->mBufLen; data->mMSS = agent->mMSS; - data->mTCPWin = agent->mTCPWin; + data->mWinSize = agent->mWinSize; data->flags = agent->flags; + data->mProtocol = agent->mProtocol; data->mThreadMode = agent->mThreadMode; data->mode = agent->mReportMode; data->info.mFormat = agent->mFormat; data->info.mTTL = agent->mTTL; - if ( isUDP( agent ) ) { + if ( isPacketOriented( agent ) ) { multihdr->report->info.mUDP = (char)agent->mThreadMode; } if ( isConnectionReport( agent ) ) { @@ -237,13 +238,14 @@ ReportHeader* InitReport( thread_Settings *agent ) { data->mLocalhost = agent->mLocalhost; data->mBufLen = agent->mBufLen; data->mMSS = agent->mMSS; - data->mTCPWin = agent->mTCPWin; + data->mWinSize = agent->mWinSize; data->flags = agent->flags; + data->mProtocol = agent->mProtocol; data->mThreadMode = agent->mThreadMode; data->mode = agent->mReportMode; data->info.mFormat = agent->mFormat; data->info.mTTL = agent->mTTL; - if ( isUDP( agent ) ) { + if ( isPacketOriented( agent ) ) { reporthdr->report.info.mUDP = (char)agent->mThreadMode; } } else { @@ -371,7 +373,6 @@ void ReportPacket( ReportHeader* agent, ReportStruct *packet ) { */ void CloseReport( ReportHeader *agent, ReportStruct *packet ) { if ( agent != NULL) { - /* * Using PacketID of -1 ends reporting */ @@ -442,8 +443,9 @@ void ReportSettings( thread_Settings *agent ) { data->type = SETTINGS_REPORT; data->mBufLen = agent->mBufLen; data->mMSS = agent->mMSS; - data->mTCPWin = agent->mTCPWin; + data->mWinSize = agent->mWinSize; data->flags = agent->flags; + data->mProtocol = agent->mProtocol; data->mThreadMode = agent->mThreadMode; data->mPort = agent->mPort; data->info.mFormat = agent->mFormat; @@ -672,24 +674,26 @@ int reporter_handle_packet( ReportHeader *reporthdr ) { Transfer_Info *stats = &reporthdr->report.info; int finished = 0; + // update received amount and time + data->TotalLen += packet->packetLen; + data->packetTime = packet->packetTime; data->cntDatagrams++; - // If this is the last packet set the endTime - if ( packet->packetID < 0 ) { - data->packetTime = packet->packetTime; + + if (packet->packetID < 0) { + // This is the last packet (FIN) finished = 1; - if ( reporthdr->report.mThreadMode != kMode_Client ) { - data->TotalLen += packet->packetLen; + if (isConnectionLess(&reporthdr->report)) { + // connectionless protocols don't count the last payload + if (reporthdr->report.mThreadMode == kMode_Client) + data->TotalLen -= packet->packetLen; + } else { + // connection-oriented protocols dont't count the FIN + data->cntDatagrams--; } - } else { - // update recieved amount and time - data->packetTime = packet->packetTime; - reporter_condprintstats( &reporthdr->report, reporthdr->multireport, finished ); - data->TotalLen += packet->packetLen; - if ( packet->packetID != 0 ) { - // UDP packet - double transit; - double deltaTransit; - + } else if ( packet->packetID != 0 ) { + // UDP or DCCP packet + double transit, deltaTransit; + // from RFC 1889, Real Time Protocol (RTP) // J = J + ( | D(i-1,i) | - J ) / 16 transit = TimeDifference( packet->packetTime, packet->sentTime ); @@ -701,7 +705,7 @@ int reporter_handle_packet( ReportHeader *reporthdr ) { stats->jitter += (deltaTransit - stats->jitter) / (16.0); } data->lastTransit = transit; - + // packet loss occured if the datagram numbers aren't sequential if ( packet->packetID != data->PacketID + 1 ) { if ( packet->packetID < data->PacketID + 1 ) { @@ -714,9 +718,7 @@ int reporter_handle_packet( ReportHeader *reporthdr ) { if ( packet->packetID > data->PacketID ) { data->PacketID = packet->packetID; } - } } - // Print a report if appropriate return reporter_condprintstats( &reporthdr->report, reporthdr->multireport, finished ); } @@ -791,11 +793,14 @@ int reporter_condprintstats( ReporterData *stats, MultiHeader *multireport, int if ( stats->info.cntError > stats->info.cntOutofOrder ) { stats->info.cntError -= stats->info.cntOutofOrder; } - stats->info.cntDatagrams = (isUDP(stats) ? stats->PacketID : stats->cntDatagrams); - stats->info.TotalLen = stats->TotalLen; + if (isConnectionLess(stats)) + stats->info.cntDatagrams = stats->PacketID; + else + stats->info.cntDatagrams = stats->cntDatagrams; + stats->info.TotalLen = stats->TotalLen; stats->info.startTime = 0; - stats->info.endTime = TimeDifference( stats->packetTime, stats->startTime ); - stats->info.free = 1; + stats->info.endTime = TimeDifference( stats->packetTime, stats->startTime ); + stats->info.free = 1; reporter_print( stats, TRANSFER_REPORT, force ); if ( isMultipleReport(stats) ) { reporter_handle_multiple_reports( multireport, &stats->info, force ); @@ -813,9 +818,13 @@ int reporter_condprintstats( ReporterData *stats, MultiHeader *multireport, int stats->info.cntError -= stats->info.cntOutofOrder; } stats->lastError = stats->cntError; - stats->info.cntDatagrams = (isUDP( stats ) ? stats->PacketID - stats->lastDatagrams : - stats->cntDatagrams - stats->lastDatagrams); - stats->lastDatagrams = (isUDP( stats ) ? stats->PacketID : stats->cntDatagrams); + if (isConnectionLess(stats)) { + stats->info.cntDatagrams = stats->PacketID - stats->lastDatagrams; + stats->lastDatagrams = stats->PacketID; + } else { + stats->info.cntDatagrams = stats->cntDatagrams - stats->lastDatagrams; + stats->lastDatagrams = stats->cntDatagrams; + } stats->info.TotalLen = stats->TotalLen - stats->lastTotal; stats->lastTotal = stats->TotalLen; stats->info.startTime = stats->info.endTime; @@ -838,7 +847,7 @@ int reporter_print( ReporterData *stats, int type, int end ) { switch ( type ) { case TRANSFER_REPORT: statistics_reports[stats->mode]( &stats->info ); - if ( end != 0 && isPrintMSS( stats ) && !isUDP( stats ) ) { + if ( end != 0 && isPrintMSS( stats ) && !isConnectionLess( stats ) ) { PrintMSS( stats ); } break; @@ -865,6 +874,8 @@ int reporter_print( ReporterData *stats, int type, int end ) { /* ------------------------------------------------------------------- * Report the MSS and MTU, given the MSS (or a guess thereof) + * This works for connection-oriented protocols only: it expects + * the protocol to be either TCP or DCCP and will give error otherwise * ------------------------------------------------------------------- */ // compare the MSS against the (MTU - 40) to (MTU - 80) bytes. @@ -872,8 +883,11 @@ int reporter_print( ReporterData *stats, int type, int end ) { #define checkMSS_MTU( inMSS, inMTU ) (inMTU-40) >= inMSS && inMSS >= (inMTU-80) -void PrintMSS( ReporterData *stats ) { - int inMSS = getsock_tcp_mss( stats->info.transferID ); +void PrintMSS( ReporterData *stats ) +{ + int inMSS = stats->mProtocol == kProto_TCP + ? getsock_tcp_mss( stats->info.transferID ) + : getsock_dccp_mps( stats->info.transferID ); if ( inMSS <= 0 ) { printf( report_mss_unsupported, stats->info.transferID ); diff --git a/src/Server.cpp b/src/Server.cpp index da7ef14..d7cd52a 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -92,13 +92,13 @@ void Server::Sig_Int( int inSigno ) { } /* ------------------------------------------------------------------- - * Receieve data from the (connected) TCP/UDP socket. + * Receive data from the (connected) socket. * Sends termination flag several times at the end. * Does not close the socket. * ------------------------------------------------------------------- */ void Server::Run( void ) { long currLen; - struct UDP_datagram* mBuf_UDP = (struct UDP_datagram*) mBuf; + struct dgram_record* dgram_rec = (struct dgram_record*) mBuf; ReportStruct *reportstruct = NULL; @@ -110,11 +110,11 @@ void Server::Run( void ) { // perform read currLen = recv( mSettings->mSock, mBuf, mSettings->mBufLen, 0 ); - if ( isUDP( mSettings ) ) { + if ( isPacketOriented( mSettings ) ) { // read the datagram ID and sentTime out of the buffer - reportstruct->packetID = ntohl( mBuf_UDP->id ); - reportstruct->sentTime.tv_sec = ntohl( mBuf_UDP->tv_sec ); - reportstruct->sentTime.tv_usec = ntohl( mBuf_UDP->tv_usec ); + reportstruct->packetID = ntohl( dgram_rec->id ); + reportstruct->sentTime.tv_sec = ntohl( dgram_rec->tv_sec ); + reportstruct->sentTime.tv_usec = ntohl( dgram_rec->tv_usec ); } reportstruct->packetLen = currLen; @@ -124,6 +124,9 @@ void Server::Run( void ) { // the datagram ID should be correct, just negated if ( reportstruct->packetID < 0 ) { reportstruct->packetID = -reportstruct->packetID; + // Don't count the FIN message in connection-oriented protocols. + if (!isConnectionLess(mSettings)) + break; currLen = -1; } ReportPacket( mSettings->reporthdr, reportstruct ); @@ -133,11 +136,10 @@ void Server::Run( void ) { gettimeofday( &(reportstruct->packetTime), NULL ); CloseReport( mSettings->reporthdr, reportstruct ); + // send back an acknowledgement of the terminating datagram // send a acknowledgement back only if we're NOT receiving multicast - if ( isUDP( mSettings ) && !isMulticast( mSettings ) ) { - // send back an acknowledgement of the terminating datagram - write_UDP_AckFIN( ); - } + if ( isPacketOriented( mSettings ) && !isMulticast( mSettings ) ) + write_dgram_AckFin( ); } else { FAIL(1, "Out of memory! Closing server thread\n", mSettings); } @@ -158,7 +160,7 @@ void Server::Run( void ) { * termination datagrams, so re-transmit our AckFIN. * ------------------------------------------------------------------- */ -void Server::write_UDP_AckFIN( ) { +void Server::write_dgram_AckFin( ) { int rc; @@ -171,15 +173,15 @@ void Server::write_UDP_AckFIN( ) { while ( count < 10 ) { count++; - UDP_datagram *UDP_Hdr; + dgram_record *dgram_rec; server_hdr *hdr; - UDP_Hdr = (UDP_datagram*) mBuf; + dgram_rec = (dgram_record*) mBuf; - if ( mSettings->mBufLen > (int) ( sizeof( UDP_datagram ) + if ( mSettings->mBufLen > (int) ( sizeof( dgram_record ) + sizeof( server_hdr ) ) ) { Transfer_Info *stats = GetReport( mSettings->reporthdr ); - hdr = (server_hdr*) (UDP_Hdr+1); + hdr = (server_hdr*) (dgram_rec+1); hdr->flags = htonl( HEADER_VERSION1 ); hdr->total_len1 = htonl( (long) (stats->TotalLen >> 32) ); @@ -224,5 +226,3 @@ void Server::write_UDP_AckFIN( ) { fprintf( stderr, warn_ack_failed, mSettings->mSock, count ); } -// end write_UDP_AckFIN - diff --git a/src/Settings.cpp b/src/Settings.cpp index 2b71693..4cde826 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -84,9 +84,10 @@ void Settings_Interpret( char option, const char *optarg, thread_Settings *mExtS const struct option long_options[] = { {"singleclient", no_argument, NULL, '1'}, +{"dualtest", no_argument, NULL, '2'}, {"bandwidth", required_argument, NULL, 'b'}, {"client", required_argument, NULL, 'c'}, -{"dualtest", no_argument, NULL, 'd'}, +{"dccp", no_argument, NULL, 'd'}, {"format", required_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, {"interval", required_argument, NULL, 'i'}, @@ -128,9 +129,10 @@ const struct option long_options[] = const struct option env_options[] = { {"IPERF_SINGLECLIENT", no_argument, NULL, '1'}, +{"IPERF_DUALTEST", no_argument, NULL, '2'}, {"IPERF_BANDWIDTH", required_argument, NULL, 'b'}, {"IPERF_CLIENT", required_argument, NULL, 'c'}, -{"IPERF_DUALTEST", no_argument, NULL, 'd'}, +{"IPERF_DCCP", no_argument, NULL, 'd'}, {"IPERF_FORMAT", required_argument, NULL, 'f'}, // skip help {"IPERF_INTERVAL", required_argument, NULL, 'i'}, @@ -167,19 +169,22 @@ const struct option env_options[] = #define SHORT_OPTIONS() -const char short_options[] = "1b:c:df:hi:l:mn:o:p:rst:uvw:x:y:B:CDF:IL:M:NP:RS:T:UVW"; +const char short_options[] = + "12b::c:df:hi:l:mn:o:p:rst:uvw:x:y:B:CDF:IL:M:NP:RS:T:UVW"; /* ------------------------------------------------------------------- * defaults * ------------------------------------------------------------------- */ #define DEFAULTS() -const long kDefault_UDPRate = 1024 * 1024; // -u if set, 1 Mbit/sec -const int kDefault_UDPBufLen = 1470; // -u if set, read/write 1470 bytes +const long kDefault_DgramRate = 1024 * 1024; // -u if set, 1 Mbit/sec +const int kDefault_UDPBufLen = 1470; // -u if set, read/write 1470 bytes // 1470 bytes is small enough to be sending one packet per datagram on ethernet // 1450 bytes is small enough to be sending one packet per datagram on ethernet // **** with IPv6 **** +const int kDefault_DCCPBufLen = 1420; // -d +// old DCCPv4: MPS=1424; new DCCPv4: MPS=1440; new DCCPv6: MPS=1420 (above) /* ------------------------------------------------------------------- * Initialize all settings to defaults. @@ -190,42 +195,43 @@ void Settings_Initialize( thread_Settings *main ) { // this memset. Only need to set non-zero values // below. memset( main, 0, sizeof(thread_Settings) ); - main->mSock = INVALID_SOCKET; - main->mReportMode = kReport_Default; + main->mSock = INVALID_SOCKET; + main->mReportMode = kReport_Default; // option, defaults main->flags = FLAG_MODETIME | FLAG_STDOUT; // Default time and stdout - //main->mUDPRate = 0; // -b, ie. TCP mode - //main->mHost = NULL; // -c, none, required for client - main->mMode = kTest_Normal; // -d, mMode == kTest_DualTest + //main->mDgramRate = 0; // -b, ie. TCP mode + main->mProtocol = kProto_TCP; // -u / -d + //main->mHost = NULL; // -c, none, required for client + main->mMode = kTest_Normal; // -2, mMode == kTest_DualTest main->mFormat = 'a'; // -f, adaptive bits // skip help // -h, //main->mBufLenSet = false; // -l, main->mBufLen = 8 * 1024; // -l, 8 Kbyte - //main->mInterval = 0; // -i, ie. no periodic bw reports + //main->mInterval = 0; // -i, ie. no periodic bw reports //main->mPrintMSS = false; // -m, don't print MSS // mAmount is time also // -n, N/A //main->mOutputFileName = NULL; // -o, filename main->mPort = 5001; // -p, ttcp port - // mMode = kTest_Normal; // -r, mMode == kTest_TradeOff + // mMode = kTest_Normal; // -r, mMode == kTest_TradeOff main->mThreadMode = kMode_Unknown; // -s, or -c, none main->mAmount = 1000; // -t, 10 seconds - // mUDPRate > 0 means UDP // -u, N/A, see kDefault_UDPRate + // mDgramRate // -u, N/A, see kDefault_DgramRate // skip version // -v, - //main->mTCPWin = 0; // -w, ie. don't set window + //main->mWinSize = 0; // -w, ie. don't set window // more esoteric options - //main->mLocalhost = NULL; // -B, none + //main->mLocalhost = NULL; // -B, bind address - none //main->mCompat = false; // -C, run in Compatibility mode //main->mDaemon = false; // -D, run as a daemon //main->mFileInput = false; // -F, - //main->mFileName = NULL; // -F, filename + //main->mFileName = NULL; // -F, filename //main->mStdin = false; // -I, default not stdin - //main->mListenPort = 0; // -L, listen port - //main->mMSS = 0; // -M, ie. don't set MSS + //main->mListenPort = 0; // -L, listen port + //main->mMSS = 0; // -M, ie. don't set MSS //main->mNodelay = false; // -N, don't set nodelay - //main->mThreads = 0; // -P, + //main->mThreads = 0; // -P, //main->mRemoveService = false; // -R, - //main->mTOS = 0; // -S, ie. don't set type of service + //main->mTOS = 0; // -S, ie. don't set type of service main->mTTL = 1; // -T, link-local TTL //main->mDomain = kMode_IPv4; // -V, //main->mSuggestWin = false; // -W, Suggest the window size. @@ -314,25 +320,34 @@ void Settings_Interpret( char option, const char *optarg, thread_Settings *mExtS case '1': // Single Client setSingleClient( mExtSettings ); break; - case 'b': // UDP bandwidth - if ( !isUDP( mExtSettings ) ) { - fprintf( stderr, warn_implied_udp, option ); - } + case '2': // Dual-test Mode if ( mExtSettings->mThreadMode != kMode_Client ) { fprintf( stderr, warn_invalid_server_option, option ); break; } + if ( isCompat( mExtSettings ) ) { + fprintf( stderr, warn_invalid_compatibility_option, option ); + } +#ifdef HAVE_THREAD + mExtSettings->mMode = kTest_DualTest; +#else + fprintf( stderr, warn_invalid_single_threaded, option ); + mExtSettings->mMode = kTest_TradeOff; +#endif + break; - Settings_GetLowerCaseArg(optarg,outarg); - mExtSettings->mUDPRate = byte_atoi(outarg); - setUDP( mExtSettings ); + case 'b': + // This sets packet-oriented mode. The optional + // argument sets datagram bandwidth (as before). + // If not given, a default bandwith is used. + setPacketOriented(mExtSettings); - // if -l has already been processed, mBufLenSet is true - // so don't overwrite that value. - if ( !isBuflenSet( mExtSettings ) ) { - mExtSettings->mBufLen = kDefault_UDPBufLen; - } + if (optarg) { + Settings_GetLowerCaseArg(optarg, outarg); + mExtSettings->mDgramRate = byte_atoi(outarg); + } else + mExtSettings->mDgramRate = kDefault_DgramRate; break; case 'c': // client mode w/ server host to connect to @@ -352,20 +367,14 @@ void Settings_Interpret( char option, const char *optarg, thread_Settings *mExtS } break; - case 'd': // Dual-test Mode - if ( mExtSettings->mThreadMode != kMode_Client ) { - fprintf( stderr, warn_invalid_server_option, option ); - break; - } - if ( isCompat( mExtSettings ) ) { - fprintf( stderr, warn_invalid_compatibility_option, option ); - } -#ifdef HAVE_THREAD - mExtSettings->mMode = kTest_DualTest; -#else - fprintf( stderr, warn_invalid_single_threaded, option ); - mExtSettings->mMode = kTest_TradeOff; -#endif + case 'd': // DCCP as transport + mExtSettings->mProtocol = kProto_DCCP; + + // if -l has already been processed, mBufLenSet is true + // so don't overwrite that value. + if ( !isBuflenSet( mExtSettings ) ) + mExtSettings->mBufLen = kDefault_DCCPBufLen; + break; case 'f': // format to print in @@ -373,9 +382,7 @@ void Settings_Interpret( char option, const char *optarg, thread_Settings *mExtS break; case 'h': // print help and exit - fprintf( stderr, usage_long ); - exit(1); - break; + die(usage_long); case 'i': // specify interval between periodic bw reports mExtSettings->mInterval = atof( optarg ); @@ -389,19 +396,20 @@ void Settings_Interpret( char option, const char *optarg, thread_Settings *mExtS Settings_GetUpperCaseArg(optarg,outarg); mExtSettings->mBufLen = byte_atoi( outarg ); setBuflenSet( mExtSettings ); - if ( !isUDP( mExtSettings ) ) { + + if ( !isPacketOriented( mExtSettings ) ) { if ( mExtSettings->mBufLen < (int) sizeof( client_hdr ) && !isCompat( mExtSettings ) ) { setCompat( mExtSettings ); fprintf( stderr, warn_implied_compatibility, option ); } } else { - if ( mExtSettings->mBufLen < (int) sizeof( UDP_datagram ) ) { - mExtSettings->mBufLen = sizeof( UDP_datagram ); + if ( mExtSettings->mBufLen < (int) sizeof( dgram_record ) ) { + mExtSettings->mBufLen = sizeof( dgram_record ); fprintf( stderr, warn_buffer_too_small, mExtSettings->mBufLen ); } if ( !isCompat( mExtSettings ) && - mExtSettings->mBufLen < (int) ( sizeof( UDP_datagram ) + mExtSettings->mBufLen < (int) ( sizeof( dgram_record ) + sizeof( client_hdr ) ) ) { setCompat( mExtSettings ); fprintf( stderr, warn_implied_compatibility, option ); @@ -459,18 +467,19 @@ void Settings_Interpret( char option, const char *optarg, thread_Settings *mExtS break; case 'u': // UDP instead of TCP + mExtSettings->mProtocol = kProto_UDP; + + setPacketOriented(mExtSettings); // if -b has already been processed, UDP rate will // already be non-zero, so don't overwrite that value - if ( !isUDP( mExtSettings ) ) { - setUDP( mExtSettings ); - mExtSettings->mUDPRate = kDefault_UDPRate; - } + if ( mExtSettings->mDgramRate == 0 ) + mExtSettings->mDgramRate = kDefault_DgramRate; // if -l has already been processed, mBufLenSet is true // so don't overwrite that value. if ( !isBuflenSet( mExtSettings ) ) { mExtSettings->mBufLen = kDefault_UDPBufLen; - } else if ( mExtSettings->mBufLen < (int) ( sizeof( UDP_datagram ) + } else if ( mExtSettings->mBufLen < (int) ( sizeof( dgram_record ) + sizeof( client_hdr ) ) && !isCompat( mExtSettings ) ) { setCompat( mExtSettings ); @@ -479,17 +488,11 @@ void Settings_Interpret( char option, const char *optarg, thread_Settings *mExtS break; case 'v': // print version and exit - fprintf( stderr, version ); - exit(1); - break; + die(version); - case 'w': // TCP window size (socket buffer size) + case 'w': // TCP window size or socket send-buffer size (UDP/DCCP) Settings_GetUpperCaseArg(optarg,outarg); - mExtSettings->mTCPWin = byte_atoi(outarg); - - if ( mExtSettings->mTCPWin < 2048 ) { - fprintf( stderr, warn_window_small, mExtSettings->mTCPWin ); - } + mExtSettings->mWinSize = byte_atoi(outarg); break; case 'x': // Limit Reports @@ -740,17 +743,17 @@ void Settings_GenerateClientSettings( thread_Settings *server, *client = new thread_Settings; memcpy(*client, server, sizeof( thread_Settings )); setCompat( (*client) ); - (*client)->mTID = thread_zeroid(); + (*client)->mTID = thread_zeroid(); (*client)->mPort = (unsigned short) ntohl(hdr->mPort); (*client)->mThreads = ntohl(hdr->numThreads); if ( hdr->bufferlen != 0 ) { (*client)->mBufLen = ntohl(hdr->bufferlen); } if ( hdr->mWinBand != 0 ) { - if ( isUDP( server ) ) { - (*client)->mUDPRate = ntohl(hdr->mWinBand); + if ( isPacketOriented( server ) ) { + (*client)->mDgramRate = ntohl(hdr->mWinBand); } else { - (*client)->mTCPWin = ntohl(hdr->mWinBand); + (*client)->mWinSize = ntohl(hdr->mWinBand); } } (*client)->mAmount = ntohl(hdr->mAmount); @@ -803,10 +806,10 @@ void Settings_GenerateClientHdr( thread_Settings *client, client_hdr *hdr ) { } else { hdr->bufferlen = 0; } - if ( isUDP( client ) ) { - hdr->mWinBand = htonl(client->mUDPRate); + if ( isPacketOriented( client ) ) { + hdr->mWinBand = htonl(client->mDgramRate); } else { - hdr->mWinBand = htonl(client->mTCPWin); + hdr->mWinBand = htonl(client->mWinSize); } if ( client->mListenPort != 0 ) { hdr->mPort = htonl(client->mListenPort); diff --git a/src/main.cpp b/src/main.cpp index 167aed5..a69fd0e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -147,6 +147,20 @@ int main( int argc, char **argv ) { // read settings from command-line parameters Settings_ParseCommandLine( argc, argv, ext_gSettings ); + if (isPacketOriented(ext_gSettings) && + !(ext_gSettings->mProtocol == kProto_UDP || + ext_gSettings->mProtocol == kProto_DCCP )) + die("Can't use packet-oriented mode with these settings."); + + if (isSingleUDP(ext_gSettings) && ext_gSettings->mProtocol != kProto_UDP) { + fprintf(stderr, "WARNING: option -U applies to UDP only, ignored!\n"); + unsetSingleUDP(ext_gSettings); + } + + if (!isModeTime(ext_gSettings) && + (isConnectionLess(ext_gSettings) || isPacketOriented(ext_gSettings))) + die("Amount-oriented (-n) works only in non-packet-oriented mode."); + // Check for either having specified client or server if ( ext_gSettings->mThreadMode == kMode_Client || ext_gSettings->mThreadMode == kMode_Listener ) { diff --git a/src/sockets.c b/src/sockets.c index 72eddd8..2787c26 100644 --- a/src/sockets.c +++ b/src/sockets.c @@ -118,6 +118,22 @@ int getsock_tcp_mss( int inSock ) { return theMSS; } /* end getsock_tcp_mss */ +/* ------------------------------------------------------------------- + * returns the DCCP maximum datagram (payload) size + * ------------------------------------------------------------------- */ +int getsock_dccp_mps(int inSock) +{ + int rc, theMPS = 0; + Socklen_t len = sizeof(theMPS); + + assert(inSock >= 0); + + rc = getsockopt(inSock, SOL_DCCP, DCCP_SOCKOPT_GET_CUR_MPS, &theMPS, &len); + WARN_errno(rc == SOCKET_ERROR, "getsockopt DCCP_SOCKOPT_GET_CUR_MPS"); + + return theMPS; +} + /* ------------------------------------------------------------------- * Attempts to reads n bytes from a socket. * Returns number actually read, or -1 on error. diff --git a/src/stdio.c b/src/stdio.c index 96782f7..c6bc003 100644 --- a/src/stdio.c +++ b/src/stdio.c @@ -238,7 +238,7 @@ void byte_snprintf( char* outString, int inLen, } else if ( inNum < 99.95 ) { /* 99.95 would be rounded to 100 */ format = "%4.1f %s"; /* ##.# */ } else if ( inNum < 999.5 ) { /* 999.5 would be rounded to 1000 */ - format = " %4.0f %s"; /* ### */ + format = "%4.0f %s"; /* ### */ } else { /* 1000-1024 fits in 4 places * If not using Adaptive sizes then * this code will not control spaces*/ diff --git a/src/tcp_window_size.c b/src/tcp_window_size.c index bdd28c5..8bc1072 100644 --- a/src/tcp_window_size.c +++ b/src/tcp_window_size.c @@ -58,6 +58,25 @@ extern "C" { #endif +/* ------------------------------------------------------------------- + * Set the socket buffer size in bytes (returns -1 on error, 0 otherwise). + * ------------------------------------------------------------------- */ +int set_buffer_sock_size(int inSock, int inWinSize , bool inSend) +{ + // note: results are verified after connect() or listen(), + // since some OS's don't show the corrected value until then. +#ifdef SO_SNDBUF + if (inWinSize > 0) { + return setsockopt(inSock, SOL_SOCKET, + inSend ? SO_SNDBUF: SO_RCVBUF, + &inWinSize, sizeof(inWinSize)); + } +#else + fprintf(stderr, "%s: no support for SO_SNDBUF\n", __FUNCTION__); +#endif + return 0; +} + /* ------------------------------------------------------------------- * If inTCPWin > 0, set the TCP window size (via the socket buffer * sizes) for inSock. Otherwise leave it as the system default. @@ -69,18 +88,16 @@ extern "C" { * This now works on AIX, by enabling RFC1323. * returns -1 on error, 0 on no error. * ------------------------------------------------------------------- */ - -int setsock_tcp_windowsize( int inSock, int inTCPWin, int inSend ) { -#ifdef SO_SNDBUF - int rc; - int newTCPWin; - +int setsock_tcp_windowsize(int inSock, int inTCPWin, int inSend) +{ assert( inSock >= 0 ); - if ( inTCPWin > 0 ) { - + if (inTCPWin > 0) { + if (inTCPWin < 2048) + fprintf(stderr, "WARNING: A TCP window size of %d bytes is " + "considered small and will give poor performance. " + "See the Iperf documentation.\n", inTCPWin); #ifdef TCP_WINSHIFT - /* UNICOS requires setting the winshift explicitly */ if ( inTCPWin > 65535 ) { int winShift = 0; @@ -116,62 +133,33 @@ int setsock_tcp_windowsize( int inSock, int inTCPWin, int inSend ) { } } #endif /* TCP_RFC1323 */ - - if ( !inSend ) { - /* receive buffer -- set - * note: results are verified after connect() or listen(), - * since some OS's don't show the corrected value until then. */ - newTCPWin = inTCPWin; - rc = setsockopt( inSock, SOL_SOCKET, SO_RCVBUF, - (char*) &newTCPWin, sizeof( newTCPWin )); - } else { - /* send buffer -- set - * note: results are verified after connect() or listen(), - * since some OS's don't show the corrected value until then. */ - newTCPWin = inTCPWin; - rc = setsockopt( inSock, SOL_SOCKET, SO_SNDBUF, - (char*) &newTCPWin, sizeof( newTCPWin )); - } - if ( rc < 0 ) { - return rc; - } } -#endif /* SO_SNDBUF */ - - return 0; -} /* end setsock_tcp_windowsize */ + return set_buffer_sock_size(inSock, inTCPWin, inSend); +} /* ------------------------------------------------------------------- - * returns the TCP window size (on the sending buffer, SO_SNDBUF), + * returns the send/receive socket buffer size in bytes * or -1 on error. * ------------------------------------------------------------------- */ - -int getsock_tcp_windowsize( int inSock, int inSend ) { - int theTCPWin = 0; - +int get_buffer_sock_size( int inSock, int inSend ) +{ + int bufSize = 0; #ifdef SO_SNDBUF + Socklen_t len = sizeof(bufSize); int rc; - Socklen_t len; - /* send buffer -- query for buffer size */ - len = sizeof( theTCPWin ); - if ( inSend ) { + if ( inSend ) rc = getsockopt( inSock, SOL_SOCKET, SO_SNDBUF, - (char*) &theTCPWin, &len ); - } else { + (char*) &bufSize, &len ); + else rc = getsockopt( inSock, SOL_SOCKET, SO_RCVBUF, - (char*) &theTCPWin, &len ); - } - if ( rc < 0 ) { + (char*) &bufSize, &len ); + if (rc < 0) return rc; - } - #endif - - return theTCPWin; -} /* end getsock_tcp_windowsize */ + return bufSize; +} #ifdef __cplusplus } /* end extern "C" */ #endif - -- 2.39.2