1
1
* Dynamically determine an appropriate default UDP send size.

We use the TCP MSS for the control connection as the default UDP
sending length, if the --length parameter is not specified for a
UDP test.  This computation replaces the former hard-coded 8K
default, which was way too large for non-jumbo-frame Ethernet
networks.

The concept for this solution was adapted from nuttcp.  The
iperf3 implementation is pretty easy since we already were
getting the MSS for the control connection anyway (although we
needed to get it slightly earlier in the setup process to be
useful).

Towards issue #496.

While here, s/int/socklen_t/ in one place to fix a compile warning,
and bump a few copyright dates.

* Warn if doing a UDP test and the socket buffer isn't big enough.

This is surprisingly an issue on FreeBSD and macOS, where the MTU
over the loopback interface is actually larger than the default
UDP socket buffer size.  In these cases, doing a UDP test over the
loopback interface (with the new UDP defaults) will fail unless a
smaller --length or a larger --window size is set explicitly.

Linux has larger UDP socket buffers by default (much larger than the
largest possible MTU), but even in the case that the socket buffers
are too small to hold an MTU-sized send, the kernel seems to do the
send correctly anyway.

Still working towards a good solution for issue #496.

* Further refinement on UDP buffer size settings.

If the default buffer size on a UDP test can't hold a packet,
then increase the buffer size to be large enough to hold one
packet payload.  (If the buffer size was explicitly set, but too
small to hold a packet payload, then warn but don't change the
buffer size.)

Minor code refactoring to...factor out some common code into
a new iperf_udp_buffercheck() function.

Still working towards issue #496.
Этот коммит содержится в:
Bruce A. Mah 2017-01-10 09:13:57 -08:00 коммит произвёл GitHub
родитель 9c83a707eb
Коммит 0fd60a3686
7 изменённых файлов: 174 добавлений и 90 удалений

Просмотреть файл

@ -1,5 +1,5 @@
/*
* iperf, Copyright (c) 2014, 2015, 2016, The Regents of the University of
* iperf, Copyright (c) 2014, 2015, 2016, 2017, 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.
@ -232,6 +232,8 @@ struct iperf_test
int listener;
int prot_listener;
int ctrl_sck_mss; /* MSS for the control channel */
/* boolean variables for Options */
int daemon; /* -D option */
int one_off; /* -1 option */

Просмотреть файл

