From: Dorian Craps <dorian.cr...@student.vinci.be>

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 the "mptcp" global option in the config, 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,
and the application will stop.

A test has been added, it is a copy of "default_rules.vtc" in tcp-rules
with the addition of "mptcp" in the config. I'm not sure what else needs
to be tested for the moment, with this global MPTCP option.

Note: another patch is coming to support enabling MPTCP per address, but
I prefer to already send this patch, just in case, as I will soon have
less time to dedicate to this.

Due to the limited impact within a data center environment,
we have decided not to implement MPTCP between the proxy and the servers.
The high-speed, low-latency nature of data center networks reduces
the benefits of MPTCP, making the complexity of its implementation
unnecessary in this context.

Developed with the help of Matthieu Baerts (matt...@kernel.org) and
Olivier Bonaventure (olivier.bonavent...@uclouvain.be)

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]
---
 doc/configuration.txt                       |  9 +++
 include/haproxy/proto_rhttp.h               |  2 +
 reg-tests/tcp-rules/default_rules_mptcp.vtc | 65 +++++++++++++++++++++
 src/cfgparse-global.c                       | 21 ++++++-
 src/cfgparse.c                              |  3 +-
 5 files changed, 98 insertions(+), 2 deletions(-)
 create mode 100644 reg-tests/tcp-rules/default_rules_mptcp.vtc

diff --git a/doc/configuration.txt b/doc/configuration.txt
index 16094c194..cd43cd863 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -1341,6 +1341,7 @@ The following keywords are supported in the "global" 
section :
    - maxsslconn
    - maxsslrate
    - maxzlibmem
+   - mptcp
    - no-memory-trimming
    - noepoll
    - noevports
@@ -2958,6 +2959,14 @@ maxzlibmem <number>
   with "show info" on the line "MaxZlibMemUsage", the memory used by zlib is
   "ZlibMemUsage" in bytes.
 
+mptcp
+  Uses MPTCP instead of TCP. Multipath TCP or MPTCP is an extension to the
+  standard TCP and is described in RFC 8684. It allows a device to make use of
+  multiple interfaces at once to send and receive TCP packets over a single
+  MPTCP connection. MPTCP can aggregate the bandwidth of multiple interfaces or
+  prefer the one with the lowest latency, it also allows a fail-over if one 
path
+  is down, and the traffic is seamlessly reinjected on other paths.
+
 no-memory-trimming
   Disables memory trimming ("malloc_trim") at a few moments where attempts are
   made to reclaim lots of memory (on memory shortage or on reload). Trimming
diff --git a/include/haproxy/proto_rhttp.h b/include/haproxy/proto_rhttp.h
index 421680fe5..80d2d6448 100644
--- a/include/haproxy/proto_rhttp.h
+++ b/include/haproxy/proto_rhttp.h
@@ -5,6 +5,8 @@
 #include <haproxy/listener-t.h>
 #include <haproxy/receiver-t.h>
 
+extern struct protocol proto_rhttp;
+
 int rhttp_bind_receiver(struct receiver *rx, char **errmsg);
 
 int rhttp_bind_listener(struct listener *listener, char *errmsg, int errlen);
