All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH mptcp-next 0/5] mptcp: add bpf_setsockopt support in
@ 2026-07-13  9:57 Gang Yan
  2026-07-13  9:57 ` [PATCH mptcp-next 1/5] mptcp: use sockopt_lock/release_sock in sockopt Gang Yan
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Gang Yan @ 2026-07-13  9:57 UTC (permalink / raw)
  To: mptcp; +Cc: pabeni, Gang Yan

From: Gang Yan <yangang@kylinos.cn>

It adds a hook in mptcp_connect, which enables setting socket options
automatically before the connect.

- Patch 1-2 have been reviewed by Paolo before, and ready for merge [1][2].
- Patch 3 let mptcp_setsockopt to take cover all the sockopt levels for
  msk.
- Patch 4 introduces a helper and a hook like tcp does.
- Patch 5 adds a self-test case for bpf_setsockopt in mptcp.

For now, I only used AI to help with grammar optimization of the commit
messages.

[1] https://patchwork.kernel.org/project/mptcp/patch/20260522-sockopt_lock-v5-2-108629a46e98@kylinos.cn/
[2] https://patchwork.kernel.org/project/mptcp/patch/20260522-sockopt_lock-v5-4-108629a46e98@kylinos.cn/

Gang Yan (5):
  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                             |   6 +
 net/mptcp/protocol.c                          |   6 +
 net/mptcp/protocol.h                          |  30 +++++
 net/mptcp/sockopt.c                           | 111 +++++++++++-------
 .../testing/selftests/bpf/prog_tests/mptcp.c  |  61 ++++++++++
 .../selftests/bpf/progs/mptcp_setsockopt.c    |  32 +++++
 7 files changed, 213 insertions(+), 42 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/progs/mptcp_setsockopt.c

-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH mptcp-next 1/5] mptcp: use sockopt_lock/release_sock in sockopt
  2026-07-13  9:57 [PATCH mptcp-next 0/5] mptcp: add bpf_setsockopt support in Gang Yan
@ 2026-07-13  9:57 ` Gang Yan
  2026-07-13  9:57 ` [PATCH mptcp-next 2/5] mptcp: reject sockopt requiring ssks' lock in BPF context Gang Yan
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Gang Yan @ 2026-07-13  9:57 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 fcf6feb2a9eb..342aec7f9086 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;
 }
 
@@ -857,7 +857,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)
@@ -900,7 +900,7 @@ static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
 		ret = -ENOPROTOOPT;
 	}
 
-	release_sock(sk);
+	sockopt_release_sock(sk);
 	return ret;
 }
 
@@ -924,9 +924,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);
 
@@ -949,7 +949,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;
@@ -964,7 +964,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;
 }
 
@@ -1135,7 +1135,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);
@@ -1148,7 +1148,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;
 			}
 
@@ -1158,7 +1158,7 @@ static int mptcp_getsockopt_tcpinfo(struct mptcp_sock *msk, char __user *optval,
 		}
 	}
 
-	release_sock(sk);
+	sockopt_release_sock(sk);
 
 	sfd.num_subflows = sfcount;
 
@@ -1227,7 +1227,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);
@@ -1240,7 +1240,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;
 			}
 
@@ -1250,7 +1250,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;
 
@@ -1336,7 +1336,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;
@@ -1366,7 +1366,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))
@@ -1375,7 +1375,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;
 }
 
@@ -1530,9 +1530,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] 6+ messages in thread

* [PATCH mptcp-next 2/5] mptcp: reject sockopt requiring ssks' lock in BPF context
  2026-07-13  9:57 [PATCH mptcp-next 0/5] mptcp: add bpf_setsockopt support in Gang Yan
  2026-07-13  9:57 ` [PATCH mptcp-next 1/5] mptcp: use sockopt_lock/release_sock in sockopt Gang Yan
@ 2026-07-13  9:57 ` Gang Yan
  2026-07-13  9:57 ` [PATCH mptcp-next 3/5] mptcp: enable bpf_setsockopt on the master socket Gang Yan
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Gang Yan @ 2026-07-13  9:57 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 342aec7f9086..0c391247eb4c 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(&timestamping, 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, int max,
 	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)
@@ -1654,6 +1678,9 @@ int mptcp_set_rcvlowat(struct sock *sk, int val)
 	if (space <= sk->sk_rcvbuf)
 		return 0;
 