@ -135,7 +135,12 @@ number of bytes to transmit (instead of \-t)
number of blocks (packets) to transmit (instead of \-t or \-n)
.TP
.BR -l ", " --length " \fIn\fR[KM]"
length of buffer to read or write (default 128 KB for TCP, 8KB for UDP)
length of buffer to read or write. For TCP tests, the default value
is 128KB.
In the case of UDP, iperf3 tries to dynamically determine a reasonable
sending size based on the path MTU; if that cannot be determined it
uses 1460 bytes as a sending size.
For SCTP tests, the default size is 64KB.
.TP
.BR --cport " \fIport\fR"
bind data streams to a specific client port (for TCP and UDP only,

Просмотреть файл

@ -1,5 +1,5 @@
/*
* iperf, Copyright (c) 2014, 2015, 2016, The Regents of the University of
* iperf, Copyright (c) 2014-2017, 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.
@ -126,6 +126,12 @@ iperf_get_control_socket(struct iperf_test *ipt)
return ipt->ctrl_sck;
}
int
iperf_get_control_socket_mss(struct iperf_test *ipt)
{
return ipt->ctrl_sck_mss;
}
int
iperf_get_test_omit(struct iperf_test *ipt)
{
@ -544,7 +550,6 @@ iperf_on_connect(struct iperf_test *test)
struct sockaddr_in *sa_inP;
struct sockaddr_in6 *sa_in6P;
socklen_t len;
int opt;
now_secs = time((time_t*) 0);
(void) strftime(now_str, sizeof(now_str), rfc1123_fmt, gmtime(&now_secs));
@ -585,9 +590,7 @@ iperf_on_connect(struct iperf_test *test)
if (test->settings->mss)
cJSON_AddNumberToObject(test->json_start, "tcp_mss", test->settings->mss);
else {
len = sizeof(opt);
getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_MAXSEG, &opt, &len);
cJSON_AddNumberToObject(test->json_start, "tcp_mss_default", opt);
cJSON_AddNumberToObject(test->json_start, "tcp_mss_default", test->ctrl_sck_mss);
}
}
} else if (test->verbose) {
@ -596,9 +599,7 @@ iperf_on_connect(struct iperf_test *test)
if (test->settings->mss)
iperf_printf(test, " TCP MSS: %d\n", test->settings->mss);
else {
len = sizeof(opt);
getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_MAXSEG, &opt, &len);
iperf_printf(test, " TCP MSS: %d (default)\n", opt);
iperf_printf(test, " TCP MSS: %d (default)\n", test->ctrl_sck_mss);
}
}
@ -1001,13 +1002,14 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
}
if (blksize == 0) {
if (test->protocol->id == Pudp)
blksize = DEFAULT_UDP_BLKSIZE;
blksize = 0; /* try to dynamically determine from MSS */
else if (test->protocol->id == Psctp)
blksize = DEFAULT_SCTP_BLKSIZE;
else
blksize = DEFAULT_TCP_BLKSIZE;
}
if (blksize <= 0 || blksize > MAX_BLOCKSIZE) {
if ((test->protocol->id != Pudp && blksize <= 0)
|| blksize > MAX_BLOCKSIZE) {
i_errno = IEBLOCKSIZE;
return -1;
}

Просмотреть файл

@ -1,5 +1,5 @@
/*
* iperf, Copyright (c) 2014, 2015, 2016, The Regents of the University of
* iperf, Copyright (c) 2014-2017, 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.
@ -39,7 +39,7 @@ struct iperf_stream;
#define Ptcp SOCK_STREAM
#define Pudp SOCK_DGRAM
#define Psctp 12
#define DEFAULT_UDP_BLKSIZE 8192
#define DEFAULT_UDP_BLKSIZE 1460 /* default is dynamically set, else this */
#define DEFAULT_TCP_BLKSIZE (128 * 1024) /* default read/write block size */
#define DEFAULT_SCTP_BLKSIZE (64 * 1024)

Просмотреть файл

@ -1,5 +1,5 @@
/*
* iperf, Copyright (c) 2014, 2015, 2016, The Regents of the University of
* iperf, Copyright (c) 2014-2017, 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.
@ -75,7 +75,7 @@ iperf_create_streams(struct iperf_test *test)
}
}
{
int len = TCP_CA_NAME_MAX;
socklen_t len = TCP_CA_NAME_MAX;
char ca[TCP_CA_NAME_MAX + 1];
if (getsockopt(s, IPPROTO_TCP, TCP_CONGESTION, ca, &len) < 0) {
close(s);
@ -335,6 +335,56 @@ iperf_connect(struct iperf_test *test)
FD_SET(test->ctrl_sck, &test->read_set);
if (test->ctrl_sck > test->max_fd) test->max_fd = test->ctrl_sck;
int opt;
socklen_t len;
len = sizeof(opt);
if (getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_MAXSEG, &opt, &len) < 0) {
test->ctrl_sck_mss = 0;
}
else {
test->ctrl_sck_mss = opt;
}
if (test->verbose) {
printf("Control connection MSS %d\n", test->ctrl_sck_mss);
}
/*
* If we're doing a UDP test and the block size wasn't explicitly
* set, then use the known MSS of the control connection to pick
* an appropriate default. If we weren't able to get the
* MSS for some reason, then default to something that should
* work on non-jumbo-frame Ethernet networks. The goal is to
* pick a reasonable default that is large but should get from
* sender to receiver without any IP fragmentation.
*
* We assume that the control connection is routed the same as the
* data packets (thus has the same PMTU). Also in the case of
* --reverse tests, we assume that the MTU is the same in both
* directions. Note that even if the algorithm guesses wrong,
* the user always has the option to override.
*/
if (test->protocol->id == Pudp) {
if (test->settings->blksize == 0) {
if (test->ctrl_sck_mss) {
test->settings->blksize = test->ctrl_sck_mss;
}
else {
test->settings->blksize = DEFAULT_UDP_BLKSIZE;
}
printf("Setting UDP block size to %d\n", test->settings->blksize);
}
/*
* Regardless of whether explicitly or implicitly set, if the
* block size is larger than the MSS, print a warning.
*/
if (test->settings->blksize > test->ctrl_sck_mss) {
printf("Warning: UDP block size %d exceeds TCP MSS %d, may result in fragmentation / drops\n", test->settings->blksize, test->ctrl_sck_mss);
}
}
return 0;
}

