Skip to content

Commit

Permalink
Fix #812 and #835 - add control connection keepalive
Browse files Browse the repository at this point in the history
  • Loading branch information
davidBar-On committed Nov 20, 2022
1 parent 9d78f79 commit 4c4139d
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 7 deletions.
12 changes: 12 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,18 @@ if test "x$iperf3_cv_header_tcp_user_timeout" = "xyes"; then
AC_DEFINE([HAVE_TCP_USER_TIMEOUT], [1], [Have TCP_USER_TIMEOUT sockopt.])
fi

# Check for TCP_KEEPIDLE sockopt (not clear where supported)
AC_CACHE_CHECK([TCP_KEEPIDLE socket option],
[iperf3_cv_header_tcp_keepalive],
AC_COMPILE_IFELSE(
[AC_LANG_PROGRAM([[#include <netinet/tcp.h>]],
[[int foo = TCP_KEEPIDLE;]])],
iperf3_cv_header_tcp_keepalive=yes,
iperf3_cv_header_tcp_keepalive=no))
if test "x$iperf3_cv_header_tcp_keepalive" = "xyes"; then
AC_DEFINE([HAVE_TCP_KEEPALIVE], [1], [Have TCP_KEEPIDLE sockopt.])
fi

# Check for IPv6 flowlabel support (believed to be Linux only)
# We check for IPV6_FLOWLABEL_MGR in <linux/in6.h> even though we
# don't use that file directly (we have our own stripped-down
Expand Down
4 changes: 4 additions & 0 deletions src/iperf.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ struct iperf_settings
int idle_timeout; /* server idle time timeout */
unsigned int snd_timeout; /* Timeout for sending tcp messages in active mode, in us */
struct iperf_time rcv_timeout; /* Timeout for receiving messages in active mode, in us */
int cntl_ka; /* Use Control TCP connection Keepalive */
int cntl_ka_keepidle; /* Control TCP connection Keepalive idle time (TCP_KEEPIDLE) */
int cntl_ka_interval; /* Control TCP connection Keepalive interval between retries (TCP_KEEPINTV) */
int cntl_ka_count; /* Control TCP connection Keepalive number of retries (TCP_KEEPCNT) */
};

struct iperf_test;
Expand Down
40 changes: 40 additions & 0 deletions src/iperf_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
{"idle-timeout", required_argument, NULL, OPT_IDLE_TIMEOUT},
{"rcv-timeout", required_argument, NULL, OPT_RCV_TIMEOUT},
{"snd-timeout", required_argument, NULL, OPT_SND_TIMEOUT},
#if defined(HAVE_TCP_KEEPALIVE)
{"cntl-ka", optional_argument, NULL, OPT_CNTL_KA},
#endif /* HAVE_TCP_KEEPALIVE */
{"debug", optional_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
Expand All @@ -1091,6 +1094,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
char* comma;
#endif /* HAVE_CPU_AFFINITY */
char* slash;
#if defined(HAVE_TCP_KEEPALIVE)
char* slash2;
#endif /* HAVE_TCP_KEEPALIVE */
char *p, *p1;
struct xbind_entry *xbe;
double farg;
Expand Down Expand Up @@ -1454,6 +1460,32 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
snd_timeout_flag = 1;
break;
#endif /* HAVE_TCP_USER_TIMEOUT */
#if defined (HAVE_TCP_KEEPALIVE)
case OPT_CNTL_KA:
test->settings->cntl_ka = 1;
if (optarg) {
slash = strchr(optarg, '/');
if (slash) {
*slash = '\0';
++slash;
slash2 = strchr(slash, '/');
if (slash2) {
*slash2 = '\0';
++slash2;
if (strlen(slash2) > 0) {
test->settings->cntl_ka_count = atoi(slash2);
}
}
if (strlen(slash) > 0) {
test->settings->cntl_ka_interval = atoi(slash);
}
}
if (strlen(optarg) > 0) {
test->settings->cntl_ka_keepidle = atoi(optarg);
}
}
break;
#endif /* HAVE_TCP_KEEPALIVE */
case 'A':
#if defined(HAVE_CPU_AFFINITY)
test->affinity = strtol(optarg, &endptr, 0);
Expand Down Expand Up @@ -2805,6 +2837,10 @@ iperf_defaults(struct iperf_test *testp)
testp->settings->rcv_timeout.secs = DEFAULT_NO_MSG_RCVD_TIMEOUT / SEC_TO_mS;
testp->settings->rcv_timeout.usecs = (DEFAULT_NO_MSG_RCVD_TIMEOUT % SEC_TO_mS) * mS_TO_US;
testp->zerocopy = 0;
testp->settings->cntl_ka = 0;
testp->settings->cntl_ka_keepidle = 0;
testp->settings->cntl_ka_interval = 0;
testp->settings->cntl_ka_count = 0;

memset(testp->cookie, 0, COOKIE_SIZE);

Expand Down Expand Up @@ -3091,6 +3127,10 @@ iperf_reset_test(struct iperf_test *test)
test->settings->tos = 0;
test->settings->dont_fragment = 0;
test->zerocopy = 0;
test->settings->cntl_ka = 0;
test->settings->cntl_ka_keepidle = 0;
test->settings->cntl_ka_interval = 0;
test->settings->cntl_ka_count = 0;

#if defined(HAVE_SSL)
if (test->settings->authtoken) {
Expand Down
6 changes: 6 additions & 0 deletions src/iperf_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ typedef uint64_t iperf_size_t;
#define OPT_DONT_FRAGMENT 26
#define OPT_RCV_TIMEOUT 27
#define OPT_SND_TIMEOUT 28
#define OPT_CNTL_KA 29

/* states */
#define TEST_START 1
Expand Down Expand Up @@ -390,6 +391,7 @@ enum {
IERCVTIMEOUT = 31, // Illegal message receive timeout
IERVRSONLYRCVTIMEOUT = 32, // Client receive timeout is valid only in reverse mode
IESNDTIMEOUT = 33, // Illegal message send timeout
IECNTLKA = 34, // Control connection Keepalive period should be larger than retry period (interval * count)
/* Test errors */
IENEWTEST = 100, // Unable to create a new test (check perror)
IEINITTEST = 101, // Test initialization failed (check perror)
Expand Down Expand Up @@ -440,6 +442,10 @@ enum {
IEBINDDEVNOSUPPORT = 146, // `ip%%dev` is not supported as system does not support bind to device
IEHOSTDEV = 147, // host device name (ip%%<dev>) is supported (and required) only for IPv6 link-local address
IESETUSERTIMEOUT = 148, // Unable to set TCP USER_TIMEOUT (check perror)
IESETCNTLKA = 149, // Unable to set socket keepalive (SO_KEEPALIVE) option
IESETCNTLKAKEEPIDLE = 150, // Unable to set socket keepalive TCP period (TCP_KEEPIDLE) option
IESETCNTLKAINTERVAL = 151, // Unable to set/get socket keepalive TCP retry interval (TCP_KEEPINTVL) option
IESETCNTLKACOUNT = 152, // Unable to set/get socket keepalive TCP number of retries (TCP_KEEPCNT) option
/* Stream errors */
IECREATESTREAM = 200, // Unable to create a new stream (check herror/perror)
IEINITSTREAM = 201, // Unable to initialize stream (check herror/perror)
Expand Down
78 changes: 76 additions & 2 deletions src/iperf_client_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,9 @@ iperf_connect(struct iperf_test *test)
{
int opt;
socklen_t len;
#if defined(HAVE_TCP_KEEPALIVE)
int kainterval, kacount;
#endif /* HAVE_TCP_KEEPALIVE */

if (NULL == test)
{
Expand Down Expand Up @@ -391,11 +394,82 @@ iperf_connect(struct iperf_test *test)
return -1;
}

#if defined (HAVE_TCP_KEEPALIVE)
// Set Control Connection TCP Keepalive (especially useful for long UDP test sessions)
if (test->settings->cntl_ka) {
opt = 1;
if (setsockopt(test->ctrl_sck, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, sizeof(opt))) {
i_errno = IESETCNTLKA;
return -1;
}

if ((opt = test->settings->cntl_ka_keepidle)) {
if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_KEEPIDLE, (char *) &opt, sizeof(opt))) {
i_errno = IESETCNTLKAKEEPIDLE;
return -1;
}
}

// Seems that at least in Windows WSL2, TCP keepalive retries inteval must be smaller
// than keepalive interval. Otherwise, the keepalive message is sent only once.
// To make sure issues will not impact the tests in case the keepalive period was set,
// when possible it is made sure that the full keepalive retries interval is less
// than the the keepalive period.
if (test->settings->cntl_ka_keepidle) {
if ((kainterval = test->settings->cntl_ka_interval) == 0) {
len = sizeof(kainterval);
if (getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_KEEPINTVL, (char *) &kainterval, &len)) {
i_errno = IESETCNTLKAINTERVAL;
return -1;
}
}
if ((kacount = test->settings->cntl_ka_count) == 0) {
len = sizeof(kacount);
if (getsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_KEEPCNT, (char *) &kacount, &len)) {
i_errno = IESETCNTLKACOUNT;
return -1;
}
}

if (test->settings->cntl_ka_keepidle <= (kainterval * kacount)) {
if (test->settings->cntl_ka_interval > 0 || test->settings->cntl_ka_count > 0) {
i_errno = IECNTLKA;
return -1;
}
if (kacount > 0) {
test->settings->cntl_ka_interval = ((test->settings->cntl_ka_keepidle - 1) / kacount);
if (test->settings->cntl_ka_interval <= 0) {
test->settings->cntl_ka_interval = 1;
}
}
}
}

if ((opt = test->settings->cntl_ka_interval)) {
if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_KEEPINTVL, (char *) &opt, sizeof(opt))) {
i_errno = IESETCNTLKAINTERVAL;
return -1;
}
}
if ((opt = test->settings->cntl_ka_count)) {
if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_KEEPCNT, (char *) &opt, sizeof(opt))) {
i_errno = IESETCNTLKACOUNT;
return -1;
}
}