+	if (has_current_bpf_ctx())
+		return -EOPNOTSUPP;
+
 	/* propagate the rcvbuf changes to all the subflows */
 	WRITE_ONCE(sk->sk_rcvbuf, space);
 	mptcp_for_each_subflow(mptcp_sk(sk), subflow) {
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH mptcp-next 3/5] mptcp: enable bpf_setsockopt on the master socket
  2026-07-13  9:57 [PATCH mptcp-next 0/5] mptcp: add bpf_setsockopt support in Gang Yan
  2026-07-13  9:57 ` [PATCH mptcp-next 1/5] mptcp: use sockopt_lock/release_sock in sockopt Gang Yan
  2026-07-13  9:57 ` [PATCH mptcp-next 2/5] mptcp: reject sockopt requiring ssks' lock in BPF context Gang Yan
@ 2026-07-13  9:57 ` Gang Yan
  2026-07-13  9:57 ` [PATCH mptcp-next 4/5] mptcp: add TCP_CONNECT_CB sock_ops hook Gang Yan
  2026-07-13  9:57 ` [PATCH mptcp-next 5/5] selftests: bpf: verify mptcp bpf_setsockopt from TCP_CONNECT_CB Gang Yan
  4 siblings, 0 replies; 6+ messages in thread
From: Gang Yan @ 2026-07-13  9:57 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   | 6 ++++++
 2 files changed, 15 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..9406984dfdbe 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -5683,6 +5683,12 @@ 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_set_sockopt,
+	 * 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] 6+ messages in thread

* [PATCH mptcp-next 4/5] mptcp: add TCP_CONNECT_CB sock_ops hook
  2026-07-13  9:57 [PATCH mptcp-next 0/5] mptcp: add bpf_setsockopt support in Gang Yan
                   ` (2 preceding siblings ...)
  2026-07-13  9:57 ` [PATCH mptcp-next 3/5] mptcp: enable bpf_setsockopt on the master socket Gang Yan
@ 2026-07-13  9:57 ` Gang Yan
  2026-07-13  9:57 ` [PATCH mptcp-next 5/5] selftests: bpf: verify mptcp bpf_setsockopt from TCP_CONNECT_CB Gang Yan
  4 siblings, 0 replies; 6+ messages in thread
From: Gang Yan @ 2026-07-13  9:57 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().

Signed-off-by: Gang Yan <yangang@kylinos.cn>
---
 net/mptcp/protocol.c |  6 ++++++
 net/mptcp/protocol.h | 30 ++++++++++++++++++++++++++++++
 2 files changed, 36 insertions(+)

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..b4640ba83a8b 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -1374,4 +1374,34 @@ 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_ops.is_locked_tcp_sock = 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] 6+ messages in thread

* [PATCH mptcp-next 5/5] selftests: bpf: verify mptcp bpf_setsockopt from TCP_CONNECT_CB
  2026-07-13  9:57 [PATCH mptcp-next 0/5] mptcp: add bpf_setsockopt support in Gang Yan
                   ` (3 preceding siblings ...)
  2026-07-13  9:57 ` [PATCH mptcp-next 4/5] mptcp: add TCP_CONNECT_CB sock_ops hook Gang Yan
@ 2026-07-13  9:57 ` Gang Yan
  4 siblings, 0 replies; 6+ messages in thread
From: Gang Yan @ 2026-07-13  9:57 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] 6+ messages in thread

end of thread, other threads:[~2026-07-13  9:58 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-13  9:57 [PATCH mptcp-next 0/5] mptcp: add bpf_setsockopt support in Gang Yan
2026-07-13  9:57 ` [PATCH mptcp-next 1/5] mptcp: use sockopt_lock/release_sock in sockopt Gang Yan
2026-07-13  9:57 ` [PATCH mptcp-next 2/5] mptcp: reject sockopt requiring ssks' lock in BPF context Gang Yan
2026-07-13  9:57 ` [PATCH mptcp-next 3/5] mptcp: enable bpf_setsockopt on the master socket Gang Yan
2026-07-13  9:57 ` [PATCH mptcp-next 4/5] mptcp: add TCP_CONNECT_CB sock_ops hook Gang Yan
2026-07-13  9:57 ` [PATCH mptcp-next 5/5] selftests: bpf: verify mptcp bpf_setsockopt from TCP_CONNECT_CB Gang Yan

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.