diff --git a/reg-tests/tcp-rules/default_rules_mptcp.vtc 
b/reg-tests/tcp-rules/default_rules_mptcp.vtc
new file mode 100644
index 000000000..485d56a73
--- /dev/null
+++ b/reg-tests/tcp-rules/default_rules_mptcp.vtc
@@ -0,0 +1,65 @@
+varnishtest "Test declaration of TCP rules in default sections with mptcp in 
the config"
+
+feature cmd "$HAPROXY_PROGRAM -cc 'version_atleast(2.5-dev0)'"
+feature cmd "test \"$(cat /proc/sys/net/mptcp/enabled)\" = 1"
+feature ignore_unknown_macro
+
+server s1 {
+        rxreq
+        txresp
+        expect req.http.x-test1-frt == "def_front"
+        expect req.http.x-test1-bck == "def_back"
+}  -start
+
+haproxy h1 -conf {
+  global
+      mptcp
+
+  defaults common
+      mode http
+      timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+      timeout client  "${HAPROXY_TEST_TIMEOUT-5s}"
+      timeout server  "${HAPROXY_TEST_TIMEOUT-5s}"
+
+  defaults def_front from common
+      tcp-request connection accept
+      tcp-request session accept
+      tcp-request inspect-delay 5s
+      tcp-request content set-var(txn.test1) "str(def_front)"
+      tcp-request content accept
+
+  defaults def_back from common
+      tcp-request inspect-delay 5s
+      tcp-request content set-var(txn.test1) "str(def_back)"
+      tcp-request content accept
+
+      tcp-response inspect-delay 5s
+      tcp-response content set-var(txn.test2) "str(def_back)"
+      tcp-response content accept
+
+  frontend fe from def_front
+      bind "fd@${feh1}"
+      tcp-request connection reject
+      tcp-request session reject
+      tcp-request content reject
+
+      http-request set-header x-test1-frt "%[var(txn.test1)]"
+
+      default_backend be
+
+  backend be from def_back
+      tcp-response content reject
+
+      http-request set-header x-test1-bck "%[var(txn.test1)]"
+      http-response set-header x-test2 "%[var(txn.test2)]"
+
+      server s1 ${s1_addr}:${s1_port}
+
+} -start
+
+client c1 -connect ${h1_feh1_sock} {
+        txreq -req GET -url /
+        rxresp
+        expect resp.status == 200
+        expect resp.http.x-test2 == "def_back"
+} -run
diff --git a/src/cfgparse-global.c b/src/cfgparse-global.c
index b173511c9..0feccd4b2 100644
--- a/src/cfgparse-global.c
+++ b/src/cfgparse-global.c
@@ -23,6 +23,8 @@
 #include <haproxy/log.h>
 #include <haproxy/peers.h>
 #include <haproxy/protocol.h>
+#include <haproxy/proto_rhttp.h>
+#include <haproxy/proto_tcp.h>
 #include <haproxy/tools.h>
 
 int cluster_secret_isset;
@@ -52,7 +54,7 @@ static const char *common_kw_list[] = {
        "presetenv", "unsetenv", "resetenv", "strict-limits", "localpeer",
        "numa-cpu-mapping", "defaults", "listen", "frontend", "backend",
        "peers", "resolvers", "cluster-secret", "no-quic", "limited-quic",
-       NULL /* must be last */
+       "mptcp", NULL /* must be last */
 };
 
 /*
@@ -1334,6 +1336,23 @@ int cfg_parse_global(const char *file, int linenum, char 
**args, int kwm)
                        HA_ATOMIC_STORE(&global.anon_key, tmp);
                }
        }
+       else if (strcmp(args[0], "mptcp") == 0) {
+               if (alertif_too_many_args(0, file, linenum, args, &err_code))
+                       goto out;
+#ifdef __linux__
+#ifndef IPPROTO_MPTCP
+#define IPPROTO_MPTCP 262
+#endif
+               proto_tcpv4.sock_prot = IPPROTO_MPTCP;
+               proto_tcpv6.sock_prot = IPPROTO_MPTCP;
+               proto_rhttp.sock_prot = IPPROTO_MPTCP;
+#else
+               ha_alert("parsing [%s:%d]: '%s' is only supported on Linux.\n",
+                        file, linenum, args[0]);
+               err_code |= ERR_ALERT | ERR_FATAL;
+               goto out;
+#endif
+       }
        else {
                struct cfg_kw_list *kwl;
                const char *best;
diff --git a/src/cfgparse.c b/src/cfgparse.c
index a2f7c4a74..2f3ff99bc 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -77,6 +77,7 @@
 #include <haproxy/peers.h>
 #include <haproxy/pool.h>
 #include <haproxy/protocol.h>
+#include <haproxy/proto_tcp.h>
 #include <haproxy/proxy.h>
 #include <haproxy/resolvers.h>
 #include <haproxy/sample.h>
@@ -1193,7 +1194,7 @@ int cfg_parse_mailers(const char *file, int linenum, char 
**args, int kwm)
                        goto out;
                }
 
-               if (proto->sock_prot != IPPROTO_TCP) {
+               if (proto->sock_prot != proto_tcpv4.sock_prot) {
                        ha_alert("parsing [%s:%d] : '%s %s' : TCP not supported 
for this address family.\n",
                                 file, linenum, args[0], args[1]);
                        err_code |= ERR_ALERT | ERR_FATAL;
-- 
2.34.1


Reply via email to