* [PATCH mptcp-next v2 1/3] mptcp: add IPV6_TCLASS socket option support
2025-08-12 8:26 [PATCH mptcp-next v2 0/3] mptcp: add more sockopts Geliang Tang
@ 2025-08-12 8:26 ` Geliang Tang
2025-08-12 8:26 ` [PATCH mptcp-next v2 2/3] mptcp: add IP_TTL " Geliang Tang
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Geliang Tang @ 2025-08-12 8:26 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang, kernel test robot
From: Geliang Tang <tanggeliang@kylinos.cn>
Implement support for IPV6_TCLASS socket option in MPTCP:
1. setsockopt(IPV6_TCLASS):
- Sets traffic class on main MPTCP socket
- Propagates value to all IPv6 subflows
2. getsockopt(IPV6_TCLASS):
- Returns current value from inet6_sk(sk)->tclass
3. Syncs traffic class to new subflows
Mirrors existing IPv4 TOS handling and ensures consistent QoS across
subflows. Fixes reported issues with traffic class propagation in MPTCP.
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202508021003.tEAjvv2S-lkp@intel.com/
Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/568
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
net/mptcp/sockopt.c | 27 +++++++++++++++++++++++----
1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index 5b0648ab663e..bb296813c578 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -458,6 +458,9 @@ static int mptcp_setsockopt_v6(struct mptcp_sock *msk, int optname,
case IPV6_FREEBIND:
return mptcp_setsockopt_first_sf_only(msk, SOL_IPV6, optname,
optval, optlen);
+ case IPV6_TCLASS:
+ return mptcp_setsockopt_all_sf(msk, SOL_IPV6, optname,
+ optval, optlen);
}
return -EOPNOTSUPP;
@@ -765,7 +768,7 @@ static int mptcp_setsockopt_sol_ip_first_sf_only(struct mptcp_sock *msk, int opt
return 0;
}
-static int mptcp_setsockopt_sol_ip_all_sf(struct mptcp_sock *msk,
+static int mptcp_setsockopt_sol_ip_all_sf(struct mptcp_sock *msk, int level,
int optname, sockptr_t optval,
unsigned int optlen)
{
@@ -773,10 +776,16 @@ static int mptcp_setsockopt_sol_ip_all_sf(struct mptcp_sock *msk,
struct sock *sk = (struct sock *)msk;
int err, val;
- if (optname != IP_TOS)
+ if (optname != IP_TOS &&
+ optname != IPV6_TCLASS)
return -EOPNOTSUPP;
- err = ip_setsockopt(sk, SOL_IP, optname, optval, optlen);
+ if (level == SOL_IP)
+ err = ip_setsockopt(sk, level, optname, optval, optlen);
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+ else
+ err = ipv6_setsockopt(sk, level, optname, optval, optlen);
+#endif
if (err != 0)
return err;
@@ -785,6 +794,8 @@ static int mptcp_setsockopt_sol_ip_all_sf(struct mptcp_sock *msk,
sockopt_seq_inc(msk);
if (optname == IP_TOS)
val = READ_ONCE(inet_sk(sk)->tos);
+ else if (optname == IPV6_TCLASS)
+ val = READ_ONCE(inet6_sk(sk)->tclass);
mptcp_for_each_subflow(msk, subflow) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
bool slow;
@@ -792,6 +803,8 @@ static int mptcp_setsockopt_sol_ip_all_sf(struct mptcp_sock *msk,
slow = lock_sock_fast(ssk);
if (optname == IP_TOS)
__ip_sock_set_tos(ssk, val);
+ else if (optname == IPV6_TCLASS)
+ WRITE_ONCE(inet6_sk(ssk)->tclass, val);
unlock_sock_fast(ssk, slow);
}
release_sock(sk);
@@ -885,7 +898,8 @@ static int mptcp_setsockopt_all_sf(struct mptcp_sock *msk, int level,
return mptcp_setsockopt_sol_tcp_all_sf(msk, optname,
optval, optlen);
case SOL_IP:
- return mptcp_setsockopt_sol_ip_all_sf(msk, optname,
+ case SOL_IPV6:
+ return mptcp_setsockopt_sol_ip_all_sf(msk, level, optname,
optval, optlen);
}
@@ -1569,6 +1583,9 @@ static int mptcp_getsockopt_v6(struct mptcp_sock *msk, int optname,
case IPV6_FREEBIND:
return mptcp_put_int_option(msk, optval, optlen,
inet_test_bit(FREEBIND, sk));
+ case IPV6_TCLASS:
+ return mptcp_put_int_option(msk, optval, optlen,
+ inet6_sk(sk)->tclass);
}
return -EOPNOTSUPP;
@@ -1677,6 +1694,8 @@ static void sync_socket_options(struct mptcp_sock *msk, struct sock *ssk)
inet_assign_bit(FREEBIND, ssk, inet_test_bit(FREEBIND, sk));
inet_assign_bit(BIND_ADDRESS_NO_PORT, ssk, inet_test_bit(BIND_ADDRESS_NO_PORT, sk));
WRITE_ONCE(inet_sk(ssk)->local_port_range, READ_ONCE(inet_sk(sk)->local_port_range));
+ if (ssk->sk_family == AF_INET6)
+ WRITE_ONCE(inet6_sk(ssk)->tclass, READ_ONCE(inet6_sk(sk)->tclass));
}
void mptcp_sockopt_sync_locked(struct mptcp_sock *msk, struct sock *ssk)
--
2.48.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH mptcp-next v2 2/3] mptcp: add IP_TTL socket option support
2025-08-12 8:26 [PATCH mptcp-next v2 0/3] mptcp: add more sockopts Geliang Tang
2025-08-12 8:26 ` [PATCH mptcp-next v2 1/3] mptcp: add IPV6_TCLASS socket option support Geliang Tang
@ 2025-08-12 8:26 ` Geliang Tang
2025-08-12 8:26 ` [PATCH mptcp-next v2 3/3] mptcp: add IPV6_UNICAST_HOPS " Geliang Tang
2025-08-12 10:19 ` [PATCH mptcp-next v2 0/3] mptcp: add more sockopts MPTCP CI
3 siblings, 0 replies; 5+ messages in thread
From: Geliang Tang @ 2025-08-12 8:26 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
Implement setsockopt/getsockopt for IP_TTL in MPTCP:
1. setsockopt(IP_TTL):
- Applies TTL to MPTCP socket
- Propagates TTL to all subflows
2. getsockopt(IP_TTL):
- Returns current TTL from inet_sk(sk)->uc_ttl
- Falls back to sysctl_ip_default_ttl when unset
3. Syncs TTL to new subflows
4. Maintains option sequence counters
Enables applications to control IPv4 TTL settings consistently across
all subflows. Resolves outstanding requests for TTL support in MPTCP.
Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/296
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
net/mptcp/sockopt.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index bb296813c578..ae9b376071c9 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -776,7 +776,7 @@ static int mptcp_setsockopt_sol_ip_all_sf(struct mptcp_sock *msk, int level,
struct sock *sk = (struct sock *)msk;
int err, val;
- if (optname != IP_TOS &&
+ if (optname != IP_TOS && optname != IP_TTL &&
optname != IPV6_TCLASS)
return -EOPNOTSUPP;
@@ -794,6 +794,8 @@ static int mptcp_setsockopt_sol_ip_all_sf(struct mptcp_sock *msk, int level,
sockopt_seq_inc(msk);
if (optname == IP_TOS)
val = READ_ONCE(inet_sk(sk)->tos);
+ else if (optname == IP_TTL)
+ val = READ_ONCE(inet_sk(sk)->uc_ttl);
else if (optname == IPV6_TCLASS)
val = READ_ONCE(inet6_sk(sk)->tclass);
mptcp_for_each_subflow(msk, subflow) {
@@ -803,6 +805,8 @@ static int mptcp_setsockopt_sol_ip_all_sf(struct mptcp_sock *msk, int level,
slow = lock_sock_fast(ssk);
if (optname == IP_TOS)
__ip_sock_set_tos(ssk, val);
+ else if (optname == IP_TTL)
+ WRITE_ONCE(inet_sk(ssk)->uc_ttl, val);
else if (optname == IPV6_TCLASS)
WRITE_ONCE(inet6_sk(ssk)->tclass, val);
unlock_sock_fast(ssk, slow);
@@ -822,6 +826,7 @@ static int mptcp_setsockopt_v4(struct mptcp_sock *msk, int optname,
case IP_LOCAL_PORT_RANGE:
return mptcp_setsockopt_first_sf_only(msk, SOL_IP, optname, optval, optlen);
case IP_TOS:
+ case IP_TTL:
return mptcp_setsockopt_all_sf(msk, SOL_IP, optname, optval, optlen);
}
@@ -1547,6 +1552,7 @@ static int mptcp_getsockopt_v4(struct mptcp_sock *msk, int optname,
char __user *optval, int __user *optlen)
{
struct sock *sk = (void *)msk;
+ int val;
switch (optname) {
case IP_TOS:
@@ -1563,6 +1569,11 @@ static int mptcp_getsockopt_v4(struct mptcp_sock *msk, int optname,
case IP_LOCAL_PORT_RANGE:
return mptcp_put_int_option(msk, optval, optlen,
READ_ONCE(inet_sk(sk)->local_port_range));
+ case IP_TTL:
+ val = READ_ONCE(inet_sk(sk)->uc_ttl);
+ if (val < 0)
+ val = READ_ONCE(sock_net(sk)->ipv4.sysctl_ip_default_ttl);
+ return mptcp_put_int_option(msk, optval, optlen, val);
}
return -EOPNOTSUPP;
@@ -1694,6 +1705,7 @@ static void sync_socket_options(struct mptcp_sock *msk, struct sock *ssk)
inet_assign_bit(FREEBIND, ssk, inet_test_bit(FREEBIND, sk));
inet_assign_bit(BIND_ADDRESS_NO_PORT, ssk, inet_test_bit(BIND_ADDRESS_NO_PORT, sk));
WRITE_ONCE(inet_sk(ssk)->local_port_range, READ_ONCE(inet_sk(sk)->local_port_range));
+ WRITE_ONCE(inet_sk(ssk)->uc_ttl, READ_ONCE(inet_sk(sk)->uc_ttl));
if (ssk->sk_family == AF_INET6)
WRITE_ONCE(inet6_sk(ssk)->tclass, READ_ONCE(inet6_sk(sk)->tclass));
}
--
2.48.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH mptcp-next v2 3/3] mptcp: add IPV6_UNICAST_HOPS socket option support
2025-08-12 8:26 [PATCH mptcp-next v2 0/3] mptcp: add more sockopts Geliang Tang
2025-08-12 8:26 ` [PATCH mptcp-next v2 1/3] mptcp: add IPV6_TCLASS socket option support Geliang Tang
2025-08-12 8:26 ` [PATCH mptcp-next v2 2/3] mptcp: add IP_TTL " Geliang Tang
@ 2025-08-12 8:26 ` Geliang Tang
2025-08-12 10:19 ` [PATCH mptcp-next v2 0/3] mptcp: add more sockopts MPTCP CI
3 siblings, 0 replies; 5+ messages in thread
From: Geliang Tang @ 2025-08-12 8:26 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
Implement setsockopt/getsockopt for IPV6_UNICAST_HOPS in MPTCP:
1. setsockopt(IPV6_UNICAST_HOPS):
- Sets hop limit on MPTCP socket
- Propagates value to all IPv6 subflows
2. getsockopt(IPV6_UNICAST_HOPS):
- Returns current hop limit (from inet6_sk or system default)
3. Syncs hop limit to new subflows
4. Uses conditional compilation for IPv6 support
Provides standard IPv6 hop limit control for MPTCP connections with
consistent behavior across subflows. Completes IPv6 socket option parity.
Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/296
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
net/mptcp/sockopt.c | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index ae9b376071c9..83071e5c608d 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -459,6 +459,7 @@ static int mptcp_setsockopt_v6(struct mptcp_sock *msk, int optname,
return mptcp_setsockopt_first_sf_only(msk, SOL_IPV6, optname,
optval, optlen);
case IPV6_TCLASS:
+ case IPV6_UNICAST_HOPS:
return mptcp_setsockopt_all_sf(msk, SOL_IPV6, optname,
optval, optlen);
}
@@ -777,7 +778,7 @@ static int mptcp_setsockopt_sol_ip_all_sf(struct mptcp_sock *msk, int level,
int err, val;
if (optname != IP_TOS && optname != IP_TTL &&
- optname != IPV6_TCLASS)
+ optname != IPV6_TCLASS && optname != IPV6_UNICAST_HOPS)
return -EOPNOTSUPP;
if (level == SOL_IP)
@@ -798,6 +799,8 @@ static int mptcp_setsockopt_sol_ip_all_sf(struct mptcp_sock *msk, int level,
val = READ_ONCE(inet_sk(sk)->uc_ttl);
else if (optname == IPV6_TCLASS)
val = READ_ONCE(inet6_sk(sk)->tclass);
+ else if (optname == IPV6_UNICAST_HOPS)
+ val = READ_ONCE(inet6_sk(sk)->hop_limit);
mptcp_for_each_subflow(msk, subflow) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
bool slow;
@@ -809,6 +812,8 @@ static int mptcp_setsockopt_sol_ip_all_sf(struct mptcp_sock *msk, int level,
WRITE_ONCE(inet_sk(ssk)->uc_ttl, val);
else if (optname == IPV6_TCLASS)
WRITE_ONCE(inet6_sk(ssk)->tclass, val);
+ else if (optname == IPV6_UNICAST_HOPS)
+ WRITE_ONCE(inet6_sk(ssk)->hop_limit, val);
unlock_sock_fast(ssk, slow);
}
release_sock(sk);
@@ -1583,6 +1588,7 @@ static int mptcp_getsockopt_v6(struct mptcp_sock *msk, int optname,
char __user *optval, int __user *optlen)
{
struct sock *sk = (void *)msk;
+ int val;
switch (optname) {
case IPV6_V6ONLY:
@@ -1597,6 +1603,13 @@ static int mptcp_getsockopt_v6(struct mptcp_sock *msk, int optname,
case IPV6_TCLASS:
return mptcp_put_int_option(msk, optval, optlen,
inet6_sk(sk)->tclass);
+ case IPV6_UNICAST_HOPS:
+ val = READ_ONCE(inet6_sk(sk)->hop_limit);
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+ if (val < 0)
+ val = READ_ONCE(sock_net(sk)->ipv6.devconf_all->hop_limit);
+#endif
+ return mptcp_put_int_option(msk, optval, optlen, val);
}
return -EOPNOTSUPP;
@@ -1706,8 +1719,10 @@ static void sync_socket_options(struct mptcp_sock *msk, struct sock *ssk)
inet_assign_bit(BIND_ADDRESS_NO_PORT, ssk, inet_test_bit(BIND_ADDRESS_NO_PORT, sk));
WRITE_ONCE(inet_sk(ssk)->local_port_range, READ_ONCE(inet_sk(sk)->local_port_range));
WRITE_ONCE(inet_sk(ssk)->uc_ttl, READ_ONCE(inet_sk(sk)->uc_ttl));
- if (ssk->sk_family == AF_INET6)
+ if (ssk->sk_family == AF_INET6) {
WRITE_ONCE(inet6_sk(ssk)->tclass, READ_ONCE(inet6_sk(sk)->tclass));
+ WRITE_ONCE(inet6_sk(ssk)->hop_limit, READ_ONCE(inet6_sk(sk)->hop_limit));
+ }
}
void mptcp_sockopt_sync_locked(struct mptcp_sock *msk, struct sock *ssk)
--
2.48.1
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH mptcp-next v2 0/3] mptcp: add more sockopts
2025-08-12 8:26 [PATCH mptcp-next v2 0/3] mptcp: add more sockopts Geliang Tang
` (2 preceding siblings ...)
2025-08-12 8:26 ` [PATCH mptcp-next v2 3/3] mptcp: add IPV6_UNICAST_HOPS " Geliang Tang
@ 2025-08-12 10:19 ` MPTCP CI
3 siblings, 0 replies; 5+ messages in thread
From: MPTCP CI @ 2025-08-12 10:19 UTC (permalink / raw)
To: Geliang Tang; +Cc: mptcp
Hi Geliang,
Thank you for your modifications, that's great!
Our CI did some validations and here is its report:
- KVM Validation: normal: Unstable: 1 failed test(s): selftest_mptcp_connect_checksum 🔴
- KVM Validation: debug: Unstable: 1 failed test(s): packetdrill_mp_capable 🔴
- KVM Validation: btf-normal (only bpftest_all): Success! ✅
- KVM Validation: btf-debug (only bpftest_all): Success! ✅
- Task: https://github.com/multipath-tcp/mptcp_net-next/actions/runs/16903540842
Initiator: Patchew Applier
Commits: https://github.com/multipath-tcp/mptcp_net-next/commits/e5177b226ee9
Patchwork: https://patchwork.kernel.org/project/mptcp/list/?series=990418
If there are some issues, you can reproduce them using the same environment as
the one used by the CI thanks to a docker image, e.g.:
$ cd [kernel source code]
$ docker run -v "${PWD}:${PWD}:rw" -w "${PWD}" --privileged --rm -it \
--pull always mptcp/mptcp-upstream-virtme-docker:latest \
auto-normal
For more details:
https://github.com/multipath-tcp/mptcp-upstream-virtme-docker
Please note that despite all the efforts that have been already done to have a
stable tests suite when executed on a public CI like here, it is possible some
reported issues are not due to your modifications. Still, do not hesitate to
help us improve that ;-)
Cheers,
MPTCP GH Action bot
Bot operated by Matthieu Baerts (NGI0 Core)
^ permalink raw reply [flat|nested] 5+ messages in thread