* [PATCH mptcp-next v2 0/7] mptcp: add bpf_setsockopt support
@ 2026-07-21 2:40 Gang Yan
2026-07-21 2:40 ` [PATCH mptcp-next v2 1/7] mptcp: drop unused @max arg of __mptcp_setsockopt_set_val Gang Yan
` (7 more replies)
0 siblings, 8 replies; 9+ messages in thread
From: Gang Yan @ 2026-07-21 2:40 UTC (permalink / raw)
To: mptcp; +Cc: pabeni, Gang Yan
From: Gang Yan <yangang@kylinos.cn>
Changelog:
v2:
- Patches 1 and 2 are new in this series; they address TCP_MAXSEG
handling in the bpf_setsockopt() path [1].
- Patch 4 adds an early return to fix msk->sk_rcvlowat being
unexpectedly modified, an issue seen in v1.
- Patch 5 makes the hook safe for the non-tcp master socket: it guards
bpf_sock_ops_cb_flags_set() with sk_is_tcp() to prevent out-of-bounds
heap reads/writes through tcp_sk(sk)->bpf_sock_ops_cb_flags, and does
not set is_locked_tcp_sock for the msk (unlike tcp_call_bpf()). That
flag authorizes the verifier's direct tcp_sock-offset field accesses;
since the msk is not a tcp_sock, leaving it at the default 0 is safe.
v1:
Link: https://patchwork.kernel.org/project/mptcp/cover/20260713095735.1222033-1-gang.yan@linux.dev/
Gang Yan (7):
mptcp: drop unused @max arg of __mptcp_setsockopt_set_val
mptcp: take TCP_MAXSEG handling into __mptcp_setsockopt_set_val
mptcp: use sockopt_lock/release_sock in sockopt
mptcp: reject sockopt requiring ssks' lock in BPF context
mptcp: enable bpf_setsockopt on the master socket
mptcp: add TCP_CONNECT_CB sock_ops hook
selftests: bpf: verify mptcp bpf_setsockopt from TCP_CONNECT_CB
include/net/mptcp.h | 9 ++
net/core/filter.c | 10 ++
net/ipv4/tcp.c | 1 +
net/mptcp/protocol.c | 6 +
net/mptcp/protocol.h | 29 ++++
net/mptcp/sockopt.c | 149 +++++++++---------
.../testing/selftests/bpf/prog_tests/mptcp.c | 61 +++++++
.../selftests/bpf/progs/mptcp_setsockopt.c | 32 ++++
8 files changed, 223 insertions(+), 74 deletions(-)
create mode 100644 tools/testing/selftests/bpf/progs/mptcp_setsockopt.c
--
2.43.0
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH mptcp-next v2 1/7] mptcp: drop unused @max arg of __mptcp_setsockopt_set_val
2026-07-21 2:40 [PATCH mptcp-next v2 0/7] mptcp: add bpf_setsockopt support Gang Yan
@ 2026-07-21 2:40 ` Gang Yan
2026-07-21 2:40 ` [PATCH mptcp-next v2 2/7] mptcp: take TCP_MAXSEG handling into __mptcp_setsockopt_set_val Gang Yan
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Gang Yan @ 2026-07-21 2:40 UTC (permalink / raw)
To: mptcp; +Cc: pabeni, Gang Yan
From: Gang Yan <yangang@kylinos.cn>
The @max argument is never read in the function body. Remove it and the
MAX_TCP_KEEP* values passed by the TCP_KEEPIDLE/INTVL/KEEPCNT callers.
Signed-off-by: Gang Yan <yangang@kylinos.cn>
---
net/mptcp/sockopt.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index fcf6feb2a9eb..9c4faa0d82ef 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -632,7 +632,7 @@ static int mptcp_setsockopt_sol_tcp_congestion(struct mptcp_sock *msk, sockptr_t
return ret;
}
-static int __mptcp_setsockopt_set_val(struct mptcp_sock *msk, int max,
+static int __mptcp_setsockopt_set_val(struct mptcp_sock *msk,
int (*set_val)(struct sock *, int),
int *msk_val, int val)
{
@@ -876,18 +876,15 @@ static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
ret = __mptcp_setsockopt_sol_tcp_nodelay(msk, val);
break;
case TCP_KEEPIDLE:
- ret = __mptcp_setsockopt_set_val(msk, MAX_TCP_KEEPIDLE,
- &tcp_sock_set_keepidle_locked,
+ ret = __mptcp_setsockopt_set_val(msk, &tcp_sock_set_keepidle_locked,
&msk->keepalive_idle, val);
break;
case TCP_KEEPINTVL:
- ret = __mptcp_setsockopt_set_val(msk, MAX_TCP_KEEPINTVL,
- &tcp_sock_set_keepintvl,
+ ret = __mptcp_setsockopt_set_val(msk, &tcp_sock_set_keepintvl,
&msk->keepalive_intvl, val);
break;
case TCP_KEEPCNT:
- ret = __mptcp_setsockopt_set_val(msk, MAX_TCP_KEEPCNT,
- &tcp_sock_set_keepcnt,
+ ret = __mptcp_setsockopt_set_val(msk, &tcp_sock_set_keepcnt,
&msk->keepalive_cnt,
val);
break;
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH mptcp-next v2 2/7] mptcp: take TCP_MAXSEG handling into __mptcp_setsockopt_set_val
2026-07-21 2:40 [PATCH mptcp-next v2 0/7] mptcp: add bpf_setsockopt support Gang Yan
2026-07-21 2:40 ` [PATCH mptcp-next v2 1/7] mptcp: drop unused @max arg of __mptcp_setsockopt_set_val Gang Yan
@ 2026-07-21 2:40 ` Gang Yan
2026-07-21 2:40 ` [PATCH mptcp-next v2 3/7] mptcp: use sockopt_lock/release_sock in sockopt Gang Yan
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Gang Yan @ 2026-07-21 2:40 UTC (permalink / raw)
To: mptcp; +Cc: pabeni, Gang Yan
From: Gang Yan <yangang@kylinos.cn>
mptcp_setsockopt_all_sf is only used in 'TCP_MAXSEG', and it can be
replaced with __mptcp_setsockopt_set_val.
Signed-off-by: Gang Yan <yangang@kylinos.cn>
---
net/ipv4/tcp.c | 1 +
net/mptcp/sockopt.c | 27 ++-------------------------
2 files changed, 3 insertions(+), 25 deletions(-)
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index b427f924608c..5915d0e62f43 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -3830,6 +3830,7 @@ int tcp_sock_set_maxseg(struct sock *sk, int val)
WRITE_ONCE(tcp_sk(sk)->rx_opt.user_mss, val);
return 0;
}
+EXPORT_SYMBOL(tcp_sock_set_maxseg);
/*
* Socket option code for TCP.
diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index 9c4faa0d82ef..3e000436ab94 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -808,28 +808,6 @@ static int mptcp_setsockopt_first_sf_only(struct mptcp_sock *msk, int level, int
return ret;
}
-static int mptcp_setsockopt_all_sf(struct mptcp_sock *msk, int level,
- int optname, sockptr_t optval,
- unsigned int optlen)
-{
- struct mptcp_subflow_context *subflow;
- int ret = 0;
-
- mptcp_for_each_subflow(msk, subflow) {
- struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
- int err;
-
- err = tcp_setsockopt(ssk, level, optname, optval, optlen);
- if (err < 0 && ret == 0)
- ret = err;
- }
-
- if (!ret)
- sockopt_seq_inc(msk);
-
- return ret;
-}
-
static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
sockptr_t optval, unsigned int optlen)
{
@@ -889,9 +867,8 @@ static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
val);
break;
case TCP_MAXSEG:
- msk->maxseg = val;
- ret = mptcp_setsockopt_all_sf(msk, SOL_TCP, optname, optval,
- optlen);
+ ret = __mptcp_setsockopt_set_val(msk, &tcp_sock_set_maxseg,
+ &msk->maxseg, val);
break;
default:
ret = -ENOPROTOOPT;
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH mptcp-next v2 3/7] mptcp: use sockopt_lock/release_sock in sockopt
2026-07-21 2:40 [PATCH mptcp-next v2 0/7] mptcp: add bpf_setsockopt support Gang Yan
2026-07-21 2:40 ` [PATCH mptcp-next v2 1/7] mptcp: drop unused @max arg of __mptcp_setsockopt_set_val Gang Yan
2026-07-21 2:40 ` [PATCH mptcp-next v2 2/7] mptcp: take TCP_MAXSEG handling into __mptcp_setsockopt_set_val Gang Yan
@ 2026-07-21 2:40 ` Gang Yan
2026-07-21 2:40 ` [PATCH mptcp-next v2 4/7] mptcp: reject sockopt requiring ssks' lock in BPF context Gang Yan
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Gang Yan @ 2026-07-21 2:40 UTC (permalink / raw)
To: mptcp; +Cc: pabeni, Gang Yan
From: Gang Yan <yangang@kylinos.cn>
TCP and the core socket layer use sockopt_lock_sock() /
sockopt_release_sock() in their setsockopt and getsockopt handlers.
Switch the MPTCP socket (msk) level lock_sock()/release_sock()
calls to use the BPF-aware wrappers, making the MPTCP sockopt
codepaths consistent with the rest of the networking stack.
Signed-off-by: Gang Yan <yangang@kylinos.cn>
---
net/mptcp/sockopt.c | 84 ++++++++++++++++++++++-----------------------
1 file changed, 42 insertions(+), 42 deletions(-)
diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index 3e000436ab94..1665438269e5 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -78,7 +78,7 @@ static void mptcp_sol_socket_sync_intval(struct mptcp_sock *msk, int optname, in
struct mptcp_subflow_context *subflow;
struct sock *sk = (struct sock *)msk;
- lock_sock(sk);
+ sockopt_lock_sock(sk);
sockopt_seq_inc(msk);
mptcp_for_each_subflow(msk, subflow) {
@@ -123,7 +123,7 @@ static void mptcp_sol_socket_sync_intval(struct mptcp_sock *msk, int optname, in
unlock_sock_fast(ssk, slow);
}
- release_sock(sk);
+ sockopt_release_sock(sk);
}
static int mptcp_sol_socket_intval(struct mptcp_sock *msk, int optname, int val)
@@ -162,7 +162,7 @@ static int mptcp_setsockopt_sol_socket_tstamp(struct mptcp_sock *msk, int optnam
if (ret)
return ret;
- lock_sock(sk);
+ sockopt_lock_sock(sk);
mptcp_for_each_subflow(msk, subflow) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
@@ -171,7 +171,7 @@ static int mptcp_setsockopt_sol_socket_tstamp(struct mptcp_sock *msk, int optnam
release_sock(ssk);
}
- release_sock(sk);
+ sockopt_release_sock(sk);
return 0;
}
@@ -237,7 +237,7 @@ static int mptcp_setsockopt_sol_socket_timestamping(struct mptcp_sock *msk,
if (ret)
return ret;
- lock_sock(sk);
+ sockopt_lock_sock(sk);
mptcp_for_each_subflow(msk, subflow) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
@@ -251,7 +251,7 @@ static int mptcp_setsockopt_sol_socket_timestamping(struct mptcp_sock *msk,
ret = err;
}
- release_sock(sk);
+ sockopt_release_sock(sk);
return ret;
}
@@ -276,7 +276,7 @@ static int mptcp_setsockopt_sol_socket_linger(struct mptcp_sock *msk, sockptr_t
if (ret)
return ret;
- lock_sock(sk);
+ sockopt_lock_sock(sk);
sockopt_seq_inc(msk);
mptcp_for_each_subflow(msk, subflow) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
@@ -293,7 +293,7 @@ static int mptcp_setsockopt_sol_socket_linger(struct mptcp_sock *msk, sockptr_t
unlock_sock_fast(ssk, slow);
}
- release_sock(sk);
+ sockopt_release_sock(sk);
return 0;
}
@@ -309,10 +309,10 @@ static int mptcp_setsockopt_sol_socket(struct mptcp_sock *msk, int optname,
case SO_REUSEADDR:
case SO_BINDTODEVICE:
case SO_BINDTOIFINDEX:
- lock_sock(sk);
+ sockopt_lock_sock(sk);
ssk = __mptcp_nmpc_sk(msk);
if (IS_ERR(ssk)) {
- release_sock(sk);
+ sockopt_release_sock(sk);
return PTR_ERR(ssk);
}
@@ -327,7 +327,7 @@ static int mptcp_setsockopt_sol_socket(struct mptcp_sock *msk, int optname,
else if (optname == SO_BINDTOIFINDEX)
sk->sk_bound_dev_if = ssk->sk_bound_dev_if;
}
- release_sock(sk);
+ sockopt_release_sock(sk);
return ret;
case SO_KEEPALIVE:
case SO_PRIORITY:
@@ -405,16 +405,16 @@ static int mptcp_setsockopt_v6(struct mptcp_sock *msk, int optname,
case IPV6_V6ONLY:
case IPV6_TRANSPARENT:
case IPV6_FREEBIND:
- lock_sock(sk);
+ sockopt_lock_sock(sk);
ssk = __mptcp_nmpc_sk(msk);
if (IS_ERR(ssk)) {
- release_sock(sk);
+ sockopt_release_sock(sk);
return PTR_ERR(ssk);
}
ret = tcp_setsockopt(ssk, SOL_IPV6, optname, optval, optlen);
if (ret != 0) {
- release_sock(sk);
+ sockopt_release_sock(sk);
return ret;
}
@@ -434,7 +434,7 @@ static int mptcp_setsockopt_v6(struct mptcp_sock *msk, int optname,
break;
}
- release_sock(sk);
+ sockopt_release_sock(sk);
break;
}
@@ -611,7 +611,7 @@ static int mptcp_setsockopt_sol_tcp_congestion(struct mptcp_sock *msk, sockptr_t
cap_net_admin = ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN);
ret = 0;
- lock_sock(sk);
+ sockopt_lock_sock(sk);
sockopt_seq_inc(msk);
mptcp_for_each_subflow(msk, subflow) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
@@ -628,7 +628,7 @@ static int mptcp_setsockopt_sol_tcp_congestion(struct mptcp_sock *msk, sockptr_t
if (ret == 0)
strscpy(msk->ca_name, name, sizeof(msk->ca_name));
- release_sock(sk);
+ sockopt_release_sock(sk);
return ret;
}
@@ -707,11 +707,11 @@ static int mptcp_setsockopt_sol_ip_set(struct mptcp_sock *msk, int optname,
if (err != 0)
return err;
- lock_sock(sk);
+ sockopt_lock_sock(sk);
ssk = __mptcp_nmpc_sk(msk);
if (IS_ERR(ssk)) {
- release_sock(sk);
+ sockopt_release_sock(sk);
return PTR_ERR(ssk);
}
@@ -732,13 +732,13 @@ static int mptcp_setsockopt_sol_ip_set(struct mptcp_sock *msk, int optname,
READ_ONCE(inet_sk(sk)->local_port_range));
break;
default:
- release_sock(sk);
+ sockopt_release_sock(sk);
WARN_ON_ONCE(1);
return -EOPNOTSUPP;
}
sockopt_seq_inc(msk);
- release_sock(sk);
+ sockopt_release_sock(sk);
return 0;
}
@@ -754,7 +754,7 @@ static int mptcp_setsockopt_v4_set_tos(struct mptcp_sock *msk, int optname,
if (err != 0)
return err;
- lock_sock(sk);
+ sockopt_lock_sock(sk);
sockopt_seq_inc(msk);
val = READ_ONCE(inet_sk(sk)->tos);
mptcp_for_each_subflow(msk, subflow) {
@@ -765,7 +765,7 @@ static int mptcp_setsockopt_v4_set_tos(struct mptcp_sock *msk, int optname,
__ip_sock_set_tos(ssk, val);
unlock_sock_fast(ssk, slow);
}
- release_sock(sk);
+ sockopt_release_sock(sk);
return 0;
}
@@ -794,7 +794,7 @@ static int mptcp_setsockopt_first_sf_only(struct mptcp_sock *msk, int level, int
int ret;
/* Limit to first subflow, before the connection establishment */
- lock_sock(sk);
+ sockopt_lock_sock(sk);
ssk = __mptcp_nmpc_sk(msk);
if (IS_ERR(ssk)) {
ret = PTR_ERR(ssk);
@@ -804,7 +804,7 @@ static int mptcp_setsockopt_first_sf_only(struct mptcp_sock *msk, int level, int
ret = tcp_setsockopt(ssk, level, optname, optval, optlen);
unlock:
- release_sock(sk);
+ sockopt_release_sock(sk);
return ret;
}
@@ -835,7 +835,7 @@ static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
if (ret)
return ret;
- lock_sock(sk);
+ sockopt_lock_sock(sk);
switch (optname) {
case TCP_INQ:
if (val < 0 || val > 1)
@@ -874,7 +874,7 @@ static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
ret = -ENOPROTOOPT;
}
- release_sock(sk);
+ sockopt_release_sock(sk);
return ret;
}
@@ -898,9 +898,9 @@ int mptcp_setsockopt(struct sock *sk, int level, int optname,
* is in TCP fallback, when TCP socket options are passed through
* to the one remaining subflow.
*/
- lock_sock(sk);
+ sockopt_lock_sock(sk);
ssk = __mptcp_tcp_fallback(msk);
- release_sock(sk);
+ sockopt_release_sock(sk);
if (ssk)
return tcp_setsockopt(ssk, level, optname, optval, optlen);
@@ -923,7 +923,7 @@ static int mptcp_getsockopt_first_sf_only(struct mptcp_sock *msk, int level, int
struct sock *ssk;
int ret;
- lock_sock(sk);
+ sockopt_lock_sock(sk);
ssk = msk->first;
if (ssk)
goto get;
@@ -938,7 +938,7 @@ static int mptcp_getsockopt_first_sf_only(struct mptcp_sock *msk, int level, int
ret = tcp_getsockopt(ssk, level, optname, optval, optlen);
out:
- release_sock(sk);
+ sockopt_release_sock(sk);
return ret;
}
@@ -1109,7 +1109,7 @@ static int mptcp_getsockopt_tcpinfo(struct mptcp_sock *msk, char __user *optval,
infoptr = optval + sfd.size_subflow_data;
- lock_sock(sk);
+ sockopt_lock_sock(sk);
mptcp_for_each_subflow(msk, subflow) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
@@ -1122,7 +1122,7 @@ static int mptcp_getsockopt_tcpinfo(struct mptcp_sock *msk, char __user *optval,
tcp_get_info(ssk, &info);
if (copy_to_user(infoptr, &info, sfd.size_user)) {
- release_sock(sk);
+ sockopt_release_sock(sk);
return -EFAULT;
}
@@ -1132,7 +1132,7 @@ static int mptcp_getsockopt_tcpinfo(struct mptcp_sock *msk, char __user *optval,
}
}
- release_sock(sk);
+ sockopt_release_sock(sk);
sfd.num_subflows = sfcount;
@@ -1201,7 +1201,7 @@ static int mptcp_getsockopt_subflow_addrs(struct mptcp_sock *msk, char __user *o
addrptr = optval + sfd.size_subflow_data;
- lock_sock(sk);
+ sockopt_lock_sock(sk);
mptcp_for_each_subflow(msk, subflow) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
@@ -1214,7 +1214,7 @@ static int mptcp_getsockopt_subflow_addrs(struct mptcp_sock *msk, char __user *o
mptcp_get_sub_addrs(ssk, &a);
if (copy_to_user(addrptr, &a, sfd.size_user)) {
- release_sock(sk);
+ sockopt_release_sock(sk);
return -EFAULT;
}
@@ -1224,7 +1224,7 @@ static int mptcp_getsockopt_subflow_addrs(struct mptcp_sock *msk, char __user *o
}
}
- release_sock(sk);
+ sockopt_release_sock(sk);
sfd.num_subflows = sfcount;
@@ -1310,7 +1310,7 @@ static int mptcp_getsockopt_full_info(struct mptcp_sock *msk, char __user *optva
sizeof(struct mptcp_subflow_info));
tcpinfoptr = u64_to_user_ptr(mfi.tcp_info);
- lock_sock(sk);
+ sockopt_lock_sock(sk);
mptcp_for_each_subflow(msk, subflow) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
struct mptcp_subflow_info sfinfo;
@@ -1340,7 +1340,7 @@ static int mptcp_getsockopt_full_info(struct mptcp_sock *msk, char __user *optva
tcpinfoptr += mfi.size_tcpinfo_user;
sfinfoptr += mfi.size_sfinfo_user;
}
- release_sock(sk);
+ sockopt_release_sock(sk);
mfi.num_subflows = sfcount;
if (mptcp_put_full_info(&mfi, optval, copylen, optlen))
@@ -1349,7 +1349,7 @@ static int mptcp_getsockopt_full_info(struct mptcp_sock *msk, char __user *optva
return 0;
fail_release:
- release_sock(sk);
+ sockopt_release_sock(sk);
return -EFAULT;
}
@@ -1504,9 +1504,9 @@ int mptcp_getsockopt(struct sock *sk, int level, int optname,
* is in TCP fallback, when socket options are passed through
* to the one remaining subflow.
*/
- lock_sock(sk);
+ sockopt_lock_sock(sk);
ssk = __mptcp_tcp_fallback(msk);
- release_sock(sk);
+ sockopt_release_sock(sk);
if (ssk)
return tcp_getsockopt(ssk, level, optname, optval, option);
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH mptcp-next v2 4/7] mptcp: reject sockopt requiring ssks' lock in BPF context
2026-07-21 2:40 [PATCH mptcp-next v2 0/7] mptcp: add bpf_setsockopt support Gang Yan
` (2 preceding siblings ...)
2026-07-21 2:40 ` [PATCH mptcp-next v2 3/7] mptcp: use sockopt_lock/release_sock in sockopt Gang Yan
@ 2026-07-21 2:40 ` Gang Yan
2026-07-21 2:41 ` [PATCH mptcp-next v2 5/7] mptcp: enable bpf_setsockopt on the master socket Gang Yan
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Gang Yan @ 2026-07-21 2:40 UTC (permalink / raw)
To: mptcp; +Cc: pabeni, Gang Yan
From: Gang Yan <yangang@kylinos.cn>
Several MPTCP setsockopt handlers need to acquire the subflow lock
via lock_sock(ssk) to propagate settings to each subflow. This lock
can sleep and is therefore not usable in BPF context where sleeping
is forbidden.
The short-term solution is to make any sockopt operation that requires
subflow-level lock fail with -EOPNOTSUPP when called from BPF context.
Signed-off-by: Gang Yan <yangang@kylinos.cn>
---
net/mptcp/sockopt.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index 1665438269e5..195f9ca342fb 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -185,6 +185,9 @@ static int mptcp_setsockopt_sol_socket_int(struct mptcp_sock *msk, int optname,
if (ret)
return ret;
+ if (has_current_bpf_ctx())
+ return -EOPNOTSUPP;
+
switch (optname) {
case SO_KEEPALIVE:
case SO_DEBUG:
@@ -218,6 +221,9 @@ static int mptcp_setsockopt_sol_socket_timestamping(struct mptcp_sock *msk,
struct so_timestamping timestamping;
int ret;
+ if (has_current_bpf_ctx())
+ return -EOPNOTSUPP;
+
if (optlen == sizeof(timestamping)) {
if (copy_from_sockptr(×tamping, optval,
sizeof(timestamping)))
@@ -265,6 +271,9 @@ static int mptcp_setsockopt_sol_socket_linger(struct mptcp_sock *msk, sockptr_t
sockptr_t kopt;
int ret;
+ if (has_current_bpf_ctx())
+ return -EOPNOTSUPP;
+
if (optlen < sizeof(ling))
return -EINVAL;
@@ -598,6 +607,9 @@ static int mptcp_setsockopt_sol_tcp_congestion(struct mptcp_sock *msk, sockptr_t
bool cap_net_admin;
int ret;
+ if (has_current_bpf_ctx())
+ return -EOPNOTSUPP;
+
if (optlen < 1)
return -EINVAL;
@@ -639,6 +651,9 @@ static int __mptcp_setsockopt_set_val(struct mptcp_sock *msk,
struct mptcp_subflow_context *subflow;
int err = 0;
+ if (has_current_bpf_ctx())
+ return -EOPNOTSUPP;
+
mptcp_for_each_subflow(msk, subflow) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
int ret;
@@ -662,6 +677,9 @@ static int __mptcp_setsockopt_sol_tcp_cork(struct mptcp_sock *msk, int val)
struct mptcp_subflow_context *subflow;
struct sock *sk = (struct sock *)msk;
+ if (has_current_bpf_ctx())
+ return -EOPNOTSUPP;
+
sockopt_seq_inc(msk);
msk->cork = !!val;
mptcp_for_each_subflow(msk, subflow) {
@@ -682,6 +700,9 @@ static int __mptcp_setsockopt_sol_tcp_nodelay(struct mptcp_sock *msk, int val)
struct mptcp_subflow_context *subflow;
struct sock *sk = (struct sock *)msk;
+ if (has_current_bpf_ctx())
+ return -EOPNOTSUPP;
+
sockopt_seq_inc(msk);
msk->nodelay = !!val;
mptcp_for_each_subflow(msk, subflow) {
@@ -749,6 +770,9 @@ static int mptcp_setsockopt_v4_set_tos(struct mptcp_sock *msk, int optname,
struct sock *sk = (struct sock *)msk;
int err, val;
+ if (has_current_bpf_ctx())
+ return -EOPNOTSUPP;
+
err = ip_setsockopt(sk, SOL_IP, optname, optval, optlen);
if (err != 0)
@@ -1610,6 +1634,9 @@ int mptcp_set_rcvlowat(struct sock *sk, int val)
if (sk->sk_protocol == IPPROTO_TCP)
return -EINVAL;
+ if (has_current_bpf_ctx())
+ return -EOPNOTSUPP;
+
if (sk->sk_userlocks & SOCK_RCVBUF_LOCK)
cap = sk->sk_rcvbuf >> 1;
else
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH mptcp-next v2 5/7] mptcp: enable bpf_setsockopt on the master socket
2026-07-21 2:40 [PATCH mptcp-next v2 0/7] mptcp: add bpf_setsockopt support Gang Yan
` (3 preceding siblings ...)
2026-07-21 2:40 ` [PATCH mptcp-next v2 4/7] mptcp: reject sockopt requiring ssks' lock in BPF context Gang Yan
@ 2026-07-21 2:41 ` Gang Yan
2026-07-21 2:41 ` [PATCH mptcp-next v2 6/7] mptcp: add TCP_CONNECT_CB sock_ops hook Gang Yan
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Gang Yan @ 2026-07-21 2:41 UTC (permalink / raw)
To: mptcp; +Cc: pabeni, Gang Yan
From: Gang Yan <yangang@kylinos.cn>
bpf_setsockopt() currently cannot be used on mptcp master sockets:
__bpf_setsockopt() dispatches by level to the protocol-agnostic
sol_*_sockopt() helpers, which either reject the msk
(sk_protocol == IPPROTO_MPTCP) and the ssk (sk_is_tcp() is false)
or bypass mptcp's own dispatch (e.g. SOL_IP going straight to
do_ip_setsockopt()).
This patch routes any level to mptcp_setsockopt(), which already
handles all levels.
Signed-off-by: Gang Yan <yangang@kylinos.cn>
---
include/net/mptcp.h | 9 +++++++++
net/core/filter.c | 7 +++++++
2 files changed, 16 insertions(+)
diff --git a/include/net/mptcp.h b/include/net/mptcp.h
index 333bde2a0b76..fcce2b7e9ef9 100644
--- a/include/net/mptcp.h
+++ b/include/net/mptcp.h
@@ -237,6 +237,9 @@ static inline __be32 mptcp_reset_option(const struct sk_buff *skb)
}
void mptcp_active_detect_blackhole(struct sock *sk, bool expired);
+
+int mptcp_setsockopt(struct sock *sk, int level, int optname,
+ sockptr_t optval, unsigned int optlen);
#else
static inline void mptcp_init(void)
@@ -314,6 +317,12 @@ static inline struct request_sock *mptcp_subflow_reqsk_alloc(const struct reques
static inline __be32 mptcp_reset_option(const struct sk_buff *skb) { return htonl(0u); }
static inline void mptcp_active_detect_blackhole(struct sock *sk, bool expired) { }
+
+static inline int mptcp_setsockopt(struct sock *sk, int level, int optname,
+ sockptr_t optval, unsigned int optlen)
+{
+ return -EINVAL;
+}
#endif /* CONFIG_MPTCP */
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
diff --git a/net/core/filter.c b/net/core/filter.c
index b446aa8be5c3..18e650bf7393 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -5683,6 +5683,13 @@ static int __bpf_setsockopt(struct sock *sk, int level, int optname,
if (!sk_fullsock(sk))
return -EINVAL;
+ /* Route any bpf_setsockopt on the mptcp socket to mptcp_setsockopt,
+ * which handles all levels.
+ */
+ if (IS_ENABLED(CONFIG_MPTCP) && sk->sk_protocol == IPPROTO_MPTCP)
+ return mptcp_setsockopt(sk, level, optname,
+ KERNEL_SOCKPTR(optval), optlen);
+
if (level == SOL_SOCKET)
return sol_socket_sockopt(sk, optname, optval, &optlen, false);
else if (IS_ENABLED(CONFIG_INET) && level == SOL_IP)
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH mptcp-next v2 6/7] mptcp: add TCP_CONNECT_CB sock_ops hook
2026-07-21 2:40 [PATCH mptcp-next v2 0/7] mptcp: add bpf_setsockopt support Gang Yan
` (4 preceding siblings ...)
2026-07-21 2:41 ` [PATCH mptcp-next v2 5/7] mptcp: enable bpf_setsockopt on the master socket Gang Yan
@ 2026-07-21 2:41 ` Gang Yan
2026-07-21 2:41 ` [PATCH mptcp-next v2 7/7] selftests: bpf: verify mptcp bpf_setsockopt from TCP_CONNECT_CB Gang Yan
2026-07-21 3:46 ` [PATCH mptcp-next v2 0/7] mptcp: add bpf_setsockopt support MPTCP CI
7 siblings, 0 replies; 9+ messages in thread
From: Gang Yan @ 2026-07-21 2:41 UTC (permalink / raw)
To: mptcp; +Cc: pabeni, Gang Yan
From: Gang Yan <yangang@kylinos.cn>
This patch adds a helper named 'mptcp_call_bpf' like tcp_call_bpf.
Invoke the new helper from mptcp_connect() with
BPF_SOCK_OPS_TCP_CONNECT_CB, placed after the subflow lock is acquired
and before tcp_connect(). At this point the msk lock is held by
__inet_stream_connect(), mirroring the placement of TCP_CONNECT_CB in
tcp_v4_connect()/tcp_v6_connect().
'bpf_sock_ops_cb_flags_set' can be called via msk, so using sk_is_tcp()
to avoid this issue.
Signed-off-by: Gang Yan <yangang@kylinos.cn>
---
Note:
I'm using CONFIG_BPF_JIT here because it is used in bpf.c for
conditional compilation. However, maybe the macro in bpf.c should be
changed to CONFIG_BPF instead?
WDYT?
---
net/core/filter.c | 3 +++
net/mptcp/protocol.c | 6 ++++++
net/mptcp/protocol.h | 29 +++++++++++++++++++++++++++++
3 files changed, 38 insertions(+)
diff --git a/net/core/filter.c b/net/core/filter.c
index 18e650bf7393..2f9778181279 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -6097,6 +6097,9 @@ BPF_CALL_2(bpf_sock_ops_cb_flags_set, struct bpf_sock_ops_kern *, bpf_sock,
if (!IS_ENABLED(CONFIG_INET) || !sk_fullsock(sk))
return -EINVAL;
+ if (!sk_is_tcp(sk))
+ return -EOPNOTSUPP;
+
tcp_sk(sk)->bpf_sock_ops_cb_flags = val;
return argval & (~BPF_SOCK_OPS_ALL_CB_FLAGS);
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index ffcf5a1788f6..837bb76e5c2d 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -4149,6 +4149,12 @@ static int mptcp_connect(struct sock *sk, struct sockaddr_unsized *uaddr,
if (!msk->fastopening)
lock_sock(ssk);
+ /* Notify cgroup BPF on the msk before initiating the subflow connect.
+ * Mirrors BPF_SOCK_OPS_TCP_CONNECT_CB; msk lock is held by the
+ * caller (__inet_stream_connect) and ssk is held before.
+ */
+ mptcp_call_bpf(sk, BPF_SOCK_OPS_TCP_CONNECT_CB, 0, NULL);
+
/* the following mirrors closely a very small chunk of code from
* __inet_stream_connect()
*/
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index da40c6f3705f..e862310b2f81 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -1374,4 +1374,33 @@ mptcp_token_join_cookie_init_state(struct mptcp_subflow_request_sock *subflow_re
static inline void mptcp_join_cookie_init(void) {}
#endif
+#ifdef CONFIG_BPF_JIT
+static inline int mptcp_call_bpf(struct sock *sk, int op, u32 nargs, u32 *args)
+{
+ struct bpf_sock_ops_kern sock_ops;
+ int ret;
+
+ memset(&sock_ops, 0, offsetof(struct bpf_sock_ops_kern, temp));
+
+ if (sk_fullsock(sk)) {
+ sock_ops.is_fullsock = 1;
+ sock_owned_by_me(sk);
+ }
+
+ sock_ops.sk = sk;
+ sock_ops.op = op;
+
+ if (nargs > 0)
+ memcpy(sock_ops.args, args, nargs * sizeof(*args));
+
+ ret = BPF_CGROUP_RUN_PROG_SOCK_OPS(&sock_ops);
+ return ret == 0 ? sock_ops.reply : -1;
+}
+#else
+static inline int mptcp_call_bpf(struct sock *sk, int op, u32 nargs, u32 *args)
+{
+ return -1;
+}
+#endif
+
#endif /* __MPTCP_PROTOCOL_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH mptcp-next v2 7/7] selftests: bpf: verify mptcp bpf_setsockopt from TCP_CONNECT_CB
2026-07-21 2:40 [PATCH mptcp-next v2 0/7] mptcp: add bpf_setsockopt support Gang Yan
` (5 preceding siblings ...)
2026-07-21 2:41 ` [PATCH mptcp-next v2 6/7] mptcp: add TCP_CONNECT_CB sock_ops hook Gang Yan
@ 2026-07-21 2:41 ` Gang Yan
2026-07-21 3:46 ` [PATCH mptcp-next v2 0/7] mptcp: add bpf_setsockopt support MPTCP CI
7 siblings, 0 replies; 9+ messages in thread
From: Gang Yan @ 2026-07-21 2:41 UTC (permalink / raw)
To: mptcp; +Cc: pabeni, Gang Yan
From: Gang Yan <yangang@kylinos.cn>
Add a BPF sockops program attached to BPF_CGROUP_SOCK_OPS that fires
on BPF_SOCK_OPS_TCP_CONNECT_CB and exercises bpf_setsockopt() on the
mptcp master socket (msk).
Two scenarios are covered by the new "setsockopt" subtest:
- TCP_INQ must succeed and the value (1) must be observable from
userspace via getsockopt() on the mptcp socket.
- TCP_CONGESTION needs the subflow lock and is therefore rejected
in bpf context with -EOPNOTSUPP.
Signed-off-by: Gang Yan <yangang@kylinos.cn>
---
.../testing/selftests/bpf/prog_tests/mptcp.c | 61 +++++++++++++++++++
.../selftests/bpf/progs/mptcp_setsockopt.c | 32 ++++++++++
2 files changed, 93 insertions(+)
create mode 100644 tools/testing/selftests/bpf/progs/mptcp_setsockopt.c
diff --git a/tools/testing/selftests/bpf/prog_tests/mptcp.c b/tools/testing/selftests/bpf/prog_tests/mptcp.c
index 7f48fd9e94e1..ad59f8fcbb68 100644
--- a/tools/testing/selftests/bpf/prog_tests/mptcp.c
+++ b/tools/testing/selftests/bpf/prog_tests/mptcp.c
@@ -18,6 +18,7 @@
#include "mptcp_bpf_rr.skel.h"
#include "mptcp_bpf_red.skel.h"
#include "mptcp_bpf_burst.skel.h"
+#include "mptcp_setsockopt.skel.h"
#define NS_TEST "mptcp_ns"
#define ADDR_1 "10.0.1.1"
@@ -813,6 +814,64 @@ static void test_burst(void)
mptcp_bpf_burst__destroy(skel);
}
+static void test_setsockopt(void)
+{
+ struct mptcp_setsockopt *skel;
+ struct netns_obj *netns;
+ int cgroup_fd, server_fd, client_fd;
+ int err;
+ int inq;
+ socklen_t len;
+
+ cgroup_fd = test__join_cgroup("/mptcp_setsockopt");
+ if (!ASSERT_OK_FD(cgroup_fd, "join_cgroup"))
+ return;
+
+ skel = mptcp_setsockopt__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel_open_load"))
+ goto close_cgroup;
+
+ skel->links.mptcp_connect_cb =
+ bpf_program__attach_cgroup(skel->progs.mptcp_connect_cb,
+ cgroup_fd);
+ if (!ASSERT_OK_PTR(skel->links.mptcp_connect_cb, "attach connect_cb"))
+ goto skel_destroy;
+
+ netns = netns_new(NS_TEST, true);
+ if (!ASSERT_OK_PTR(netns, "netns_new"))
+ goto skel_destroy;
+
+ server_fd = start_mptcp_server(AF_INET, NULL, 0, 0);
+ if (!ASSERT_OK_FD(server_fd, "start_mptcp_server"))
+ goto close_netns;
+
+ client_fd = connect_to_fd(server_fd, 0);
+ if (!ASSERT_OK_FD(client_fd, "connect_to_fd"))
+ goto close_server;
+
+ /* TCP_INQ should be set successfullly */
+ ASSERT_EQ(skel->bss->connect_cb_inq_ret, 0, "connect_cb TCP_INQ ret");
+
+ len = sizeof(inq);
+ err = getsockopt(client_fd, SOL_TCP, TCP_INQ, &inq, &len);
+ if (ASSERT_OK(err, "getsockopt TCP_INQ"))
+ ASSERT_EQ(inq, 1, "TCP_INQ value");
+
+ /* TCP_CONGESTION should be -EOPNOTSUPP */
+ ASSERT_EQ(skel->bss->connect_cb_cc_ret, -EOPNOTSUPP,
+ "connect_cb TCP_CONGESTION ret");
+
+ close(client_fd);
+close_server:
+ close(server_fd);
+close_netns:
+ netns_free(netns);
+skel_destroy:
+ mptcp_setsockopt__destroy(skel);
+close_cgroup:
+ close(cgroup_fd);
+}
+
void test_mptcp(void)
{
if (test__start_subtest("base"))
@@ -835,4 +894,6 @@ void test_mptcp(void)
test_red();
if (test__start_subtest("burst"))
test_burst();
+ if (test__start_subtest("setsockopt"))
+ test_setsockopt();
}
diff --git a/tools/testing/selftests/bpf/progs/mptcp_setsockopt.c b/tools/testing/selftests/bpf/progs/mptcp_setsockopt.c
new file mode 100644
index 000000000000..f54b77ea6c5f
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/mptcp_setsockopt.c
@@ -0,0 +1,32 @@
+#include "bpf_tracing_net.h"
+#include "mptcp_bpf.h"
+
+#ifndef TCP_INQ
+#define TCP_INQ 36
+#endif
+
+int connect_cb_inq_ret;
+int connect_cb_cc_ret;
+
+char cc_reno[TCP_CA_NAME_MAX] = "reno";
+
+SEC("sockops")
+int mptcp_connect_cb(struct bpf_sock_ops *skops)
+{
+ struct bpf_sock *sk = skops->sk;
+ int one = 1;
+
+ if (skops->op != BPF_SOCK_OPS_TCP_CONNECT_CB)
+ return 1;
+
+ if (!sk || sk->protocol != IPPROTO_MPTCP)
+ return 1;
+
+ connect_cb_inq_ret =
+ bpf_setsockopt(skops, SOL_TCP, TCP_INQ, &one, sizeof(one));
+ connect_cb_cc_ret =
+ bpf_setsockopt(skops, SOL_TCP, TCP_CONGESTION,
+ cc_reno, sizeof(cc_reno));
+
+ return 1;
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH mptcp-next v2 0/7] mptcp: add bpf_setsockopt support
2026-07-21 2:40 [PATCH mptcp-next v2 0/7] mptcp: add bpf_setsockopt support Gang Yan
` (6 preceding siblings ...)
2026-07-21 2:41 ` [PATCH mptcp-next v2 7/7] selftests: bpf: verify mptcp bpf_setsockopt from TCP_CONNECT_CB Gang Yan
@ 2026-07-21 3:46 ` MPTCP CI
7 siblings, 0 replies; 9+ messages in thread
From: MPTCP CI @ 2026-07-21 3:46 UTC (permalink / raw)
To: Gang Yan; +Cc: mptcp
Hi Gang,
Thank you for your modifications, that's great!
Our CI did some validations and here is its report:
- KVM Validation: normal (except selftest_mptcp_join): Success! ✅
- KVM Validation: normal (only selftest_mptcp_join): Success! ✅
- KVM Validation: debug (except selftest_mptcp_join): Success! ✅
- KVM Validation: debug (only selftest_mptcp_join): Success! ✅
- 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/29797149050
Initiator: Patchew Applier
Commits: https://github.com/multipath-tcp/mptcp_net-next/commits/6e51b04fa682
Patchwork: https://patchwork.kernel.org/project/mptcp/list/?series=1131261
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] 9+ messages in thread
end of thread, other threads:[~2026-07-21 3:46 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-21 2:40 [PATCH mptcp-next v2 0/7] mptcp: add bpf_setsockopt support Gang Yan
2026-07-21 2:40 ` [PATCH mptcp-next v2 1/7] mptcp: drop unused @max arg of __mptcp_setsockopt_set_val Gang Yan
2026-07-21 2:40 ` [PATCH mptcp-next v2 2/7] mptcp: take TCP_MAXSEG handling into __mptcp_setsockopt_set_val Gang Yan
2026-07-21 2:40 ` [PATCH mptcp-next v2 3/7] mptcp: use sockopt_lock/release_sock in sockopt Gang Yan
2026-07-21 2:40 ` [PATCH mptcp-next v2 4/7] mptcp: reject sockopt requiring ssks' lock in BPF context Gang Yan
2026-07-21 2:41 ` [PATCH mptcp-next v2 5/7] mptcp: enable bpf_setsockopt on the master socket Gang Yan
2026-07-21 2:41 ` [PATCH mptcp-next v2 6/7] mptcp: add TCP_CONNECT_CB sock_ops hook Gang Yan
2026-07-21 2:41 ` [PATCH mptcp-next v2 7/7] selftests: bpf: verify mptcp bpf_setsockopt from TCP_CONNECT_CB Gang Yan
2026-07-21 3:46 ` [PATCH mptcp-next v2 0/7] mptcp: add bpf_setsockopt support MPTCP CI
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.