Просмотреть файл

@ -136,7 +136,7 @@ const char usage_longstr[] = "Usage: iperf [-s|-c host] [options]\n"
" -n, --bytes #[KMG] number of bytes to transmit (instead of -t)\n"
" -k, --blockcount #[KMG] number of blocks (packets) to transmit (instead of -t or -n)\n"
" -l, --len #[KMG] length of buffer to read or write\n"
" (default %d KB for TCP, %d KB for UDP)\n"
" (default %d KB for TCP, dynamic or %d for UDP)\n"
" --cport <port> bind to a specific client port (TCP and UDP, default: ephemeral port)\n"
" -P, --parallel # number of parallel client streams to run\n"
" -R, --reverse run in reverse mode (server sends, client receives)\n"

Просмотреть файл

@ -1,5 +1,5 @@
/*
* iperf, Copyright (c) 2014, 2016, The Regents of the University of
* iperf, Copyright (c) 2014, 2016, 2017, 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.
@ -194,39 +194,14 @@ iperf_udp_send(struct iperf_stream *sp)
*/
/*
* iperf_udp_accept
*
* Accepts a new UDP "connection"
* Set and verify socket buffer sizes.
* Return 0 if no error, -1 if an error, +1 if socket buffers are
* potentially too small to hold a message.
*/
int
iperf_udp_accept(struct iperf_test *test)
iperf_udp_buffercheck(struct iperf_test *test, int s)
{
struct sockaddr_storage sa_peer;
int buf;
socklen_t len;
int sz, s;
/*
* Get the current outstanding socket. This socket will be used to handle
* data transfers and a new "listening" socket will be created.
*/
s = test->prot_listener;
/*
* Grab the UDP packet sent by the client. From that we can extract the
* client's address, and then use that information to bind the remote side
* of the socket to the client.
*/
len = sizeof(sa_peer);
if ((sz = recvfrom(test->prot_listener, &buf, sizeof(buf), 0, (struct sockaddr *) &sa_peer, &len)) < 0) {
i_errno = IESTREAMACCEPT;
return -1;
}
if (connect(s, (struct sockaddr *) &sa_peer, len) < 0) {
i_errno = IESTREAMACCEPT;
return -1;
}
int rc = 0;
/*
* Set socket buffer size if requested. Do this for both sending and
@ -259,6 +234,14 @@ iperf_udp_accept(struct iperf_test *test)
i_errno = IESETBUF2;
return -1;
}
if (test->settings->blksize > opt) {
char str[80];
snprintf(str, sizeof(str),
"Block size %d > sending socket buffer size %d",
test->settings->blksize, opt);
warning(str);
rc = 1;
}
/* Read back and verify the receiver socket buffer size */
optlen = sizeof(opt);
@ -273,7 +256,73 @@ iperf_udp_accept(struct iperf_test *test)
i_errno = IESETBUF2;
return -1;
}
if (test->settings->blksize > opt) {
char str[80];
snprintf(str, sizeof(str),
"Block size %d > receiving socket buffer size %d",
test->settings->blksize, opt);
warning(str);
rc = 1;
}
return rc;
}
/*
* iperf_udp_accept
*
* Accepts a new UDP "connection"
*/
int
iperf_udp_accept(struct iperf_test *test)
{
struct sockaddr_storage sa_peer;
int buf;
socklen_t len;
int sz, s;
int rc;
/*
* Get the current outstanding socket. This socket will be used to handle
* data transfers and a new "listening" socket will be created.
*/
s = test->prot_listener;
/*
* Grab the UDP packet sent by the client. From that we can extract the
* client's address, and then use that information to bind the remote side
* of the socket to the client.
*/
len = sizeof(sa_peer);
if ((sz = recvfrom(test->prot_listener, &buf, sizeof(buf), 0, (struct sockaddr *) &sa_peer, &len)) < 0) {
i_errno = IESTREAMACCEPT;
return -1;
}
if (connect(s, (struct sockaddr *) &sa_peer, len) < 0) {
i_errno = IESTREAMACCEPT;
return -1;
}
/* Check and set socket buffer sizes */
rc = iperf_udp_buffercheck(test, s);
if (rc < 0)
/* error */
return rc;
/*
* If the socket buffer was too small, but it was the default
* size, then try explicitly setting it to something larger.
*/
if (rc > 0) {
if (test->settings->socket_bufsize == 0) {
printf("Increasing socket buffer size to %d\n",
test->settings->blksize);
test->settings->socket_bufsize = test->settings->blksize;
rc = iperf_udp_buffercheck(test, s);
if (rc < 0)
return rc;
}
}
#if defined(HAVE_SO_MAX_PACING_RATE)
/* If socket pacing is specified, try it. */
if (test->settings->fqrate) {
@ -357,6 +406,7 @@ iperf_udp_connect(struct iperf_test *test)
#ifdef SO_RCVTIMEO
struct timeval tv;
#endif
int rc;
/* Create and bind our local socket. */
if ((s = netdial(test->settings->domain, Pudp, test->bind_address, test->bind_port, test->server_hostname, test->server_port)) < 0) {
@ -364,51 +414,26 @@ iperf_udp_connect(struct iperf_test *test)
return -1;
}
/* Check and set socket buffer sizes */
rc = iperf_udp_buffercheck(test, s);
if (rc < 0)
/* error */
return rc;
/*
* Set socket buffer size if requested. Do this for both sending and
* receiving so that we can cover both normal and --reverse operation.
* If the socket buffer was too small, but it was the default
* size, then try explicitly setting it to something larger.
*/
int opt;
socklen_t optlen;
if ((opt = test->settings->socket_bufsize)) {
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
i_errno = IESETBUF;
return -1;
}
if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
i_errno = IESETBUF;
return -1;
}
if (rc > 0) {
if (test->settings->socket_bufsize == 0) {
printf("Increasing socket buffer size to %d\n",
test->settings->blksize);
test->settings->socket_bufsize = test->settings->blksize;
rc = iperf_udp_buffercheck(test, s);
if (rc < 0)
return rc;
}
}
/* Read back and verify the sender socket buffer size */
optlen = sizeof(opt);
if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, &optlen) < 0) {
i_errno = IESETBUF;
return -1;
}
if (test->debug) {
printf("SNDBUF is %u, expecting %u\n", opt, test->settings->socket_bufsize);
}
if (test->settings->socket_bufsize && test->settings->socket_bufsize > opt) {
i_errno = IESETBUF2;
return -1;
}
/* Read back and verify the receiver socket buffer size */
optlen = sizeof(opt);
if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, &optlen) < 0) {
i_errno = IESETBUF;
return -1;
}
if (test->debug) {
printf("RCVBUF is %u, expecting %u\n", opt, test->settings->socket_bufsize);
}
if (test->settings->socket_bufsize && test->settings->socket_bufsize > opt) {
i_errno = IESETBUF2;
return -1;
}
#if defined(HAVE_SO_MAX_PACING_RATE)
/* If socket pacing is available and not disabled, try it. */
if (test->settings->fqrate) {