From 5f0e4e89229c25f3d910ff443675b37ce75598d9 Mon Sep 17 00:00:00 2001 From: Liam McBirnie Date: Tue, 16 Dec 2025 17:13:51 +1000 Subject: [PATCH] Add option to set the ToS of the control connection. --- docs/invoking.rst | 10 +++++++++ src/iperf.h | 1 + src/iperf_api.c | 46 ++++++++++++++++++++++++++++++++++++++++++ src/iperf_api.h | 8 ++++++++ src/iperf_client_api.c | 37 +++++++++++++++++++++++++++++++-- src/iperf_locale.c | 7 +++++++ src/iperf_server_api.c | 11 ++++++++++ 7 files changed, 118 insertions(+), 2 deletions(-) diff --git a/docs/invoking.rst b/docs/invoking.rst index f498ce05c..04f82b49a 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 1371abdf0..b227ef01f 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -170,6 +170,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 b41368709..2c536c09e 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1135,6 +1135,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'}, @@ -1505,6 +1507,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; @@ -5487,6 +5505,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 9d783a316..cb68e3e47 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -106,6 +106,8 @@ typedef atomic_uint_fast64_t atomic_iperf_size_t; #define OPT_SKIP_RX_COPY 32 #define OPT_JSON_STREAM_FULL_OUTPUT 33 #define OPT_SERVER_MAX_DURATION 34 +#define OPT_CONTROL_TOS 35 +#define OPT_CONTROL_DSCP 36 /* states */ #define TEST_START 1 @@ -319,6 +321,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 37d741f87..31d171898 100644 --- a/src/iperf_client_api.c +++ b/src/iperf_client_api.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -409,6 +410,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 */ @@ -430,8 +462,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 78df64d73..89c6185ee 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 e9313244f..a0a7f0e5c 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)) {