From a0ca7a0ed4d7fb32af011aa2af955b5db14ef5e4 Mon Sep 17 00:00:00 2001 From: Olivier Bonaventure Date: Mon, 5 Sep 2022 12:13:42 +0200 Subject: [PATCH] Initial support for Multipath TCP on recent Linux kernels --- configure.ac | 46 ++++++++++++++++++++++++++++++++ src/openvpn/options.c | 20 ++++++++++++++ src/openvpn/options.h | 3 +++ src/openvpn/ps.c | 12 +++++++++ src/openvpn/socket.c | 62 +++++++++++++++++++++++++++++++++++++++++-- src/openvpn/socket.h | 7 +++++ 6 files changed, 148 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index be31889ec2b..1d570802f14 100644 --- a/configure.ac +++ b/configure.ac @@ -286,6 +286,12 @@ AC_ARG_WITH( [with_openssl_engine="auto"] ) +AC_ARG_WITH(mptcp, + [AS_HELP_STRING([--without-mptcp],[Disable Multipath TCP support])], + [enable_mptcp=no], + [enable_mptcp=yes] +) + AC_ARG_VAR([PLUGINDIR], [Path of plug-in directory @<:@default=LIBDIR/openvpn/plugins@:>@]) if test -n "${PLUGINDIR}"; then plugindir="${PLUGINDIR}" @@ -820,6 +826,46 @@ case "$host" in esac +dnl +dnl Checking Multipath TCP support on Linux +dnl +case "$host" in + *-*-linux*) + AC_MSG_CHECKING([Multipath TCP support ]) + AS_IF([test "x$enable_mptcp" != xno], + [AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ + #include + #include + #include + #include + #ifndef IPPROTO_MPTCP + #define IPPROTO_MPTCP 262 + #endif + int x=0; + ]], + [[ + int s= socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP); + if(s!=-1) + { + close(s); + return(0); + } + else + { + return(-1); + } + ]] + ) ], + [AC_DEFINE([ENABLE_MPTCP], [1], + [AC_MSG_RESULT([Multipath TCP is enabled on this system])] )], + [ AC_MSG_RESULT([Multipath TCP is not enabled. On Linux, you need a kernel >= 5.15 and ensure that sysctl.net.mptcp_enabled is set to 1]) ], + ) + ]) + ;; +esac + + + if test "${with_crypto_library}" = "openssl"; then AC_ARG_VAR([OPENSSL_CFLAGS], [C compiler flags for OpenSSL]) AC_ARG_VAR([OPENSSL_LIBS], [linker flags for OpenSSL]) diff --git a/src/openvpn/options.c b/src/openvpn/options.c index a296086d4f0..9bd38b445c3 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -137,6 +137,9 @@ static const char usage_message[] = " udp6, tcp6-server, tcp6-client\n" "--proto-force p : only consider protocol p in list of connection profiles.\n" " p = udp or tcp\n" +#if defined(TARGET_LINUX) && defined(ENABLE_MPTCP) + "--multipath : Enable Multipath TCP on the TCP connections.\n" +#endif "--connect-retry n [m] : For client, number of seconds to wait between\n" " connection retries (default=%d). On repeated retries\n" " the wait time is exponentially increased to a maximum of m\n" @@ -903,6 +906,11 @@ init_options(struct options *o, const bool init_gc) } #endif /* _WIN32 */ o->allow_recursive_routing = false; + +#if defined(TARGET_LINUX) && defined(ENABLE_MPTCP) + o->enable_multipath = false; +#endif + } void @@ -9285,6 +9293,18 @@ add_option(struct options *options, goto err; } } +#if defined(TARGET_LINUX) && defined(ENABLE_MPTCP) + else if (streq(p[0], "multipath")) + { + VERIFY_PERMISSION(OPT_P_GENERAL); + if (p[1]) + { + msg(msglevel, "--multipath does not accept any parameters"); + goto err; + } + options->enable_multipath = true; + } +#endif else { int i; diff --git a/src/openvpn/options.h b/src/openvpn/options.h index 6d9174a4446..ab22d500fbb 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -448,6 +448,9 @@ struct options #define SF_NO_PUSH_ROUTE_GATEWAY (1<<2) unsigned int server_flags; +#ifdef ENABLE_MPTCP + bool enable_multipath; +#endif bool server_bridge_proxy_dhcp; bool server_bridge_defined; diff --git a/src/openvpn/ps.c b/src/openvpn/ps.c index a69da2f9bed..da5b8620c67 100644 --- a/src/openvpn/ps.c +++ b/src/openvpn/ps.c @@ -39,6 +39,14 @@ #include "memdbg.h" + +#if defined(TARGET_LINUX) && defined(ENABLE_MPTCP) +#ifndef IPPROTO_MPTCP +#define IPPROTO_MPTCP 262 +#endif +#endif + + struct port_share *port_share = NULL; /* GLOBAL */ /* size of i/o buffers */ @@ -427,7 +435,11 @@ proxy_entry_new(struct proxy_connection **list, struct proxy_connection *cp; /* connect to port share server */ +#if defined(TARGET_LINUX) && defined(ENABLE_MPTCP) + if ((sd_server = socket(PF_INET, SOCK_STREAM, IPPROTO_MPTCP)) < 0) +#else if ((sd_server = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) +#endif { msg(M_WARN|M_ERRNO, "PORT SHARE PROXY: cannot create socket"); return false; diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c index 4e29327bfad..6edb60a8d8b 100644 --- a/src/openvpn/socket.c +++ b/src/openvpn/socket.c @@ -44,6 +44,12 @@ #include "memdbg.h" +#if defined(TARGET_LINUX) && defined(ENABLE_MPTCP) +#ifndef IPPROTO_MPTCP +#define IPPROTO_MPTCP 262 +#endif +#endif + /* * Convert sockflags/getaddr_flags into getaddr_flags */ @@ -1082,6 +1088,39 @@ create_socket_udp(struct addrinfo *addrinfo, const unsigned int flags) return sd; } +#if defined(TARGET_LINUX) && defined(ENABLE_MPTCP) +socket_descriptor_t +create_socket_mptcp(struct addrinfo *addrinfo) +{ + socket_descriptor_t sd; + + ASSERT(addrinfo); + ASSERT(addrinfo->ai_socktype == SOCK_STREAM); + addrinfo->ai_protocol = IPPROTO_MPTCP; + if ((sd = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol)) < 0) + { + msg(M_ERR, "Cannot create MPTCP socket"); + } + + { + int on = 1; + if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, + (void *) &on, sizeof(on)) < 0) + { + msg(M_ERR, "TCP: Cannot setsockopt SO_REUSEADDR on TCP socket"); + } + } + + /* set socket file descriptor to not pass across execs, so that + * scripts don't have access to it */ + set_cloexec(sd); + + return sd; +} + +#endif + + static void bind_local(struct link_socket *sock, const sa_family_t ai_family) { @@ -1125,6 +1164,21 @@ create_socket(struct link_socket *sock, struct addrinfo *addr) } else if (addr->ai_protocol == IPPROTO_TCP || addr->ai_socktype == SOCK_STREAM) { +#if defined(TARGET_LINUX) && defined(ENABLE_MPTCP) + if(sock->info.multipath) + { + sock->sd = create_socket_mptcp(addr); + // Multipath TCP could fail because it is not enabled on this host + // Try regular TCP + if(sock->sd == -1) + { + + msg(M_NONFATAL, "Can't resolve MPTCP socket, fallback to TCP !"); + sock->sd = create_socket_tcp(addr); + } + } + else +#endif sock->sd = create_socket_tcp(addr); } else @@ -1849,7 +1903,11 @@ link_socket_init_phase1(struct context *c, int mode) sock->bind_local = o->ce.bind_local; sock->resolve_retry_seconds = o->resolve_retry_seconds; sock->mtu_discover_type = o->ce.mtu_discover_type; - + +#if defined(TARGET_LINUX) && defined(ENABLE_MPTCP) + sock->info.multipath = o->enable_multipath; +#endif + #ifdef ENABLE_DEBUG sock->gremlin = o->gremlin; #endif @@ -2208,7 +2266,7 @@ link_socket_init_phase2(struct context *c) else #endif { - create_socket(sock, sock->info.lsa->current_remote); + create_socket(sock, sock->info.lsa->current_remote); } } diff --git a/src/openvpn/socket.h b/src/openvpn/socket.h index 462afa31bb4..5bed24034fa 100644 --- a/src/openvpn/socket.h +++ b/src/openvpn/socket.h @@ -122,6 +122,9 @@ struct link_socket_info bool bind_ipv6_only; int mtu_changed; /* Set to true when mtu value is changed */ bool dco_installed; +#if defined(TARGET_LINUX) && defined(ENABLE_MPTCP) + bool multipath; +#endif }; /* @@ -469,6 +472,10 @@ bool ipv6_addr_safe(const char *ipv6_text_addr); socket_descriptor_t create_socket_tcp(struct addrinfo *); +#ifdef ENABLE_MPTCP +socket_descriptor_t create_socket_mptcp(struct addrinfo *); +#endif + socket_descriptor_t socket_do_accept(socket_descriptor_t sd, struct link_socket_actual *act, const bool nowait);