]> sjero.net Git - iperf/commitdiff
DCCP support for iperf
authorGerrit Renker <gerrit@erg.abdn.ac.uk>
Tue, 24 Feb 2009 19:31:15 +0000 (20:31 +0100)
committerGerrit Renker <gerrit@erg.abdn.ac.uk>
Tue, 24 Feb 2009 19:31:15 +0000 (20:31 +0100)
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).

23 files changed:
compat/error.c
include/Client.hpp
include/Locale.h
include/PerfSocket.hpp
include/Reporter.h
include/Server.hpp
include/Settings.hpp
include/headers.h
include/util.h
include/version.h
src/Client.cpp
src/Extractor.c
src/Listener.cpp
src/Locale.c
src/PerfSocket.cpp
src/ReportDefault.c
src/Reporter.c
src/Server.cpp
src/Settings.cpp
src/main.cpp
src/sockets.c
src/stdio.c
src/tcp_window_size.c

index 1d3647e3e4e3b9e497b1efe191b52a7998e67e24..6ba9ab3b3278e3ab83aef36894ba8fe689ee4284 100644 (file)
@@ -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
index e5d2e2956e7d9928e856c48488e0a53b59054d2c..2d4364dfc7625a4191d25ad9289b6ab53bbc222d 100644 (file)
@@ -74,7 +74,7 @@ public:
     // UDP / TCP
     void Send( void );
         
-    void write_UDP_FIN( );
+    void write_dgram_FIN( );
 
     // client connect
     void Connect( );
index 01f55b54d187171a22090325ac6b337cfab37992..5365016a251592f2c248eabe738d5b09a18be7d0 100644 (file)
@@ -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[];
index 2bfe3de848c6be0dd491eeeb50b72c595deea0e6..aa8f3b1ef1dc7db80c439e47b69919f3087fe22c 100644 (file)
@@ -59,6 +59,7 @@
 #include "Mutex.h"
 #include "Settings.hpp"
 
+    void MakeSocket( thread_Settings *inSettings );
     void SetSocketOptions( thread_Settings *inSettings );
 
     // handle interupts
index 91343f4d521627a81944ce3b384f89e91a631cd8..174055ff893b0f1a1e5c41d24da4f3f248409599 100644 (file)
@@ -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;
index c11d54bf9bab42a182d494e71ce4b6cb772ea4ea..06ac69833b8dff2c1f2ec99d45d9e326edd42e87 100644 (file)
@@ -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 );
 
index 4c3aede9b0f6e3c9e97593336098de8926a6638a..bf125cbeadbc886b829c82a85a59a0584e207f13 100644 (file)
 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
index 6bf6da8462081b80b6b7c4b716619a2a39372b8b..5b534f742a3ac978bba8a7f6277565ca5f71a729 100644 (file)
@@ -153,10 +153,15 @@ typedef uintmax_t max_size_t;
     #define SHUT_RDWR 2
 #endif // SHUT_RD
 
+/* DCCP-specific definitions */
+#include <linux/dccp.h>
+#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 */
-
-
-
-
-
-
index d23618cf191b94d370dbc94c6ddff12d76d40f7a..0f2d4253fe01e95a5ea9b22c7dcc927c139d360e 100644 (file)
@@ -52,7 +52,7 @@
 
 #ifndef UTIL_H
 #define UTIL_H
-
+#include <stdarg.h>
 #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 */
 
index 59d3ada96ad499d8f8b6d13f9f2c3c6c15e13381..f8f2c7df2fb5ab69d6e6e193b4d91c6d9297c7ca 100644 (file)
@@ -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"
index d6324d96b30968862d9afb68e9507ba60a1b19c3..2195c4fd3027c8b34f9da14273af7ffcca75b310 100644 (file)
@@ -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
index 2571ebfd4802ce83e8c0f7ee7e136f389882bf7f..6b4abda96a08a143f07441ee5aa354abc5d793f6 100644 (file)
@@ -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
  */
index cf3c1596c790cbd03c659e7ca19ff3c7ca4585ea..1cc0de944c232a316954cf712e78c36e95f2693b 100644 (file)
@@ -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);
         }
 
 
index d7e71f87439ff260aa8c6af795b0eae8fec6cc13..b2e8d1f8093a1f75366af8587e88c8e84c6a18f4 100644 (file)
@@ -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      <host>   bind to <host>, 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    <host>   run in client mode, connecting to <host>\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 <name>   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_<long option name>, such as IPERF_BANDWIDTH.\n\
 \n\
-Report bugs to <dast@nlanr.net>\n";
+Report bugs to <gerrit@erg.abdn.ac.uk>";
 
 // include a description of the threading in the version
 #if   defined( HAVE_POSIX_THREAD )
@@ -125,7 +126,7 @@ Report bugs to <dast@nlanr.net>\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";
index c25a0b6f80ad9118c9726c219a22c3767c1825e8..d149d596dd6ed2b9fd6d7d94693510c06b1f778d 100644 (file)
@@ -68,8 +68,6 @@
  *   <netinet/in.h>
  *   <sys/socket.h>
  * ------------------------------------------------------------------- */
-
-
 #define HEADERS()
 
 #include "headers.h"
 #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
index 7944e038e07295c37e69869a4b10743c86dc1c7b..a3ecc979060091c31dc30242c9cbd946668ce274 100644 (file)
@@ -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 );
index e34763ec99915ffdd145f02f26afa7e4febe2d23..db9418e3bb97baecd1754950589a574d9f7b662f 100644 (file)
@@ -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 );
index da7ef146fadca67c222dccf10879d4e2b1069f41..d7cd52ac730a78a71422511eb0717557995cf17d 100644 (file)
@@ -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 
-
index 2b71693c70c132b6fd49554908107a00ab069d4c..4cde826405af41db1b7fb6ef7021d1f85adc7044 100644 (file)
@@ -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);
index 167aed5b977a06b771d723660770cc2fcdb62d96..a69fd0ef91e1c3bdbffea4154f0f0940b687ddb0 100644 (file)
@@ -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 ) {
index 72eddd82a8c288fee619f9e536ce52a9cf76ab6f..2787c267de3e61c1ca1ecc1640d0c2f730729ba4 100644 (file)
@@ -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.
index 96782f79d0b4b0df283fb06a49cd0c9eb3b782a1..c6bc00390ba40654c17b86b72e22b1929e863f34 100644 (file)
@@ -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*/
index bdd28c5c32e1c95755259d9ab2845f85ca886eae..8bc107202f60238bf9e670d579940f156f477ce7 100644 (file)
 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
-