From 7bdb349247624b2549482c5577f165336ec91212 Mon Sep 17 00:00:00 2001 From: doriancraps Date: Thu, 4 Apr 2024 11:33:19 +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 Co-developed-by: Olivier Bonaventure Co-developed-by: Matthieu Baerts Signed-off-by: Dorian Craps --- .github/scripts/spellcheck.words | 2 + docs/cmdline-opts/Makefile.inc | 1 + docs/cmdline-opts/mptcp.md | 38 ++++++++++++ docs/libcurl/curl_easy_setopt.md | 4 ++ docs/libcurl/opts/CURLOPT_MPTCP.md | 76 +++++++++++++++++++++++ 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 | 13 ++++ 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, 160 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 050513c76f2769..552858573f165d 100644 --- a/.github/scripts/spellcheck.words +++ b/.github/scripts/spellcheck.words @@ -485,6 +485,7 @@ monospace MorphOS MPE MPL +MPTCP mprintf MQTT mqtt @@ -505,6 +506,7 @@ mTLS MUA multicwd multiparts +multipath MultiSSL mumbo musedev diff --git a/docs/cmdline-opts/Makefile.inc b/docs/cmdline-opts/Makefile.inc index 428cc3bab2a066..c546a18c8f89d0 100644 --- a/docs/cmdline-opts/Makefile.inc +++ b/docs/cmdline-opts/Makefile.inc @@ -160,6 +160,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..2c36dca08748e9 --- /dev/null +++ b/docs/cmdline-opts/mptcp.md @@ -0,0 +1,38 @@ +--- +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. +- The server you are connecting to must also support MPTCP. If not, the + connection seamlessly fall*s* backto 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 770fdc91bc7d69..fcd3fd13915cf0 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..207775b4a38e91 --- /dev/null +++ b/docs/libcurl/opts/CURLOPT_MPTCP.md @@ -0,0 +1,76 @@ +--- +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. + +Note: 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 fall*s* 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. +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 7a292b81d5189d..f1322a45c1d8a7 100644 --- a/docs/libcurl/opts/Makefile.inc +++ b/docs/libcurl/opts/Makefile.inc @@ -244,6 +244,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 a627dd68b6c37d..3cc394be76860b 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -708,6 +708,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 0905809439c35b..0e30c563da059f 100644 --- a/docs/options-in-versions +++ b/docs/options-in-versions @@ -125,6 +125,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 b2377b7895b988..7ab968c6572768 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -2206,6 +2206,9 @@ typedef enum { /* millisecond version */ CURLOPT(CURLOPT_SERVER_RESPONSE_TIMEOUT_MS, CURLOPTTYPE_LONG, 324), + /* Set MPTCP */ + CURLOPT(CURLOPT_MPTCP, CURLOPTTYPE_LONG, 325), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/lib/cf-socket.c b/lib/cf-socket.c index a5feb6b95ab2b6..a852a523193115 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,12 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } + +#ifdef __linux__ + if(data->set.mptcp) + transport = TRNSPRT_MPTCP; +#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 9c4438a100e2bc..ea284970d33b38 100644 --- a/lib/easyoptions.c +++ b/lib/easyoptions.c @@ -178,6 +178,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}, @@ -375,6 +376,6 @@ struct curl_easyoption Curl_easyopts[] = { */ int Curl_easyopts_check(void) { - return ((CURLOPT_LASTENTRY%10000) != (324 + 1)); + return ((CURLOPT_LASTENTRY%10000) != (325 + 1)); } #endif diff --git a/lib/setopt.c b/lib/setopt.c index 8a5a5d7c33d21d..2226ea553d79c0 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2931,6 +2931,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 ce28f25bba59e9..185e2442307430 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -788,6 +788,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 @@ -1811,6 +1812,7 @@ struct UserDefined { #ifdef USE_WEBSOCKETS BIT(ws_raw_mode); #endif + BIT(mptcp); /* enable MPTCP support */ }; #ifndef CURL_DISABLE_MIME diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index dfa74d81ff4b81..534467a27404c7 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 5cb898852b5e46..d1b4e2b8688081 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -199,6 +199,7 @@ typedef enum { C_MAX_REDIRS, C_MAX_TIME, C_METALINK, + C_MPTCP, C_NEGOTIATE, C_NETRC, C_NETRC_FILE, @@ -480,6 +481,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}, @@ -2747,6 +2749,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 5d9364405448bf..1a2d98ccfecbb1 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c @@ -378,6 +378,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 49ec7d835cfedb..849c8985860ff3 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);