diff --git a/docs/invoking.rst b/docs/invoking.rst index c7da7fcbc..ca7ed993c 100644 --- a/docs/invoking.rst +++ b/docs/invoking.rst @@ -226,6 +226,16 @@ the executable. iperf3 versions. Use this option to preserve the less secure, but more compatible, behavior. + --control-tos n + Set the IP type of service bits of the control connection. The + usual prefixes for octal and hex can be used, i.e. 52, 064 and + 0x34 all specify the same value. + + --control-dscp dscp + Set the IP DSCP bits of the control connection. Both numeric and + symbolic values are accepted. Numeric values can be specified in + decimal, octal and hex (see --control-tos above). + -m, --mptcp Use the MPTCP variant for the current protocol. This only ap- plies to TCP and enables MPTCP usage. diff --git a/src/iperf.h b/src/iperf.h index a6052737a..8dd389e98 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -172,6 +172,7 @@ struct iperf_settings int mss; /* for TCP MSS */ int ttl; /* IP TTL option */ int tos; /* type of service bit */ + int ctrl_tos; /* type of service for control socket */ int flowlabel; /* IPv6 flow label */ iperf_size_t bytes; /* number of bytes to send */ iperf_size_t blocks; /* number of blocks (packets) to send */ diff --git a/src/iperf_api.c b/src/iperf_api.c index fb4273f1e..5e25637fd 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1139,6 +1139,8 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) {"version6", no_argument, NULL, '6'}, {"tos", required_argument, NULL, 'S'}, {"dscp", required_argument, NULL, OPT_DSCP}, + {"control-tos", required_argument, NULL, OPT_CONTROL_TOS}, + {"control-dscp", required_argument, NULL, OPT_CONTROL_DSCP}, {"extra-data", required_argument, NULL, OPT_EXTRA_DATA}, #if defined(HAVE_FLOWLABEL) {"flowlabel", required_argument, NULL, 'L'}, @@ -1519,6 +1521,22 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) } client_flag = 1; break; + case OPT_CONTROL_TOS: + test->settings->ctrl_tos = strtol(optarg, &endptr, 0); + if (endptr == optarg || + test->settings->ctrl_tos < 0 || + test->settings->ctrl_tos > 255) { + i_errno = IEBADTOS; + return -1; + } + break; + case OPT_CONTROL_DSCP: + test->settings->ctrl_tos = parse_qos(optarg); + if(test->settings->ctrl_tos < 0) { + i_errno = IEBADTOS; + return -1; + } + break; case OPT_EXTRA_DATA: test->extra_data = strdup(optarg); client_flag = 1; @@ -5570,6 +5588,34 @@ iflush(struct iperf_test *test) return rc2; } +int +iperf_set_tos(int s, int tos) +{ + if (tos) { + if (getsockdomain(s) == AF_INET6) { +#ifdef IPV6_TCLASS + if (setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)) < 0) { + i_errno = IESETCOS; + return -1; + } + if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) { + /* ignore any failure of v4 TOS in IPv6 case */ + } +#else + i_errno = IESETCOS; + return -1; +#endif + } else { + if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) { + i_errno = IESETTOS; + return -1; + } + } + } + + return 0; +} + #if defined (HAVE_TCP_KEEPALIVE) // Set Control Connection TCP Keepalive (especially useful for long UDP test sessions) int diff --git a/src/iperf_api.h b/src/iperf_api.h index 25ac951e0..4d4cb273c 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -107,6 +107,8 @@ typedef atomic_uint_fast64_t atomic_iperf_size_t; #define OPT_JSON_STREAM_FULL_OUTPUT 33 #define OPT_SERVER_MAX_DURATION 34 #define OPT_GSRO 35 +#define OPT_CONTROL_TOS 36 +#define OPT_CONTROL_DSCP 37 /* states */ #define TEST_START 1 @@ -320,6 +322,12 @@ void iperf_free_stream(struct iperf_stream * sp); */ int iperf_common_sockopts(struct iperf_test *, int s); +/** + * iperf_set_tos -- set socket TOS + * + */ +int iperf_set_tos(int s, int tos); + #if defined (HAVE_TCP_KEEPALIVE) /** * iperf_set_control_keepalive -- set control connection TCP keepalive diff --git a/src/iperf_client_api.c b/src/iperf_client_api.c index 165193ba6..a24189699 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -414,6 +415,37 @@ iperf_handle_message_client(struct iperf_test *test) return 0; } +/* + * Make connection to server + * This is derived from netdial() but also sets the tos before connection. + */ +static int +netdial_with_tos(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, int timeout, int tos) +{ + struct addrinfo *server_res = NULL; + int s, saved_errno; + + s = create_socket(domain, proto, 0, local, bind_dev, local_port, server, port, &server_res); + if (s < 0) { + return -1; + } + + // Set IP TOS + if (iperf_set_tos(s, tos) < 0) { + return -1; + } + + if (timeout_connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen, timeout) < 0 && errno != EINPROGRESS) { + saved_errno = errno; + close(s); + freeaddrinfo(server_res); + errno = saved_errno; + return -1; + } + + freeaddrinfo(server_res); + return s; +} /* iperf_connect -- client to server connection function */ @@ -435,8 +467,9 @@ 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, test->bind_dev, 0, test->server_hostname, test->server_port, test->settings->connect_timeout); + // Create the control channel using an ephemeral port + test->ctrl_sck = netdial_with_tos(test->settings->domain, Ptcp, test->bind_address, test->bind_dev, 0, test->server_hostname, + test->server_port, test->settings->connect_timeout, test->settings->ctrl_tos); if (test->ctrl_sck < 0) { i_errno = IECONNECT; return -1; diff --git a/src/iperf_locale.c b/src/iperf_locale.c index eb07c9651..d878355b0 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -159,6 +159,13 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" " and client during the authentication process\n" " --use-pkcs1-padding use pkcs1 padding at your own risk\n" #endif //HAVE_SSL + " --control-tos N set the IP type of service of the control connection, 0-255.\n" + " The usual prefixes for octal and hex can be used,\n" + " i.e. 52, 064 and 0x34 all specify the same value.\n" + + " --control-dscp val set the IP dscp value of the control connection, either 0-63 or symbolic.\n" + " Numeric values can be specified in decimal,\n" + " octal and hex (see --control-tos above).\n" "Client specific:\n" " -c, --client [%%] run in client mode, connecting to \n" " (option equivalent to `--bind-dev `)\n" diff --git a/src/iperf_server_api.c b/src/iperf_server_api.c index 66401fa6b..2ffdd7238 100644 --- a/src/iperf_server_api.c +++ b/src/iperf_server_api.c @@ -129,6 +129,12 @@ iperf_server_listen(struct iperf_test *test) } } + // Set IP TOS + if (iperf_set_tos(test->listener, test->settings->ctrl_tos) < 0) { + close(test->listener); + return -1; + } + if (!test->json_output) { if (test->server_last_run_rc != 2) test->server_test_number +=1; @@ -174,6 +180,11 @@ iperf_accept(struct iperf_test *test) goto error_handling; } + // Set IP TOS + if (iperf_set_tos(test->ctrl_sck, test->settings->ctrl_tos) < 0) { + goto error_handling; + } + #if defined(HAVE_TCP_USER_TIMEOUT) int opt; if ((opt = test->settings->snd_timeout)) {