Skip to content

Commit cd19b88

Browse files
Geliang TangMartin KaFai Lau
authored andcommitted
selftests/bpf: Add getsockopt to inspect mptcp subflow
This patch adds a "cgroup/getsockopt" way to inspect the subflows of an MPTCP socket, and verify the modifications done by the same BPF program in the previous commit: a different mark per subflow, and a different TCP CC set on the second one. This new hook will be used by the next commit to verify the socket options set on each subflow. This extra "cgroup/getsockopt" prog walks the msk->conn_list and use bpf_core_cast to cast a pointer for readonly. It allows to inspect all the fields of a structure. Note that on the kernel side, the MPTCP socket stores a list of subflows under 'msk->conn_list'. They can be iterated using the generic 'list' helpers. They have been imported here, with a small difference: list_for_each_entry() uses 'can_loop' to limit the number of iterations, and ease its use. Because only data need to be read here, it is enough to use this technique. It is planned to use bpf_iter, when BPF programs will be used to modify data from the different subflows. mptcp_subflow_tcp_sock() and mptcp_for_each_stubflow() helpers have also be imported. Suggested-by: Martin KaFai Lau <martin.lau@kernel.org> Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> Reviewed-by: Matthieu Baerts (NGI0) <matttbe@kernel.org> Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org> Link: https://lore.kernel.org/r/20240926-upstream-bpf-next-20240506-mptcp-subflow-test-v7-2-d26029e15cdd@kernel.org Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
1 parent 83752e1 commit cd19b88

File tree

3 files changed

+112
-1
lines changed

3 files changed

+112
-1
lines changed

MAINTAINERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16281,7 +16281,7 @@ F: include/net/mptcp.h
1628116281
F: include/trace/events/mptcp.h
1628216282
F: include/uapi/linux/mptcp*.h
1628316283
F: net/mptcp/
16284-
F: tools/testing/selftests/bpf/*/*mptcp*.c
16284+
F: tools/testing/selftests/bpf/*/*mptcp*.[ch]
1628516285
F: tools/testing/selftests/net/mptcp/
1628616286

1628716287
NETWORKING [TCP]
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
2+
#ifndef __MPTCP_BPF_H__
3+
#define __MPTCP_BPF_H__
4+
5+
#include "bpf_experimental.h"
6+
7+
/* list helpers from include/linux/list.h */
8+
static inline int list_is_head(const struct list_head *list,
9+
const struct list_head *head)
10+
{
11+
return list == head;
12+
}
13+
14+
#define list_entry(ptr, type, member) \
15+
container_of(ptr, type, member)
16+
17+
#define list_first_entry(ptr, type, member) \
18+
list_entry((ptr)->next, type, member)
19+
20+
#define list_next_entry(pos, member) \
21+
list_entry((pos)->member.next, typeof(*(pos)), member)
22+
23+
#define list_entry_is_head(pos, head, member) \
24+
list_is_head(&pos->member, (head))
25+
26+
/* small difference: 'can_loop' has been added in the conditions */
27+
#define list_for_each_entry(pos, head, member) \
28+
for (pos = list_first_entry(head, typeof(*pos), member); \
29+
!list_entry_is_head(pos, head, member) && can_loop; \
30+
pos = list_next_entry(pos, member))
31+
32+
/* mptcp helpers from protocol.h */
33+
#define mptcp_for_each_subflow(__msk, __subflow) \
34+
list_for_each_entry(__subflow, &((__msk)->conn_list), node)
35+
36+
static __always_inline struct sock *
37+
mptcp_subflow_tcp_sock(const struct mptcp_subflow_context *subflow)
38+
{
39+
return subflow->tcp_sock;
40+
}
41+
42+
#endif

tools/testing/selftests/bpf/progs/mptcp_subflow.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44

55
/* vmlinux.h, bpf_helpers.h and other 'define' */
66
#include "bpf_tracing_net.h"
7+
#include "mptcp_bpf.h"
78

89
char _license[] SEC("license") = "GPL";
910

1011
char cc[TCP_CA_NAME_MAX] = "reno";
12+
int pid;
1113

1214
/* Associate a subflow counter to each token */
1315
struct {
@@ -57,3 +59,70 @@ int mptcp_subflow(struct bpf_sock_ops *skops)
5759

5860
return 1;
5961
}
62+
63+
static int _check_getsockopt_subflow_mark(struct mptcp_sock *msk, struct bpf_sockopt *ctx)
64+
{
65+
struct mptcp_subflow_context *subflow;
66+
int i = 0;
67+
68+
mptcp_for_each_subflow(msk, subflow) {
69+
struct sock *ssk;
70+
71+
ssk = mptcp_subflow_tcp_sock(bpf_core_cast(subflow,
72+
struct mptcp_subflow_context));
73+
74+
if (ssk->sk_mark != ++i) {
75+
ctx->retval = -2;
76+
break;
77+
}
78+
}
79+
80+
return 1;
81+
}
82+
83+
static int _check_getsockopt_subflow_cc(struct mptcp_sock *msk, struct bpf_sockopt *ctx)
84+
{
85+
struct mptcp_subflow_context *subflow;
86+
87+
mptcp_for_each_subflow(msk, subflow) {
88+
struct inet_connection_sock *icsk;
89+
struct sock *ssk;
90+
91+
ssk = mptcp_subflow_tcp_sock(bpf_core_cast(subflow,
92+
struct mptcp_subflow_context));
93+
icsk = bpf_core_cast(ssk, struct inet_connection_sock);
94+
95+
if (ssk->sk_mark == 2 &&
96+
__builtin_memcmp(icsk->icsk_ca_ops->name, cc, TCP_CA_NAME_MAX)) {
97+
ctx->retval = -2;
98+
break;
99+
}
100+
}
101+
102+
return 1;
103+
}
104+
105+
SEC("cgroup/getsockopt")
106+
int _getsockopt_subflow(struct bpf_sockopt *ctx)
107+
{
108+
struct bpf_sock *sk = ctx->sk;
109+
struct mptcp_sock *msk;
110+
111+
if (bpf_get_current_pid_tgid() >> 32 != pid)
112+
return 1;
113+
114+
if (!sk || sk->protocol != IPPROTO_MPTCP ||
115+
(!(ctx->level == SOL_SOCKET && ctx->optname == SO_MARK) &&
116+
!(ctx->level == SOL_TCP && ctx->optname == TCP_CONGESTION)))
117+
return 1;
118+
119+
msk = bpf_core_cast(sk, struct mptcp_sock);
120+
if (msk->pm.subflows != 1) {
121+
ctx->retval = -1;
122+
return 1;
123+
}
124+
125+
if (ctx->optname == SO_MARK)
126+
return _check_getsockopt_subflow_mark(msk, ctx);
127+
return _check_getsockopt_subflow_cc(msk, ctx);
128+
}

0 commit comments

Comments
 (0)