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.
This commit is contained in:
Bruce A. Mah 2014-08-28 08:59:32 -07:00
parent d30a2c8d04
commit 329523a557
No known key found for this signature in database
GPG Key ID: 4984910A8CAAEE8A
7 changed files with 215 additions and 22 deletions

12
LICENSE
View File

@ -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.
=====

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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

115
src/portable_endian.h Normal file
View File

@ -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 <endian.h>
#elif defined(__APPLE__)
# include <libkern/OSByteOrder.h>
# 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 <sys/endian.h>
#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
# include <sys/endian.h>
# 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 <winsock2.h>
# include <sys/param.h>
# 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