Skip to content

Commit

Permalink
Add Multipath TCP (MPTCP) support (Proxy)
Browse files Browse the repository at this point in the history
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.

Adding the possibility to create MPTCP sockets would thus
be a really fine addition to httpd, by allowing clients
to make use of their different interfaces.

This patch introduces the possibilty to connect to backend
servers using MPTCP. Note however that these changes are
only available on Linux, as IPPROTO_MPTCP is Linux specific
for the time being.

For proxies, we can connect using MPTCP by passing the
"multipathtcp" parameter:

ProxyPass "/example" "http://backend.example.com" multipathtcp=On

We then store this information in the worker and create sockets
appropriately according to this value.

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]
  • Loading branch information
Aperence authored and notroj committed Aug 28, 2024
1 parent 92fe2ba commit 0943b86
Show file tree
Hide file tree
Showing 5 changed files with 25 additions and 2 deletions.
4 changes: 4 additions & 0 deletions docs/manual/mod/mod_proxy.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1326,6 +1326,10 @@ ProxyPass "/example" "http://backend.example.com" max=20 ttl=120 retry=300
<td><p>TTL in seconds for how long DNS resolutions of the backend address are cached.
-1 means until restart of Apache httpd.</p>
</td></tr>
<tr><td><a id="multipathtcp" name="multipathtcp">multipathtcp</a></td>
<td>Off</td>
<td><p>Enable/disable the use of <a href="https://mptcp.dev">Multipath TCP (MPTCP)</a></p>
</td></tr>

</table>

Expand Down
12 changes: 12 additions & 0 deletions modules/proxy/mod_proxy.c
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,18 @@ static const char *set_worker_param(apr_pool_t *p,
worker->s->response_field_size = (s ? s : HUGE_STRING_LEN);
worker->s->response_field_size_set = 1;
}
else if (!strcasecmp(key, "multipathtcp")) {
#ifdef IPPROTO_MPTCP
if (!strcasecmp(val, "On"))
worker->s->sock_proto = IPPROTO_MPTCP;
else if (!strcasecmp(val, "Off"))
worker->s->sock_proto = APR_PROTO_TCP;
else
return "multipathtcp must be On|Off";
#else
return "multipathtcp is not supported on your platform";
#endif
}
else {
if (set_worker_hc_param_f) {
return set_worker_hc_param_f(p, s, worker, key, val, NULL);
Expand Down
3 changes: 3 additions & 0 deletions modules/proxy/mod_proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ typedef struct {
unsigned int ppinherit_set:1;
unsigned int map_encoded_one:1;
unsigned int map_encoded_all:1;

int sock_proto;
} proxy_server_conf;

typedef struct {
Expand Down Expand Up @@ -497,6 +499,7 @@ typedef struct {
unsigned int address_ttl_set:1;
apr_int32_t address_ttl; /* backend address' TTL (seconds) */
apr_uint32_t address_expiry; /* backend address' next expiry time */
int sock_proto; /* The protocol to use to create the socket */
} proxy_worker_shared;

#define ALIGNED_PROXY_WORKER_SHARED_SIZE (APR_ALIGN_DEFAULT(sizeof(proxy_worker_shared)))
Expand Down
3 changes: 3 additions & 0 deletions modules/proxy/mod_proxy_connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,9 @@ static int proxy_connect_handler(request_rec *r, proxy_worker *worker,
* For now we do nothing, ie we get DNS round robin.
* XXX FIXME
*/

/* set the socket protocol to use */
conf->sock_proto = worker->s->sock_proto;
failed = ap_proxy_connect_to_backend(&sock, "CONNECT", nexthop,
connectname, conf, r);

Expand Down
5 changes: 3 additions & 2 deletions modules/proxy/proxy_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -2697,7 +2697,8 @@ PROXY_DECLARE(int) ap_proxy_connect_to_backend(apr_socket_t **newsock,

while (backend_addr && !connected) {
if ((rv = apr_socket_create(newsock, backend_addr->family,
SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) {
SOCK_STREAM, conf->sock_proto,
r->pool)) != APR_SUCCESS) {
loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
ap_log_rerror(APLOG_MARK, loglevel, rv, r, APLOGNO(00935)
"%s: error creating fam %d socket for target %s",
Expand Down Expand Up @@ -3858,7 +3859,7 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const char *proxy_function,
#endif
{
if ((rv = apr_socket_create(&newsock, backend_addr->family,
SOCK_STREAM, APR_PROTO_TCP,
SOCK_STREAM, worker->s->sock_proto,
conn->scpool)) != APR_SUCCESS) {
loglevel = backend_addr->next ? APLOG_DEBUG : APLOG_ERR;
ap_log_error(APLOG_MARK, loglevel, rv, s, APLOGNO(00952)
Expand Down

0 comments on commit 0943b86

Please sign in to comment.