aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBruce A. Mah <bmah@kitchenlab.org>2017-05-17 12:50:50 -0700
committerGitHub <noreply@github.com>2017-05-17 12:50:50 -0700
commit9d7d60aca1c946547c88042a302a434d10d580ed (patch)
treeb5868160e7f050121040d438561ff6ba1fd96e6b /src
parentf1415a0d98bb6737329bb4091d3289d9b341bd72 (diff)
downloadiperf3-9d7d60aca1c946547c88042a302a434d10d580ed.tar.gz
Issue 216 (#581)
* Add configurable timeout for the setup of the control connection. This is specified using the new --connect-timeout option, with an integer parameter in ms. The iperf3 client will wait for this amount of time for the setup of the control connection to the server. If this option is not given, the OS default for TCP connection setup is used. Specifying a smaller connection timeout allows faster detection of a down / unresponsive iperf3 server. The implementation uses a variation on the timeout_connect() function from OpenBSD's netcat utility. Fixes #216.
Diffstat (limited to 'src')
-rwxr-xr-xsrc/iperf.h1
-rw-r--r--src/iperf3.18
-rwxr-xr-xsrc/iperf_api.c6
-rwxr-xr-xsrc/iperf_api.h1
-rw-r--r--src/iperf_client_api.c2
-rw-r--r--src/iperf_config.h.in3
-rw-r--r--src/iperf_locale.c1
-rw-r--r--src/iperf_udp.c2
-rw-r--r--src/net.c52
-rw-r--r--src/net.h5
10 files changed, 74 insertions, 7 deletions
diff --git a/src/iperf.h b/src/iperf.h
index 53c44d2..c270d73 100755
--- a/src/iperf.h
+++ b/src/iperf.h
@@ -128,6 +128,7 @@ struct iperf_settings
char unit_format; /* -f */
int num_ostreams; /* SCTP initmsg settings */
char *authtoken; /* Authentication token */
+ int connect_timeout; /* socket connection timeout, in ms */
};
struct iperf_test;
diff --git a/src/iperf3.1 b/src/iperf3.1
index fbf1ae5..34206db 100644
--- a/src/iperf3.1
+++ b/src/iperf3.1
@@ -110,6 +110,14 @@ use SCTP rather than TCP (FreeBSD and Linux)
.BR -u ", " --udp
use UDP rather than TCP
.TP
+.BR --connect-timeout " \fIn\fR"
+set timeout for establishing the initial control connection to the
+server, in milliseconds.
+The default behavior is the operating system's timeout for TCP
+connection establishment.
+Providing a shorter value may speed up detection of a down iperf3
+server.
+.TP
.BR -b ", " --bandwidth " \fIn\fR[KM]"
set target bandwidth to \fIn\fR bits/sec (default 1 Mbit/sec for UDP, unlimited for TCP).
If there are multiple streams (\-P flag), the bandwidth limit is applied
diff --git a/src/iperf_api.c b/src/iperf_api.c
index f33e720..2813eb8 100755
--- a/src/iperf_api.c
+++ b/src/iperf_api.c
@@ -682,6 +682,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
#endif /* HAVE_SSL */
{"fq-rate", required_argument, NULL, OPT_FQ_RATE},
{"pacing-timer", required_argument, NULL, OPT_PACING_TIMER},
+ {"connect-timeout", required_argument, NULL, OPT_CONNECT_TIMEOUT},
{"debug", no_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
@@ -1035,6 +1036,10 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
test->settings->pacing_timer = unit_atoi(optarg);
client_flag = 1;
break;
+ case OPT_CONNECT_TIMEOUT:
+ test->settings->connect_timeout = unit_atoi(optarg);
+ client_flag = 1;
+ break;
case 'h':
usage_long(stdout);
exit(0);
@@ -2070,6 +2075,7 @@ iperf_defaults(struct iperf_test *testp)
testp->settings->mss = 0;
testp->settings->bytes = 0;
testp->settings->blocks = 0;
+ testp->settings->connect_timeout = -1;
memset(testp->cookie, 0, COOKIE_SIZE);
testp->multisend = 10; /* arbitrary */
diff --git a/src/iperf_api.h b/src/iperf_api.h
index e1f3137..69ae8ca 100755
--- a/src/iperf_api.h
+++ b/src/iperf_api.h
@@ -59,6 +59,7 @@ struct iperf_stream;
#define OPT_SERVER_RSA_PRIVATE_KEY 14
#define OPT_SERVER_AUTHORIZED_USERS 15
#define OPT_PACING_TIMER 16
+#define OPT_CONNECT_TIMEOUT 17
/* states */
#define TEST_START 1
diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c
index 3b4e448..64dec4a 100644
--- a/src/iperf_client_api.c
+++ b/src/iperf_client_api.c
@@ -321,7 +321,7 @@ iperf_connect(struct iperf_test *test)
/* Create and connect the control channel */
if (test->ctrl_sck < 0)
// Create the control channel using an ephemeral port
- test->ctrl_sck = netdial(test->settings->domain, Ptcp, test->bind_address, 0, test->server_hostname, test->server_port);
+ test->ctrl_sck = netdial(test->settings->domain, Ptcp, test->bind_address, 0, test->server_hostname, test->server_port, test->settings->connect_timeout);
if (test->ctrl_sck < 0) {
i_errno = IECONNECT;
return -1;
diff --git a/src/iperf_config.h.in b/src/iperf_config.h.in
index 5aebee3..bd03935 100644
--- a/src/iperf_config.h.in
+++ b/src/iperf_config.h.in
@@ -21,6 +21,9 @@
/* Define to 1 if you have the <netinet/sctp.h> header file. */
#undef HAVE_NETINET_SCTP_H
+/* Define to 1 if you have the <poll.h> header file. */
+#undef HAVE_POLL_H
+
/* Define to 1 if you have the `sched_setaffinity' function. */
#undef HAVE_SCHED_SETAFFINITY
diff --git a/src/iperf_locale.c b/src/iperf_locale.c
index d90a6df..9d65a55 100644
--- a/src/iperf_locale.c
+++ b/src/iperf_locale.c
@@ -131,6 +131,7 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
" --nstreams # number of SCTP streams\n"
#endif /* HAVE_SCTP */
" -u, --udp use UDP rather than TCP\n"
+ " --connect-timeout # timeout for control connection setup (ms)\n"
" -b, --bandwidth #[KMG][/#] target bandwidth in bits/sec (0 for unlimited)\n"
" (default %d Mbit/sec for UDP, unlimited for TCP)\n"
" (optional slash and packet count for burst mode)\n"
diff --git a/src/iperf_udp.c b/src/iperf_udp.c
index 85ef4d4..e006b09 100644
--- a/src/iperf_udp.c
+++ b/src/iperf_udp.c
@@ -427,7 +427,7 @@ iperf_udp_connect(struct iperf_test *test)
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) {
+ if ((s = netdial(test->settings->domain, Pudp, test->bind_address, test->bind_port, test->server_hostname, test->server_port, -1)) < 0) {
i_errno = IESTREAMCONNECT;
return -1;
}
diff --git a/src/net.c b/src/net.c
index aa4a15c..0d04a4a 100644
--- a/src/net.c
+++ b/src/net.c
@@ -1,5 +1,5 @@
/*
- * iperf, Copyright (c) 2014, 2015, The Regents of the University of
+ * iperf, Copyright (c) 2014, 2015, 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.
@@ -56,17 +56,63 @@
#endif
#endif /* HAVE_SENDFILE */
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif /* HAVE_POLL_H */
+
#include "iperf_util.h"
#include "net.h"
#include "timer.h"
+/*
+ * timeout_connect adapted from netcat, via OpenBSD and FreeBSD
+ * Copyright (c) 2001 Eric Jackson <ericj@monkey.org>
+ */
+int
+timeout_connect(int s, const struct sockaddr *name, socklen_t namelen,
+ int timeout)
+{
+ struct pollfd pfd;
+ socklen_t optlen;
+ int flags, optval;
+ int ret;
+
+ if (timeout != -1) {
+ flags = fcntl(s, F_GETFL, 0);
+ if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1)
+ return -1;
+ }
+
+ if ((ret = connect(s, name, namelen)) != 0 && errno == EINPROGRESS) {
+ pfd.fd = s;
+ pfd.events = POLLOUT;
+ if ((ret = poll(&pfd, 1, timeout)) == 1) {
+ optlen = sizeof(optval);
+ if ((ret = getsockopt(s, SOL_SOCKET, SO_ERROR,
+ &optval, &optlen)) == 0) {
+ errno = optval;
+ ret = optval == 0 ? 0 : -1;
+ }
+ } else if (ret == 0) {
+ errno = ETIMEDOUT;
+ ret = -1;
+ } else
+ ret = -1;
+ }
+
+ if (timeout != -1 && fcntl(s, F_SETFL, flags) == -1)
+ ret = -1;
+
+ return (ret);
+}
+
/* netdial and netannouce code comes from libtask: http://swtch.com/libtask/
* Copyright: http://swtch.com/libtask/COPYRIGHT
*/
/* make connection to server */
int
-netdial(int domain, int proto, char *local, int local_port, char *server, int port)
+netdial(int domain, int proto, char *local, int local_port, char *server, int port, int timeout)
{
struct addrinfo hints, *local_res, *server_res;
int s;
@@ -111,7 +157,7 @@ netdial(int domain, int proto, char *local, int local_port, char *server, int po
}
((struct sockaddr_in *) server_res->ai_addr)->sin_port = htons(port);
- if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) {
+ if (timeout_connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen, timeout) < 0 && errno != EINPROGRESS) {
close(s);
freeaddrinfo(server_res);
return -1;
diff --git a/src/net.h b/src/net.h
index d1188c3..9cfc522 100644
--- a/src/net.h
+++ b/src/net.h
@@ -1,5 +1,5 @@
/*
- * iperf, Copyright (c) 2014, 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.
@@ -27,7 +27,8 @@
#ifndef __NET_H
#define __NET_H
-int netdial(int domain, int proto, char *local, int local_port, char *server, int port);
+int timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, int timeout);
+int netdial(int domain, int proto, char *local, int local_port, char *server, int port, int timeout);
int netannounce(int domain, int proto, char *local, int port);
int Nread(int fd, char *buf, size_t count, int prot);
int Nwrite(int fd, const char *buf, size_t count, int prot) /* __attribute__((hot)) */;