xref: /linux/tools/testing/selftests/bpf/progs/mptcp_subflow.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2020, Tessares SA. */
3 /* Copyright (c) 2024, Kylin Software */
4 
5 /* vmlinux.h, bpf_helpers.h and other 'define' */
6 #include "bpf_tracing_net.h"
7 #include "mptcp_bpf.h"
8 
9 char _license[] SEC("license") = "GPL";
10 
11 char cc[TCP_CA_NAME_MAX] = "reno";
12 int pid;
13 
14 /* Associate a subflow counter to each token */
15 struct {
16 	__uint(type, BPF_MAP_TYPE_HASH);
17 	__uint(key_size, sizeof(__u32));
18 	__uint(value_size, sizeof(__u32));
19 	__uint(max_entries, 100);
20 } mptcp_sf SEC(".maps");
21 
22 SEC("sockops")
23 int mptcp_subflow(struct bpf_sock_ops *skops)
24 {
25 	__u32 init = 1, key, mark, *cnt;
26 	struct mptcp_sock *msk;
27 	struct bpf_sock *sk;
28 	int err;
29 
30 	if (skops->op != BPF_SOCK_OPS_TCP_CONNECT_CB)
31 		return 1;
32 
33 	sk = skops->sk;
34 	if (!sk)
35 		return 1;
36 
37 	msk = bpf_skc_to_mptcp_sock(sk);
38 	if (!msk)
39 		return 1;
40 
41 	key = msk->token;
42 	cnt = bpf_map_lookup_elem(&mptcp_sf, &key);
43 	if (cnt) {
44 		/* A new subflow is added to an existing MPTCP connection */
45 		__sync_fetch_and_add(cnt, 1);
46 		mark = *cnt;
47 	} else {
48 		/* A new MPTCP connection is just initiated and this is its primary subflow */
49 		bpf_map_update_elem(&mptcp_sf, &key, &init, BPF_ANY);
50 		mark = init;
51 	}
52 
53 	/* Set the mark of the subflow's socket based on appearance order */
54 	err = bpf_setsockopt(skops, SOL_SOCKET, SO_MARK, &mark, sizeof(mark));
55 	if (err < 0)
56 		return 1;
57 	if (mark == 2)
58 		err = bpf_setsockopt(skops, SOL_TCP, TCP_CONGESTION, cc, TCP_CA_NAME_MAX);
59 
60 	return 1;
61 }
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 }
129