Issue 496 (#498)
* 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.
Этот коммит содержится в:
родитель
9c83a707eb
Коммит
0fd60a3686
@ -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"
|
||||
|
171
src/iperf_udp.c
171
src/iperf_udp.c
@ -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) {
|
||||
|
Загрузка…
x
Ссылка в новой задаче
Block a user