From 2442dc54c70c9e61119aa152d54280566a131157 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 6 Mar 2024 11:23:33 +0800 Subject: [PATCH] add MPTCPv1 support The Multipath TCP (MPTCP) protocol (v1 / RFC 8684) has been added in the upstream Linux kernel since v5.6. MPTCP is strongly tied to TCP, and the kernel APIs are almost the same. The only required dependency is the 'IPPROTO_MPTCP' protocol number definition, which should be provided by the netinet/in.h header if it is recent enough. This patch adds a new flag '-m' or '--mptcp' to support MPTCPv1. It can be used like this: > iperf3 -m -s > iperf3 -m -c 127.0.0.1 There is no need to check for IPPROTO_MPTCP support in configure.ac at build time, it is at runtime we will see if the kernel being use supports or not MPTCP. If IPPROTO_MPTCP is not supported by the kernel being tested, it is normal to fail because the feature is not available and the user explicitly asked to use MPTCP. Closes: https://github.com/esnet/iperf/pull/1659 Co-developed-by: Paolo Abeni Signed-off-by: Geliang Tang --- src/iperf.h | 1 + src/iperf3.1 | 4 ++++ src/iperf_api.c | 19 ++++++++++++++++++- src/iperf_locale.c | 3 +++ src/iperf_tcp.c | 22 +++++++++++++++++++--- src/net.c | 10 +++++----- src/net.h | 2 +- 7 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/iperf.h b/src/iperf.h index 527e549ed..c56a68f09 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -341,6 +341,7 @@ struct iperf_test int repeating_payload; /* --repeating-payload */ int timestamps; /* --timestamps */ char *timestamp_format; + int mptcp; /* -m, --mptcp */ char *json_output_string; /* rendered JSON output if json_output is set */ /* Select related parameters */ diff --git a/src/iperf3.1 b/src/iperf3.1 index f8eff48d2..9e425cabc 100644 --- a/src/iperf3.1 +++ b/src/iperf3.1 @@ -202,6 +202,10 @@ iperf-3.17, OAEP padding is used, however this is a breaking change that is not compatible with older iperf3 versions. Use this option to preserve the less secure, but more compatible, behavior. .TP +.BR -m ", " --mptcp " " +use mptcp variant for the current protocol. This only applies to +TCP and enables MPTCP usage. +.TP .BR -d ", " --debug " " emit debugging output. Primarily (perhaps exclusively) of use to developers. diff --git a/src/iperf_api.c b/src/iperf_api.c index 4c73e8328..c9aaceb04 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -1145,6 +1145,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(linux) + {"mptcp", no_argument, NULL, 'm'}, +#endif {"debug", optional_argument, NULL, 'd'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} @@ -1170,7 +1173,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) FILE *ptr_file; #endif /* HAVE_SSL */ - while ((flag = getopt_long(argc, argv, "p:f:i:D1VJvsc:ub:t:n:k:l:P:Rw:B:M:N46S:L:ZO:F:A:T:C:dI:hX:", longopts, NULL)) != -1) { + while ((flag = getopt_long(argc, argv, "p:f:i:D1VJvsc:ub:t:n:k:l:P:Rw:B:mM:N46S:L:ZO:F:A:T:C:dI:hX:", longopts, NULL)) != -1) { switch (flag) { case 'p': portno = atoi(optarg); @@ -1643,6 +1646,12 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) test->settings->connect_timeout = unit_atoi(optarg); client_flag = 1; break; +#if defined(linux) + case 'm': + set_protocol(test, Ptcp); + test->mptcp = 1; + break; +#endif case 'h': usage_long(stdout); exit(0); @@ -2220,6 +2229,10 @@ send_parameters(struct iperf_test *test) cJSON_AddTrueToObject(j, "reverse"); if (test->bidirectional) cJSON_AddTrueToObject(j, "bidirectional"); +#if defined(linux) + if (test->mptcp) + cJSON_AddTrueToObject(j, "mptcp"); +#endif if (test->settings->socket_bufsize) cJSON_AddNumberToObject(j, "window", test->settings->socket_bufsize); if (test->settings->blksize) @@ -2336,6 +2349,10 @@ get_parameters(struct iperf_test *test) iperf_set_test_reverse(test, 1); if ((j_p = cJSON_GetObjectItem(j, "bidirectional")) != NULL) iperf_set_test_bidirectional(test, 1); +#if defined(linux) + if ((j_p = cJSON_GetObjectItem(j, "mptcp")) != NULL) + test->mptcp = 1; +#endif if ((j_p = cJSON_GetObjectItem(j, "window")) != NULL) test->settings->socket_bufsize = j_p->valueint; if ((j_p = cJSON_GetObjectItem(j, "len")) != NULL) diff --git a/src/iperf_locale.c b/src/iperf_locale.c index 9d94e0234..5db9a3eec 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -128,6 +128,9 @@ 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(linux) + " -m, --mptcp use MPTCP rather than plain TCP\n" +#endif " -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" diff --git a/src/iperf_tcp.c b/src/iperf_tcp.c index e025515ab..40a37cb00 100644 --- a/src/iperf_tcp.c +++ b/src/iperf_tcp.c @@ -44,6 +44,10 @@ #include "net.h" #include "cjson.h" +#ifndef IPPROTO_MPTCP +#define IPPROTO_MPTCP 262 +#endif + #if defined(HAVE_FLOWLABEL) #include "flowlabel.h" #endif /* HAVE_FLOWLABEL */ @@ -183,9 +187,10 @@ iperf_tcp_listen(struct iperf_test *test) * * It's not clear whether this is a requirement or a convenience. */ - if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) { + if (test->no_delay || test->mptcp || test->settings->mss || test->settings->socket_bufsize) { struct addrinfo hints, *res; char portstr[6]; + int proto = 0; FD_CLR(s, &test->read_set); close(s); @@ -211,7 +216,12 @@ iperf_tcp_listen(struct iperf_test *test) return -1; } - if ((s = socket(res->ai_family, SOCK_STREAM, 0)) < 0) { +#if defined(linux) + if (test->mptcp) + proto = IPPROTO_MPTCP; +#endif + + if ((s = socket(res->ai_family, SOCK_STREAM, proto)) < 0) { freeaddrinfo(res); i_errno = IESTREAMLISTEN; return -1; @@ -379,8 +389,14 @@ iperf_tcp_connect(struct iperf_test *test) socklen_t optlen; int saved_errno; int rcvbuf_actual, sndbuf_actual; + int proto = 0; + +#if defined(linux) + if (test->mptcp) + proto = IPPROTO_MPTCP; +#endif - s = create_socket(test->settings->domain, SOCK_STREAM, test->bind_address, test->bind_dev, test->bind_port, test->server_hostname, test->server_port, &server_res); + s = create_socket(test->settings->domain, SOCK_STREAM, proto, test->bind_address, test->bind_dev, test->bind_port, test->server_hostname, test->server_port, &server_res); if (s < 0) { i_errno = IESTREAMCONNECT; return -1; diff --git a/src/net.c b/src/net.c index 632ae0319..a885b16bf 100644 --- a/src/net.c +++ b/src/net.c @@ -124,7 +124,7 @@ timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, /* create a socket */ int -create_socket(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, struct addrinfo **server_res_out) +create_socket(int domain, int type, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, struct addrinfo **server_res_out) { struct addrinfo hints, *local_res = NULL, *server_res = NULL; int s, saved_errno; @@ -133,14 +133,14 @@ create_socket(int domain, int proto, const char *local, const char *bind_dev, in if (local) { memset(&hints, 0, sizeof(hints)); hints.ai_family = domain; - hints.ai_socktype = proto; + hints.ai_socktype = type; if ((gerror = getaddrinfo(local, NULL, &hints, &local_res)) != 0) return -1; } memset(&hints, 0, sizeof(hints)); hints.ai_family = domain; - hints.ai_socktype = proto; + hints.ai_socktype = type; snprintf(portstr, sizeof(portstr), "%d", port); if ((gerror = getaddrinfo(server, portstr, &hints, &server_res)) != 0) { if (local) @@ -149,7 +149,7 @@ create_socket(int domain, int proto, const char *local, const char *bind_dev, in return -1; } - s = socket(server_res->ai_family, proto, 0); + s = socket(server_res->ai_family, type, proto); if (s < 0) { if (local) freeaddrinfo(local_res); @@ -239,7 +239,7 @@ netdial(int domain, int proto, const char *local, const char *bind_dev, int loca struct addrinfo *server_res = NULL; int s, saved_errno; - s = create_socket(domain, proto, local, bind_dev, local_port, server, port, &server_res); + s = create_socket(domain, proto, 0, local, bind_dev, local_port, server, port, &server_res); if (s < 0) { return -1; } diff --git a/src/net.h b/src/net.h index f0e1b4f98..1f5cc4d34 100644 --- a/src/net.h +++ b/src/net.h @@ -28,7 +28,7 @@ #define __NET_H int timeout_connect(int s, const struct sockaddr *name, socklen_t namelen, int timeout); -int create_socket(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, struct addrinfo **server_res_out); +int create_socket(int domain, int type, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, struct addrinfo **server_res_out); int netdial(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, int timeout); int netannounce(int domain, int proto, const char *local, const char *bind_dev, int port); int Nread(int fd, char *buf, size_t count, int prot);