From 329523a5574bafe387241261c06919296d00a105 Mon Sep 17 00:00:00 2001 From: "Bruce A. Mah" Date: Thu, 28 Aug 2014 08:59:32 -0700 Subject: [PATCH] Add a --udp-counters-64bit feature to support very long UDP tests. UDP tests store a packet sequence number in the packets to detect loss and ordering issues. This sequence number is a 32-bit signed integer, which can wrap during very long-running UDP tests. This change adds an option (defaulting to off) which uses a 64-bit unsigned integer to store this quantity in the packet. The option is specified on the client side; the server must support this feature for proper functioning (older servers will interoperate with newer clients, as long as --udp-counters-64-bit is not used). The default might be changed in a future version of iperf3. As a part of this change, the client sends its version string to the server in the parameter block. Uses a public-domain compatibility shim for 64-bit byte order conversions. There are probably some additional platforms that need to be supported, in particular Solaris. We might add some configure-time checks to only enable this feature on platforms where we can support the byte-order conversions. This change is not well-tested. Towards issue #191. --- LICENSE | 12 +++++ src/iperf.h | 5 +- src/iperf_api.c | 25 ++++++++- src/iperf_api.h | 3 ++ src/iperf_udp.c | 76 +++++++++++++++++++++------- src/locale.c | 1 + src/portable_endian.h | 115 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 215 insertions(+), 22 deletions(-) create mode 100644 src/portable_endian.h diff --git a/LICENSE b/LICENSE index adb1d16..e5fc4c8 100644 --- a/LICENSE +++ b/LICENSE @@ -255,3 +255,15 @@ This software contains source code (src/units.{c.h}) that is: * ------------------------------------------------------------------- * input and output numbers, converting with kilo, mega, giga * ------------------------------------------------------------------- */ + +===== + +This software contains source code (src/portable_endian.h) that is: + +// "License": Public Domain +// I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like. + +===== + + + diff --git a/src/iperf.h b/src/iperf.h index 9f3f8f2..07ce9a2 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -91,7 +91,7 @@ struct iperf_settings int tos; /* type of service bit */ int flowlabel; /* IPv6 flow label */ iperf_size_t bytes; /* number of bytes to send */ - int blocks; /* number of blocks (packets) to send */ + iperf_size_t blocks; /* number of blocks (packets) to send */ char unit_format; /* -f */ }; @@ -200,6 +200,7 @@ struct iperf_test int zerocopy; /* -Z option - use sendfile */ int debug; /* -d option - enable debug */ int get_server_output; /* --get-server-output */ + int udp_counters_64bit; /* --use-64-bit-udp-counters */ int multisend; @@ -227,7 +228,7 @@ struct iperf_test int num_streams; /* total streams in the test (-P) */ iperf_size_t bytes_sent; - int blocks_sent; + iperf_size_t blocks_sent; char cookie[COOKIE_SIZE]; // struct iperf_stream *streams; /* pointer to list of struct stream */ SLIST_HEAD(slisthead, iperf_stream) streams; diff --git a/src/iperf_api.c b/src/iperf_api.c index acc1366..068930f 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -59,7 +59,7 @@ #include "tcp_window_size.h" #include "iperf_util.h" #include "locale.h" - +#include "version.h" /* Forwards. */ static int send_parameters(struct iperf_test *test); @@ -235,6 +235,12 @@ iperf_get_test_bind_address(struct iperf_test *ipt) return ipt->bind_address; } +int +iperf_get_test_udp_counters_64bit(struct iperf_test *ipt) +{ + return ipt->udp_counters_64bit; +} + /************** Setter routines for some fields inside iperf_test *************/ void @@ -388,6 +394,12 @@ iperf_set_test_bind_address(struct iperf_test *ipt, char *bind_address) ipt->bind_address = strdup(bind_address); } +void +iperf_set_test_udp_counters_64bit(struct iperf_test *ipt, int udp_counters_64bit) +{ + ipt->udp_counters_64bit = udp_counters_64bit; +} + /********************** Get/set test protocol structure ***********************/ struct protocol * @@ -597,6 +609,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) {"pidfile", required_argument, NULL, 'I'}, {"logfile", required_argument, NULL, OPT_LOGFILE}, {"get-server-output", no_argument, NULL, OPT_GET_SERVER_OUTPUT}, + {"udp-counters-64bit", no_argument, NULL, OPT_UDP_COUNTERS_64BIT}, {"debug", no_argument, NULL, 'd'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} @@ -834,6 +847,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) test->get_server_output = 1; client_flag = 1; break; + case OPT_UDP_COUNTERS_64BIT: + test->udp_counters_64bit = 1; + break; case 'h': default: usage_long(); @@ -1197,6 +1213,10 @@ send_parameters(struct iperf_test *test) cJSON_AddStringToObject(j, "congestion", test->congestion); if (test->get_server_output) cJSON_AddIntToObject(j, "get_server_output", iperf_get_test_get_server_output(test)); + if (test->udp_counters_64bit) + cJSON_AddIntToObject(j, "udp_counters_64bit", iperf_get_test_udp_counters_64bit(test)); + + cJSON_AddStringToObject(j, "client_version", IPERF_VERSION); if (test->debug) { printf("send_parameters:\n%s\n", cJSON_Print(j)); @@ -1271,6 +1291,8 @@ get_parameters(struct iperf_test *test) test->congestion = strdup(j_p->valuestring); if ((j_p = cJSON_GetObjectItem(j, "get_server_output")) != NULL) iperf_set_test_get_server_output(test, 1); + if ((j_p = cJSON_GetObjectItem(j, "udp_counters_64bit")) != NULL) + iperf_set_test_udp_counters_64bit(test, 1); if (test->sender && test->protocol->id == Ptcp && has_tcpinfo_retransmits()) test->sender_has_retransmits = 1; cJSON_Delete(j); @@ -1883,6 +1905,7 @@ iperf_reset_test(struct iperf_test *test) test->settings->mss = 0; memset(test->cookie, 0, COOKIE_SIZE); test->multisend = 10; /* arbitrary */ + test->udp_counters_64bit = 0; /* Free output line buffers, if any (on the server only) */ struct iperf_textline *t; diff --git a/src/iperf_api.h b/src/iperf_api.h index 37504f6..ecac575 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -29,6 +29,7 @@ struct iperf_stream; #define OPT_SCTP 1 #define OPT_LOGFILE 2 #define OPT_GET_SERVER_OUTPUT 3 +#define OPT_UDP_COUNTERS_64BIT 4 /* states */ #define TEST_START 1 @@ -73,6 +74,7 @@ char* iperf_get_test_json_output_string ( struct iperf_test* ipt ); int iperf_get_test_zerocopy( struct iperf_test* ipt ); int iperf_get_test_get_server_output( struct iperf_test* ipt ); char* iperf_get_test_bind_address ( struct iperf_test* ipt ); +int iperf_get_test_udp_counters_64bit( struct iperf_test* ipt ); /* Setter routines for some fields inside iperf_test. */ void iperf_set_verbose( struct iperf_test* ipt, int verbose ); @@ -96,6 +98,7 @@ int iperf_has_zerocopy( void ); void iperf_set_test_zerocopy( struct iperf_test* ipt, int zerocopy ); void iperf_set_test_get_server_output( struct iperf_test* ipt, int get_server_output ); void iperf_set_test_bind_address( struct iperf_test* ipt, char *bind_address ); +void iperf_set_test_udp_counters_64bit( struct iperf_test* ipt, int udp_counters_64bit ); /** * exchange_parameters - handles the param_Exchange part for client diff --git a/src/iperf_udp.c b/src/iperf_udp.c index bc7a8f1..38d0cc7 100644 --- a/src/iperf_udp.c +++ b/src/iperf_udp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2011, The Regents of the University of California, + * Copyright (c) 2009-2011, 2014, The Regents of the University of California, * through Lawrence Berkeley National Laboratory (subject to receipt of any * required approvals from the U.S. Dept. of Energy). All rights reserved. * @@ -28,7 +28,7 @@ #include "iperf_udp.h" #include "timer.h" #include "net.h" - +#include "portable_endian.h" /* iperf_udp_recv * @@ -37,9 +37,10 @@ int iperf_udp_recv(struct iperf_stream *sp) { + uint32_t sec, usec; + uint64_t pcount; int r; int size = sp->settings->blksize; - uint32_t sec, usec, pcount; double transit = 0, d = 0; struct timeval sent_time, arrival_time; @@ -51,14 +52,27 @@ iperf_udp_recv(struct iperf_stream *sp) sp->result->bytes_received += r; sp->result->bytes_received_this_interval += r; - memcpy(&sec, sp->buffer, sizeof(sec)); - memcpy(&usec, sp->buffer+4, sizeof(usec)); - memcpy(&pcount, sp->buffer+8, sizeof(pcount)); - sec = ntohl(sec); - usec = ntohl(usec); - pcount = ntohl(pcount); - sent_time.tv_sec = sec; - sent_time.tv_usec = usec; + if (sp->test->udp_counters_64bit) { + memcpy(&sec, sp->buffer, sizeof(sec)); + memcpy(&usec, sp->buffer+4, sizeof(usec)); + memcpy(&pcount, sp->buffer+8, sizeof(pcount)); + sec = ntohl(sec); + usec = ntohl(usec); + pcount = be64toh(pcount); + sent_time.tv_sec = sec; + sent_time.tv_usec = usec; + } + else { + uint32_t pc; + memcpy(&sec, sp->buffer, sizeof(sec)); + memcpy(&usec, sp->buffer+4, sizeof(usec)); + memcpy(&pc, sp->buffer+8, sizeof(pc)); + sec = ntohl(sec); + usec = ntohl(usec); + pcount = ntohl(pc); + sent_time.tv_sec = sec; + sent_time.tv_usec = usec; + } /* Out of order packets */ if (pcount >= sp->packet_count + 1) { @@ -68,7 +82,7 @@ iperf_udp_recv(struct iperf_stream *sp) sp->packet_count = pcount; } else { sp->outoforder_packets++; - iperf_err(sp->test, "OUT OF ORDER - incoming packet = %d and received packet = %d AND SP = %d", pcount, sp->packet_count, sp->socket); + iperf_err(sp->test, "OUT OF ORDER - incoming packet = %llu and received packet = %d AND SP = %d", pcount, sp->packet_count, sp->socket); } /* jitter measurement */ @@ -83,6 +97,10 @@ iperf_udp_recv(struct iperf_stream *sp) // J = |(R1 - S1) - (R0 - S0)| [/ number of packets, for average] sp->jitter += (d - sp->jitter) / 16.0; + if (sp->test->debug) { + fprintf(stderr, "packet_count %llu\n", sp->packet_count); + } + return r; } @@ -95,20 +113,40 @@ int iperf_udp_send(struct iperf_stream *sp) { int r; - uint32_t sec, usec, pcount; int size = sp->settings->blksize; struct timeval before; gettimeofday(&before, 0); ++sp->packet_count; - sec = htonl(before.tv_sec); - usec = htonl(before.tv_usec); - pcount = htonl(sp->packet_count); - memcpy(sp->buffer, &sec, sizeof(sec)); - memcpy(sp->buffer+4, &usec, sizeof(usec)); - memcpy(sp->buffer+8, &pcount, sizeof(pcount)); + if (sp->test->udp_counters_64bit) { + + uint32_t sec, usec; + uint64_t pcount; + + sec = htonl(before.tv_sec); + usec = htonl(before.tv_usec); + pcount = htobe64(sp->packet_count); + + memcpy(sp->buffer, &sec, sizeof(sec)); + memcpy(sp->buffer+4, &usec, sizeof(usec)); + memcpy(sp->buffer+8, &pcount, sizeof(pcount)); + + } + else { + + uint32_t sec, usec, pcount; + + sec = htonl(before.tv_sec); + usec = htonl(before.tv_usec); + pcount = htonl(sp->packet_count); + + memcpy(sp->buffer, &sec, sizeof(sec)); + memcpy(sp->buffer+4, &usec, sizeof(usec)); + memcpy(sp->buffer+8, &pcount, sizeof(pcount)); + + } r = Nwrite(sp->socket, sp->buffer, size, Pudp); diff --git a/src/locale.c b/src/locale.c index f51ac8b..9e4c647 100644 --- a/src/locale.c +++ b/src/locale.c @@ -126,6 +126,7 @@ const char usage_longstr[] = "Usage: iperf [-s|-c host] [options]\n" " -O, --omit N omit the first n seconds\n" " -T, --title str prefix every output line with this string\n" " --get-server-output get results from server\n" + " --udp-counters-64bit use 64-bit counters in UDP test packets\n" #ifdef NOT_YET_SUPPORTED /* still working on these */ #endif diff --git a/src/portable_endian.h b/src/portable_endian.h new file mode 100644 index 0000000..42d40be --- /dev/null +++ b/src/portable_endian.h @@ -0,0 +1,115 @@ +// "License": Public Domain +// I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like. + +#ifndef PORTABLE_ENDIAN_H__ +#define PORTABLE_ENDIAN_H__ + +#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) + +# define __WINDOWS__ + +#endif + +#if defined(__linux__) || defined(__CYGWIN__) + +# include + +#elif defined(__APPLE__) + +# include + +# define htobe16(x) OSSwapHostToBigInt16(x) +# define htole16(x) OSSwapHostToLittleInt16(x) +# define be16toh(x) OSSwapBigToHostInt16(x) +# define le16toh(x) OSSwapLittleToHostInt16(x) + +# define htobe32(x) OSSwapHostToBigInt32(x) +# define htole32(x) OSSwapHostToLittleInt32(x) +# define be32toh(x) OSSwapBigToHostInt32(x) +# define le32toh(x) OSSwapLittleToHostInt32(x) + +# define htobe64(x) OSSwapHostToBigInt64(x) +# define htole64(x) OSSwapHostToLittleInt64(x) +# define be64toh(x) OSSwapBigToHostInt64(x) +# define le64toh(x) OSSwapLittleToHostInt64(x) + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#elif defined(__OpenBSD__) + +# include + +#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) + +# include + +# define be16toh(x) betoh16(x) +# define le16toh(x) letoh16(x) + +# define be32toh(x) betoh32(x) +# define le32toh(x) letoh32(x) + +# define be64toh(x) betoh64(x) +# define le64toh(x) letoh64(x) + +#elif defined(__WINDOWS__) + +# include +# include + +# if BYTE_ORDER == LITTLE_ENDIAN + +# define htobe16(x) htons(x) +# define htole16(x) (x) +# define be16toh(x) ntohs(x) +# define le16toh(x) (x) + +# define htobe32(x) htonl(x) +# define htole32(x) (x) +# define be32toh(x) ntohl(x) +# define le32toh(x) (x) + +# define htobe64(x) htonll(x) +# define htole64(x) (x) +# define be64toh(x) ntohll(x) +# define le64toh(x) (x) + +# elif BYTE_ORDER == BIG_ENDIAN + + /* that would be xbox 360 */ +# define htobe16(x) (x) +# define htole16(x) __builtin_bswap16(x) +# define be16toh(x) (x) +# define le16toh(x) __builtin_bswap16(x) + +# define htobe32(x) (x) +# define htole32(x) __builtin_bswap32(x) +# define be32toh(x) (x) +# define le32toh(x) __builtin_bswap32(x) + +# define htobe64(x) (x) +# define htole64(x) __builtin_bswap64(x) +# define be64toh(x) (x) +# define le64toh(x) __builtin_bswap64(x) + +# else + +# error byte order not supported + +# endif + +# define __BYTE_ORDER BYTE_ORDER +# define __BIG_ENDIAN BIG_ENDIAN +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __PDP_ENDIAN PDP_ENDIAN + +#else + +# error platform not supported + +#endif + +#endif \ No newline at end of file