if (test->verbose) {
printf("Control connection TCP Keepalive TCP_KEEPIDLE/TCP_KEEPINTVL/TCP_KEEPCNT set to %d/%d/%d (0 is system default)\n",
test->settings->cntl_ka_keepidle, test->settings->cntl_ka_interval, test->settings->cntl_ka_count);
}
}
#endif //HAVE_TCP_KEEPALIVE

#if defined(HAVE_TCP_USER_TIMEOUT)
if ((opt = test->settings->snd_timeout)) {
if (setsockopt(test->ctrl_sck, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) {
i_errno = IESETUSERTIMEOUT;
return -1;
i_errno = IESETUSERTIMEOUT;
return -1;
}
}
#endif /* HAVE_TCP_USER_TIMEOUT */
Expand Down
30 changes: 25 additions & 5 deletions src/iperf_error.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ iperf_strerror(int int_errno)
case IEINTERVAL:
snprintf(errstr, len, "invalid report interval (min = %g, max = %g seconds)", MIN_INTERVAL, MAX_INTERVAL);
break;
case IEBIND: /* UNUSED */
case IEBIND: /* UNUSED */
snprintf(errstr, len, "--bind must be specified to use --cport");
break;
case IEUDPBLOCKSIZE:
Expand Down Expand Up @@ -437,7 +437,7 @@ iperf_strerror(int int_errno)
case IETOTALRATE:
snprintf(errstr, len, "total required bandwidth is larger than server limit");
break;
case IESKEWTHRESHOLD:
case IESKEWTHRESHOLD:
snprintf(errstr, len, "skew threshold must be a positive number");
break;
case IEIDLETIMEOUT:
Expand All @@ -446,22 +446,42 @@ iperf_strerror(int int_errno)
case IEBINDDEV:
snprintf(errstr, len, "Unable to bind-to-device (check perror, maybe permissions?)");
break;
case IEBINDDEVNOSUPPORT:
case IEBINDDEVNOSUPPORT:
snprintf(errstr, len, "`<ip>%%<dev>` is not supported as system does not support bind to device");
break;
case IEHOSTDEV:
case IEHOSTDEV:
snprintf(errstr, len, "host device name (ip%%<dev>) is supported (and required) only for IPv6 link-local address");
break;
case IENOMSG:
snprintf(errstr, len, "idle timeout for receiving data");
break;
case IESETDONTFRAGMENT:
case IESETDONTFRAGMENT:
snprintf(errstr, len, "unable to set IP Do-Not-Fragment flag");
break;
case IESETUSERTIMEOUT:
snprintf(errstr, len, "unable to set TCP/SCTP MSS");
perr = 1;
break;
case IECNTLKA:
snprintf(errstr, len, "control connection Keepalive period should be larger than retry period (interval * count)");
perr = 1;
break;
case IESETCNTLKA:
snprintf(errstr, len, "unable to set socket keepalive (SO_KEEPALIVE) option");
perr = 1;
break;
case IESETCNTLKAKEEPIDLE:
snprintf(errstr, len, "unable to set socket keepalive TCP period (TCP_KEEPIDLE) option");
perr = 1;
break;
case IESETCNTLKAINTERVAL:
snprintf(errstr, len, "unable to set/get socket keepalive TCP retry interval (TCP_KEEPINTVL) option");
perr = 1;
break;
case IESETCNTLKACOUNT:
snprintf(errstr, len, "unable to set/get socket keepalive TCP number of retries (TCP_KEEPCNT) option");
perr = 1;
break;
default:
snprintf(errstr, len, "int_errno=%d", int_errno);
perr = 1;
Expand Down
4 changes: 4 additions & 0 deletions src/iperf_locale.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
" --snd-timeout # timeout for unacknowledged TCP data\n"
" (in ms, default is system settings)\n"
#endif /* HAVE_TCP_USER_TIMEOUT */
#if defined(HAVE_TCP_KEEPALIVE)
" --cntl-ka[=#/#/#] use control connection TCP keepalive - KEEPIDLE/KEEPINTV/KEEPCNT\n"
" each value is optional with system settings default\n"
#endif //HAVE_TCP_KEEPALIVE
" -d, --debug[=#] emit debugging output\n"
" (optional optional \"=\" and debug level: 1-4. Default is 4 - all messages)\n"
" -v, --version show version information and quit\n"
Expand Down

0 comments on commit 4c4139d

Please sign in to comment.