Multipath TCP (MPTCP) is one of the most valuable networking capabilities in the Linux kernel in recent years, especially suitable for scenarios such as in-vehicle gateways (T-BOX), parallel driving, and multi-link redundancy. It can simultaneously utilize multiple links such as 4G/5G, Wi-Fi, and Ethernet, significantly enhancing data throughput, link recovery, and disaster recovery capabilities in weak network environments.
However, in practical engineering, the performance of MPTCP in Linux 5.x is far from being as “plug-and-play” as imagined. Especially in high-pressure scenarios such as weak networks, multi-links, NAT, and public networks, the stability and completeness of MPTCP’s functionality are crucial.
Although Linux 5.10 is the first LTS version to include MPTCP, there are still many unresolved defects in engineering practice— including flow drops, inability to recover subflows, connection hangs, address mapping failures, and abnormal subflow establishment in NAT environments. After multiple validations in our projects, we found that MPTCP in Linux 5.10 is more like “it can run, but it is not suitable for production environments.”
In contrast, Linux 5.15 truly becomes the first “engineering usable version” of the MPTCP kernel. In this version, the community has merged a large number of fix patches and capability enhancements, which not only significantly improve stability and functional consistency but also allow the subflow mode to truly reach a level that can run stably in actual projects.
Based on our practical experience in in-vehicle networks, parallel driving, and real-time services in weak networks: MPTCP in Linux 5.10 does not meet mass production conditions, while MPTCP in Linux 5.15 has stable engineering value for deployment.
1Why pay attention to Linux MPTCP?
Simply put, MPTCP allows:
-
A single connection to use multiple network interfaces (WiFi + cellular / dual cellular + Ethernet)
-
Business to be transparent to network switching
-
Weak networks to be more reliable
-
Multi-links to aggregate bandwidth
-
Automatic failover during sudden changes in network quality
This capability is critical for scenarios such as in-vehicle gateways, unmanned delivery, cloud control systems, and edge nodes. However, it also places higher demands on the implementation quality of the Linux kernel.
2MPTCP in Linux 5.10 Kernel: Incomplete Functionality, Engineering UnusableLinux 5.10 is the first long-term support (LTS) version to include MPTCP, but it is more like a “basic model”:
2.1

Subflow implementation is incomplete
-
Subflow establishment/closure processes are prone to race conditions
-
Multiple subflows are prone to out-of-order, deadlock, and DATA_FIN blockage in weak network scenarios
-
Join subflow fails with high probability in NAT environments
-
Cannot correctly recover subflows after abnormal disconnections
Engineering practice conclusion: Unable to stably establish subflows, let alone recover in weak networks.
2.2

Path Manager functionality is limited
Linux 5.10’s PM (Path Manager) has multiple defects:
-
fullmesh mode is unavailable
-
Address synchronization (ADD_ADDR/RM_ADDR) logic defects
-
Interface up/down events cannot correctly trigger reconnections
-
mptcpd has limited compatibility on Linux 5.10, basically unusable
Result: A device that seems to have multiple links actually only maintains one link operational.
2.3

Scheduler behavior is rudimentary
The default scheduling policy in Linux 5.10 cannot guarantee stable throughput in:
-
Asymmetric RTT scenarios of WiFi + cellular
-
High packet loss scenarios
-
Links with significant bandwidth differences
It may even lead to connection hangs.
2.4

Serious bugs in DSS
-
DATA_FIN cannot be successfully sent or received
-
Retransmission mapping errors
-
Confused skb_mapping leads to read/write hangs
These issues are particularly evident in continuous weak networks (e.g., in-vehicle cellular).
2.5

Poor NAT/firewall compatibility
MPTCP in Linux 5.10 fails significantly in the following environments:
-
Multi-level NAT
-
Carrier NAT
-
Wireless gateways
-
VPN + NAT scenarios
Engineering result: Subflows cannot join, and MPTCP degrades to ordinary TCP.
2.6

Insufficient stability
Under stress testing:
-
Connections may hang
-
Subflows may remain in half-closed state indefinitely
-
Retry mechanisms are inadequate
3Upgrading MPTCP stack from Linux 5.10 to Linux 5.15
In actual projects, if conditions permit, the most recommended solution is always to directly upgrade the system kernel to Linux 5.15 or higher, and regularly follow up on minor version updates according to the LTS lifecycle (refer to the official maintenance plan: kernel.org). This is the best way to obtain stable and complete MPTCP capabilities.
However, in many in-vehicle projects, custom BSPs, SoC closed SDKs, or commercial distributions, the kernel version is often locked at Linux 5.10, making it impossible to upgrade the entire system. In this case, a feasible engineering solution that we have validated on a large scale is:
To upgrade the entire MPTCP stack to the Linux 5.15 version while keeping the Linux 5.10 kernel framework unchanged.
We have validated the feasibility of this solution in over 100 terminals, accumulating over 300,000 hours of real-world testing, and its stability performance is completely consistent with the native Linux 5.15 kernel. As long as the MPTCP subsystem of 5.15 is merged correctly and 3 to 5 simple conflict patches are handled well, it is possible to achieve the same MPTCP stability as Linux 5.15 without upgrading the entire kernel.
Below, we will provide a set of executable guidance to help engineering teams safely and controllably upgrade MPTCP from 5.10 to 5.15 to meet the actual needs in in-vehicle networks and weak network environments.
3.1

Download LTS code
git clone https://github.com/gregkh/linux.git
3.2

