From 0906bf93fb754ed27e347b7cdb6e5fcf60e71d7c Mon Sep 17 00:00:00 2001 From: Dorian Craps Date: Thu, 4 Apr 2024 11:42:50 +0200 Subject: [PATCH] socket: add MPTCP support Multipath TCP (MPTCP), standardized in RFC8684 [1], is a TCP extension that enables a TCP connection to use different paths. Multipath TCP has been used for several use cases. On smartphones, MPTCP enables seamless handovers between cellular and Wi-Fi networks while preserving established connections. This use-case is what pushed Apple to use MPTCP since 2013 in multiple applications [2]. On dual-stack hosts, Multipath TCP enables the TCP connection to automatically use the best performing path, either IPv4 or IPv6. If one path fails, MPTCP automatically uses the other path. To benefit from MPTCP, both the client and the server have to support it. Multipath TCP is a backward-compatible TCP extension that is enabled by default on recent Linux distributions (Debian, Ubuntu, Redhat, ...). Multipath TCP is included in the Linux kernel since version 5.6 [3]. To use it on Linux, an application must explicitly enable it when creating the socket. No need to change anything else in the application. This attached patch adds an --mptcp option which allows the creation of an MPTCP socket instead of TCP on Linux. If Multipath TCP is not supported on the system, an error will be reported. It is important to note that if the end server doesn't support MPTCP, the connection will continue after a seamless fallback to TCP. Link: https://www.rfc-editor.org/rfc/rfc8684.html [1] Link: https://www.tessares.net/apples-mptcp-story-so-far/ [2] Link: https://www.mptcp.dev [3] Co-developed-by: Dorian Craps (@CrapsDorian) Co-developed-by: Olivier Bonaventure (@obonaventure) Co-developed-by: Matthieu Baerts (@matttbe) Signed-off-by: Dorian Craps --- .github/scripts/spellcheck.words | 2 + docs/cmdline-opts/Makefile.inc | 1 + docs/cmdline-opts/mptcp.md | 41 ++++++++++++ docs/libcurl/curl_easy_setopt.md | 4 ++ docs/libcurl/opts/CURLOPT_MPTCP.md | 78 +++++++++++++++++++++++ docs/libcurl/opts/CURLOPT_TCP_FASTOPEN.md | 1 + docs/libcurl/opts/Makefile.inc | 1 + docs/libcurl/symbols-in-versions | 1 + docs/options-in-versions | 1 + include/curl/curl.h | 3 + lib/cf-socket.c | 17 +++++ lib/easyoptions.c | 3 +- lib/setopt.c | 3 + lib/urldata.h | 2 + src/tool_cfgable.h | 1 + src/tool_getparam.c | 5 ++ src/tool_listhelp.c | 3 + src/tool_operate.c | 3 + 18 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 docs/cmdline-opts/mptcp.md create mode 100644 docs/libcurl/opts/CURLOPT_MPTCP.md diff --git a/.github/scripts/spellcheck.words b/.github/scripts/spellcheck.words index 5e534846c815bf..ca544c8056b4db 100644 --- a/.github/scripts/spellcheck.words +++ b/.github/scripts/spellcheck.words @@ -502,6 +502,7 @@ monospace MorphOS MPE MPL +MPTCP mprintf MQTT mqtt @@ -522,6 +523,7 @@ mTLS MUA multicwd multiparts +multipath MultiSSL mumbo musedev diff --git a/docs/cmdline-opts/Makefile.inc b/docs/cmdline-opts/Makefile.inc index deb4c7c326dc1c..75855f54572b81 100644 --- a/docs/cmdline-opts/Makefile.inc +++ b/docs/cmdline-opts/Makefile.inc @@ -161,6 +161,7 @@ DPAGES = \ max-redirs.md \ max-time.md \ metalink.md \ + mptcp.md \ negotiate.md \ netrc-file.md \ netrc-optional.md \ diff --git a/docs/cmdline-opts/mptcp.md b/docs/cmdline-opts/mptcp.md new file mode 100644 index 00000000000000..37a5281f89d6db --- /dev/null +++ b/docs/cmdline-opts/mptcp.md @@ -0,0 +1,41 @@ +--- +c: Copyright (C) Dorian Craps, +SPDX-License-Identifier: curl +Long: mptcp +Added: 8.8.0 +Help: Enable Multipath TCP (MPTCP) +Category: connection +Multi: boolean +See-also: + - tcp-fastopen +Example: + - --mptcp $URL +--- + +# `--mptcp` + +Enables the use of Multipath TCP (MPTCP) for connections. MPTCP is an extension +to the standard TCP that allows multiple TCP streams over different network +paths between the same source and destination. This can enhance bandwidth and +improve reliability by using multiple paths simultaneously. + +MPTCP is beneficial in networks where multiple paths exist between clients and +servers, such as mobile networks where a device may switch between WiFi and +cellular data or in wired networks with multiple Internet Service Providers. + +## Usage + +To use MPTCP for your connections, add the `--mptcp` option when using `curl'. + +## Requirements + +This feature is currently only supported on Linux starting from kernel 5.6. Only +TCP connections are modified, hence this option does not effect HTTP/3 (QUIC) +connections. + +The server you are connecting to must also support MPTCP. If not, the connection +seamlessly falls back to TCP. + +## Availability + +The `--mptcp` option is available starting from `curl` version 8.8.0. diff --git a/docs/libcurl/curl_easy_setopt.md b/docs/libcurl/curl_easy_setopt.md index b80d0b6640efb9..32155c5ead4bbb 100644 --- a/docs/libcurl/curl_easy_setopt.md +++ b/docs/libcurl/curl_easy_setopt.md @@ -425,6 +425,10 @@ Path to a Unix domain socket. See CURLOPT_UNIX_SOCKET_PATH(3) Path to an abstract Unix domain socket. See CURLOPT_ABSTRACT_UNIX_SOCKET(3) +## CURLOPT_MPTCP + +Enable Multipath TCP (MPTCP). See CURLOPT_MPTCP(3) + # NAMES and PASSWORDS OPTIONS (Authentication) ## CURLOPT_NETRC diff --git a/docs/libcurl/opts/CURLOPT_MPTCP.md b/docs/libcurl/opts/CURLOPT_MPTCP.md new file mode 100644 index 00000000000000..9d0ffa3224e980 --- /dev/null +++ b/docs/libcurl/opts/CURLOPT_MPTCP.md @@ -0,0 +1,78 @@ +--- +c: Copyright (C) Dorian Craps, , et al. +SPDX-License-Identifier: curl +Title: CURLOPT_MPTCP +Section: 3 +Source: libcurl +See-also: + - CURLOPT_TCP_FASTOPEN (3) +Protocol: + - All +--- + +# NAME + +CURLOPT_MPTCP - enable Multipath TCP + +# SYNOPSIS + +~~~c +#include + +CURLcode curl_easy_setopt(CURL *handle, CURLOPT_MPTCP, long enable); +~~~ + +# DESCRIPTION + +Pass a long as parameter set to 1L to enable or 0 to disable. + +MPTCP is an extension to the standard TCP that allows multiple TCP streams +over different network paths between the same source and destination. +This can enhance bandwidth and improve reliability by using multiple paths +simultaneously. +MPTCP is beneficial in networks where multiple paths exist between clients +and servers, such as mobile networks where a device may switch between WiFi +and cellular data or in wired networks with multiple Internet Service Providers. + +Enabling MPTCP can improve the performance and reliability of network requests, +particularly in environments where multiple network paths (e.g., WiFi and +cellular) are available. + +MPTCP support depends on the underlying operating system and network +infrastructure. Some networks might drop unknown MPTCP, and its effectiveness +varies based on the network configuration and conditions. If MPTCP is not +supported by the network or the end server, the connection seamlessly falls back +to TCP. + +# DEFAULT + +0 + +# EXAMPLE + +~~~c +int main(void) +{ + CURL *curl = curl_easy_init(); + if(curl) { + curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); + curl_easy_setopt(curl, CURLOPT_MPTCP, 1L); + curl_easy_perform(curl); + } +} +~~~ + +# AVAILABILITY + +Support for MPTCP in libcurl requires Linux 5.6 or later. Only TCP connections +are modified, hence this option does not effect HTTP/3 (QUIC) connections. + +The features availability in libcurl can also depend on the version of libcurl. +Added in 8.8.0. + +# RETURN VALUE + +Returns CURLE_OK if MPTCP is successfully enabled for the connection, +otherwise returns an error code specific to the reason it could not be enabled, +which might include lack of operating system support or libcurl not being built +with MPTCP support. diff --git a/docs/libcurl/opts/CURLOPT_TCP_FASTOPEN.md b/docs/libcurl/opts/CURLOPT_TCP_FASTOPEN.md index 42b44a5674dd69..41d9835b78d2c0 100644 --- a/docs/libcurl/opts/CURLOPT_TCP_FASTOPEN.md +++ b/docs/libcurl/opts/CURLOPT_TCP_FASTOPEN.md @@ -6,6 +6,7 @@ Section: 3 Source: libcurl See-also: - CURLOPT_SSL_FALSESTART (3) + - CURLOPT_MPTCP (3) Protocol: - All --- diff --git a/docs/libcurl/opts/Makefile.inc b/docs/libcurl/opts/Makefile.inc index bf5ea7645faac5..5cefb148a031b3 100644 --- a/docs/libcurl/opts/Makefile.inc +++ b/docs/libcurl/opts/Makefile.inc @@ -245,6 +245,7 @@ man_MANS = \ CURLOPT_MAXREDIRS.3 \ CURLOPT_MIME_OPTIONS.3 \ CURLOPT_MIMEPOST.3 \ + CURLOPT_MPTCP.3 \ CURLOPT_NETRC.3 \ CURLOPT_NETRC_FILE.3 \ CURLOPT_NEW_DIRECTORY_PERMS.3 \ diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index e5531df92cff01..40a5c8d19483e8 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -710,6 +710,7 @@ CURLOPT_MAXREDIRS 7.5 CURLOPT_MIME_OPTIONS 7.81.0 CURLOPT_MIMEPOST 7.56.0 CURLOPT_MUTE 7.1 7.8 7.15.5 +CURLOPT_MPTCP 8.8.0 CURLOPT_NETRC 7.1 CURLOPT_NETRC_FILE 7.11.0 CURLOPT_NEW_DIRECTORY_PERMS 7.16.4 diff --git a/docs/options-in-versions b/docs/options-in-versions index d3513ff119d7f6..816436754c34a7 100644 --- a/docs/options-in-versions +++ b/docs/options-in-versions @@ -126,6 +126,7 @@ --max-redirs 7.5 --max-time (-m) 4.0 --metalink 7.27.0 +--mptcp 8.8.0 --negotiate 7.10.6 --netrc (-n) 4.6 --netrc-file 7.21.5 diff --git a/include/curl/curl.h b/include/curl/curl.h index 25b3836b4e5275..31427740994e12 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -2213,6 +2213,9 @@ typedef enum { /* set ECH configuration */ CURLOPT(CURLOPT_ECH, CURLOPTTYPE_STRINGPOINT, 325), + /* Set MPTCP */ + CURLOPT(CURLOPT_MPTCP, CURLOPTTYPE_LONG, 326), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/lib/cf-socket.c b/lib/cf-socket.c index eeae5f9950c542..bf458b4145fb98 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -244,6 +244,13 @@ void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest, dest->socktype = SOCK_STREAM; dest->protocol = IPPROTO_IP; break; + case TRNSPRT_MPTCP: +#ifndef IPPROTO_MPTCP +#define IPPROTO_MPTCP 262 +#endif + dest->socktype = SOCK_STREAM; + dest->protocol = IPPROTO_MPTCP; + break; default: /* UDP and QUIC */ dest->socktype = SOCK_DGRAM; dest->protocol = IPPROTO_UDP; @@ -1602,6 +1609,16 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } + + if(data->set.mptcp) { +#ifdef __linux__ + transport = TRNSPRT_MPTCP; +#else + result = CURLE_UNSUPPORTED_PROTOCOL; + goto out; +#endif + } + cf_socket_ctx_init(ctx, ai, transport); result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx); diff --git a/lib/easyoptions.c b/lib/easyoptions.c index c79d136707e4d4..85fe36528904bf 100644 --- a/lib/easyoptions.c +++ b/lib/easyoptions.c @@ -179,6 +179,7 @@ struct curl_easyoption Curl_easyopts[] = { {"MAX_SEND_SPEED_LARGE", CURLOPT_MAX_SEND_SPEED_LARGE, CURLOT_OFF_T, 0}, {"MIMEPOST", CURLOPT_MIMEPOST, CURLOT_OBJECT, 0}, {"MIME_OPTIONS", CURLOPT_MIME_OPTIONS, CURLOT_LONG, 0}, + {"MPTCP", CURLOPT_MPTCP, CURLOT_LONG, 0}, {"NETRC", CURLOPT_NETRC, CURLOT_VALUES, 0}, {"NETRC_FILE", CURLOPT_NETRC_FILE, CURLOT_STRING, 0}, {"NEW_DIRECTORY_PERMS", CURLOPT_NEW_DIRECTORY_PERMS, CURLOT_LONG, 0}, @@ -376,6 +377,6 @@ struct curl_easyoption Curl_easyopts[] = { */ int Curl_easyopts_check(void) { - return ((CURLOPT_LASTENTRY%10000) != (325 + 1)); + return ((CURLOPT_LASTENTRY%10000) != (326 + 1)); } #endif diff --git a/lib/setopt.c b/lib/setopt.c index f719cc43dde297..d5d2091ee2e8d5 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2940,6 +2940,9 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) result = CURLE_NOT_BUILT_IN; #endif break; + case CURLOPT_MPTCP: + data->set.mptcp = TRUE; + break; case CURLOPT_SSL_ENABLE_NPN: break; case CURLOPT_SSL_ENABLE_ALPN: diff --git a/lib/urldata.h b/lib/urldata.h index 8bccffb0f59b20..55a5be97103c0b 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -801,6 +801,7 @@ struct ldapconninfo; #define TRNSPRT_UDP 4 #define TRNSPRT_QUIC 5 #define TRNSPRT_UNIX 6 +#define TRNSPRT_MPTCP 7 /* * The connectdata struct contains all fields and variables that should be @@ -1876,6 +1877,7 @@ struct UserDefined { #ifdef USE_ECH int tls_ech; /* TLS ECH configuration */ #endif + BIT(mptcp); /* enable MPTCP support */ }; #ifndef CURL_DISABLE_MIME diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index 74d0c45f2ed121..5b930279de5c6f 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -292,6 +292,7 @@ struct OperationConfig { CLOBBER_NEVER, /* If the file exists, always fail */ CLOBBER_ALWAYS /* If the file exists, always overwrite it */ } file_clobber_mode; + bool mptcp; /* enable MPTCP support */ struct GlobalConfig *global; struct OperationConfig *prev; struct OperationConfig *next; /* Always last in the struct */ diff --git a/src/tool_getparam.c b/src/tool_getparam.c index f56981a743d868..9f9ba0819483f4 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -200,6 +200,7 @@ typedef enum { C_MAX_REDIRS, C_MAX_TIME, C_METALINK, + C_MPTCP, C_NEGOTIATE, C_NETRC, C_NETRC_FILE, @@ -482,6 +483,7 @@ static const struct LongShort aliases[]= { {"max-redirs", ARG_STRG, ' ', C_MAX_REDIRS}, {"max-time", ARG_STRG, 'm', C_MAX_TIME}, {"metalink", ARG_BOOL, ' ', C_METALINK}, + {"mptcp", ARG_BOOL, ' ', C_MPTCP}, {"negotiate", ARG_BOOL, ' ', C_NEGOTIATE}, {"netrc", ARG_BOOL, 'n', C_NETRC}, {"netrc-file", ARG_FILE, ' ', C_NETRC_FILE}, @@ -2794,6 +2796,9 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ } } break; + case C_MPTCP: /* --mptcp */ + config->mptcp = TRUE; + break; default: /* unknown flag */ err = PARAM_OPTION_UNKNOWN; break; diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c index 8429322045fa79..e0228ee513215f 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c @@ -381,6 +381,9 @@ const struct helptxt helptext[] = { {" --metalink", "Process given URLs as metalink XML file", CURLHELP_MISC}, + {" --mptcp", + "Enable Multipath TCP (MPTCP)", + CURLHELP_CONNECTION}, {" --negotiate", "Use HTTP Negotiate (SPNEGO) authentication", CURLHELP_AUTH | CURLHELP_HTTP}, diff --git a/src/tool_operate.c b/src/tool_operate.c index 9f14b3e5860353..437059a9f24123 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -1291,6 +1291,9 @@ static CURLcode single_transfer(struct GlobalConfig *global, if(config->tcp_fastopen) my_setopt(curl, CURLOPT_TCP_FASTOPEN, 1L); + if(config->mptcp) + my_setopt(curl, CURLOPT_MPTCP, 1L); + /* where to store */ my_setopt(curl, CURLOPT_WRITEDATA, per); my_setopt(curl, CURLOPT_INTERLEAVEDATA, per);