Skip to content

Commit

Permalink
add MPTCPv1 support
Browse files Browse the repository at this point in the history
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: #1659
Co-developed-by: Paolo Abeni <[email protected]>
Signed-off-by: Geliang Tang <[email protected]>
  • Loading branch information
geliangtang authored and Geliang Tang committed Mar 12, 2024
1 parent c362e1a commit 5f71968
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 10 deletions.
1 change: 1 addition & 0 deletions src/iperf.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,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 */
Expand Down
4 changes: 4 additions & 0 deletions src/iperf3.1
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,10 @@ parameter is specified in ms, and defaults to the system settings.
This functionality depends on the TCP_USER_TIMEOUT socket option, and
will not work on systems that do not support it.
.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.
Expand Down
19 changes: 18 additions & 1 deletion src/iperf_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,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}
Expand All @@ -1169,7 +1172,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);
Expand Down Expand Up @@ -1639,6 +1642,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);
Expand Down Expand Up @@ -2216,6 +2225,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)
Expand Down Expand Up @@ -2332,6 +2345,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)
Expand Down
3 changes: 3 additions & 0 deletions src/iperf_locale.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
22 changes: 19 additions & 3 deletions src/iperf_tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -182,9 +186,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);
Expand All @@ -210,7 +215,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;
Expand Down Expand Up @@ -375,8 +385,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;
Expand Down
10 changes: 5 additions & 5 deletions src/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -133,22 +133,22 @@ 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)
freeaddrinfo(local_res);
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);
Expand Down Expand Up @@ -238,7 +238,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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 5f71968

Please sign in to comment.