Extract MPTCP-related changes
My project is Linux 5.10.66, and I upgraded to Linux 5.15.162. The specific extraction script is as follows, based on this script you will be able to extract about 431 patches.
#!/bin/bash# Get relevant commit logsgit fetch origin v5.15.162echo "git fetch ok"# Filter all commit hashes between v5.10.66 and v5.15.162 in chronological ordergit log v5.10.66..v5.15.162 --pretty=format:"%H" --reverse > all.logecho "git log ok"# Create directory to save patchesmkdir -p mptcp_patches# Initialize patch numberpatch_number=1# Iterate through each commit hashwhile read commit_hash; do # Get the list of files modified in the commit files=$(git diff-tree --no-commit-id --name-only -r $commit_hash) # Check if net/mptcp was modified if echo "$files" | grep -q "net/mptcp"; then # Get commit summary commit_subject=$(git show -s --format=%s $commit_hash) # Remove spaces and special characters from summary commit_subject_clean=$(echo $commit_subject | tr -d '[:punct:]' | tr ' ' '_') # Generate patch file, using commit hash to generate file git format-patch -1 $commit_hash -o mptcp_patches # Get the name of the newly generated patch file latest_patch=$(ls -t mptcp_patches | head -n 1) # Rename patch file, adding number and summary patch_filename=$(printf "mptcp_patches/%04d-%s-%s.patch" $patch_number $commit_hash $commit_subject_clean) mv "mptcp_patches/$latest_patch" "$patch_filename" echo "$patch_filename" patch_number=$((patch_number + 1)) fidone < all.log# Clean up log filerm -f all.logecho "Patch generation completed, saved in mptcp_patches directory."
3.3

Do not merge SO_TIMESTAMP settings
The following patches do not need to be merged directly, but inform you to handle similarly when compilation fails, by removing this feature.
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.cindex c7cfd9818f8f..0fd47259e1d5 100644--- a/net/ipv4/tcp.c+++ b/net/ipv4/tcp.c@@ -2511,7 +2511,8 @@ void __tcp_close(struct sock *sk, long timeout) /* remove backlog if any, without releasing ownership. */ __release_sock(sk);- this_cpu_inc(tcp_orphan_count);+ // TODO jeff+ percpu_counter_inc(sk->sk_prot->orphan_count); /* Have we already been destroyed by a softirq or backlog? */ if (state != TCP_CLOSE && sk->sk_state == TCP_CLOSE)diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.cindex 565384a7f062..5a702b895afe 100644--- a/net/mptcp/sockopt.c+++ b/net/mptcp/sockopt.c@@ -142,27 +142,8 @@ static void mptcp_so_incoming_cpu(struct mptcp_sock *msk, int val) static int mptcp_setsockopt_sol_socket_tstamp(struct mptcp_sock *msk, int optname, int val) {- sockptr_t optval = KERNEL_SOCKPTR(&>val);- struct mptcp_subflow_context *subflow;- struct sock *sk = (struct sock *)msk;- int ret;-- ret = sock_setsockopt(sk->sk_socket, SOL_SOCKET, optname,- optval, sizeof(val));- if (ret)- return ret;-- lock_sock(sk);- mptcp_for_each_subflow(msk, subflow) {- struct sock *ssk = mptcp_subflow_tcp_sock(subflow);- bool slow = lock_sock_fast(ssk);-- sock_set_timestamp(sk, optname, !!val);- unlock_sock_fast(ssk, slow);- }-- release_sock(sk);- return 0;+ pr_debug("msk=%p. SO_TIMESTAMP-xxx no support", msk);+ return -ESOCKTNOSUPPORT; } static int mptcp_setsockopt_sol_socket_int(struct mptcp_sock *msk, int optname,@@ -203,32 +184,8 @@ static int mptcp_setsockopt_sol_socket_timestamping(struct mptcp_sock *msk, sockptr_t optval, unsigned int optlen) {- struct mptcp_subflow_context *subflow;- struct sock *sk = (struct sock *)msk;- int val, ret;-- ret = mptcp_get_int_option(msk, optval, optlen, &>val);- if (ret)- return ret;-- ret = sock_setsockopt(sk->sk_socket, SOL_SOCKET, optname,- KERNEL_SOCKPTR(&>val), sizeof(val));- if (ret)- return ret;-- lock_sock(sk);-- mptcp_for_each_subflow(msk, subflow) {- struct sock *ssk = mptcp_subflow_tcp_sock(subflow);- bool slow = lock_sock_fast(ssk);-- sock_set_timestamping(sk, optname, val);- unlock_sock_fast(ssk, slow);- }-- release_sock(sk);-- return 0;+ pr_debug("msk=%p. SO_TIMESTAMP-xxx no support", msk);+ return -ESOCKTNOSUPPORT; } static int mptcp_setsockopt_sol_socket_linger(struct mptcp_sock *msk, sockptr_t optval,@@ -329,8 +286,6 @@ static int mptcp_setsockopt_sol_socket(struct mptcp_sock *msk, int optname, case SO_RCVTIMEO_OLD: case SO_RCVTIMEO_NEW: case SO_BUSY_POLL:- case SO_PREFER_BUSY_POLL:- case SO_BUSY_POLL_BUDGET: /* No need to copy: only relevant for msk */ return sock_setsockopt(sk->sk_socket, SOL_SOCKET, optname, optval, optlen); case SO_NO_CHECK:
3.4

Additional merging of two patches
Execute these two commands in the directory of the downloaded Linux LTS code from section 3.1.
git show 049fe386d35353398eee40ba8d76ab62cb5a24e5git show 2dcb96bacce36021c2f3eaae0cef607b5bb71ede
4Improvement points for MPTCP in Linux 5.15
-
Linux 5.15 does not fully support Fullmesh path management mode.
-
Linux 5.15 does not support redundant scheduling mode. If you expect to implement the multi-send single-receive mode of mptcpV0, you need to merge a non-mainline version of the MPTCP eBPF scheduler.
