]> Git Repo - linux.git/blob - tools/testing/selftests/bpf/progs/setget_sockopt.c
Linux 6.14-rc3
[linux.git] / tools / testing / selftests / bpf / progs / setget_sockopt.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) Meta Platforms, Inc. and affiliates. */
3
4 #include "vmlinux.h"
5 #include "bpf_tracing_net.h"
6 #include <bpf/bpf_core_read.h>
7 #include <bpf/bpf_helpers.h>
8 #include <bpf/bpf_tracing.h>
9 #include "bpf_misc.h"
10
11 extern unsigned long CONFIG_HZ __kconfig;
12
13 const volatile char veth[IFNAMSIZ];
14 const volatile int veth_ifindex;
15
16 int nr_listen;
17 int nr_passive;
18 int nr_active;
19 int nr_connect;
20 int nr_binddev;
21 int nr_socket_post_create;
22 int nr_fin_wait1;
23
24 struct sockopt_test {
25         int opt;
26         int new;
27         int restore;
28         int expected;
29         int tcp_expected;
30         unsigned int flip:1;
31 };
32
33 static const char not_exist_cc[] = "not_exist";
34 static const char cubic_cc[] = "cubic";
35 static const char reno_cc[] = "reno";
36
37 static const struct sockopt_test sol_socket_tests[] = {
38         { .opt = SO_REUSEADDR, .flip = 1, },
39         { .opt = SO_SNDBUF, .new = 8123, .expected = 8123 * 2, },
40         { .opt = SO_RCVBUF, .new = 8123, .expected = 8123 * 2, },
41         { .opt = SO_KEEPALIVE, .flip = 1, },
42         { .opt = SO_PRIORITY, .new = 0xeb9f, .expected = 0xeb9f, },
43         { .opt = SO_REUSEPORT, .flip = 1, },
44         { .opt = SO_RCVLOWAT, .new = 8123, .expected = 8123, },
45         { .opt = SO_MARK, .new = 0xeb9f, .expected = 0xeb9f, },
46         { .opt = SO_MAX_PACING_RATE, .new = 0xeb9f, .expected = 0xeb9f, },
47         { .opt = SO_TXREHASH, .flip = 1, },
48         { .opt = 0, },
49 };
50
51 static const struct sockopt_test sol_tcp_tests[] = {
52         { .opt = TCP_NODELAY, .flip = 1, },
53         { .opt = TCP_KEEPIDLE, .new = 123, .expected = 123, .restore = 321, },
54         { .opt = TCP_KEEPINTVL, .new = 123, .expected = 123, .restore = 321, },
55         { .opt = TCP_KEEPCNT, .new = 123, .expected = 123, .restore = 124, },
56         { .opt = TCP_SYNCNT, .new = 123, .expected = 123, .restore = 124, },
57         { .opt = TCP_WINDOW_CLAMP, .new = 8123, .expected = 8123, .restore = 8124, },
58         { .opt = TCP_CONGESTION, },
59         { .opt = TCP_THIN_LINEAR_TIMEOUTS, .flip = 1, },
60         { .opt = TCP_USER_TIMEOUT, .new = 123400, .expected = 123400, },
61         { .opt = TCP_NOTSENT_LOWAT, .new = 1314, .expected = 1314, },
62         { .opt = TCP_BPF_SOCK_OPS_CB_FLAGS, .new = BPF_SOCK_OPS_ALL_CB_FLAGS,
63           .expected = BPF_SOCK_OPS_ALL_CB_FLAGS, },
64         { .opt = 0, },
65 };
66
67 static const struct sockopt_test sol_ip_tests[] = {
68         { .opt = IP_TOS, .new = 0xe1, .expected = 0xe1, .tcp_expected = 0xe0, },
69         { .opt = 0, },
70 };
71
72 static const struct sockopt_test sol_ipv6_tests[] = {
73         { .opt = IPV6_TCLASS, .new = 0xe1, .expected = 0xe1, .tcp_expected = 0xe0, },
74         { .opt = IPV6_AUTOFLOWLABEL, .flip = 1, },
75         { .opt = 0, },
76 };
77
78 struct loop_ctx {
79         void *ctx;
80         struct sock *sk;
81 };
82
83 static int bpf_test_sockopt_flip(void *ctx, struct sock *sk,
84                                  const struct sockopt_test *t,
85                                  int level)
86 {
87         int old, tmp, new, opt = t->opt;
88
89         opt = t->opt;
90
91         if (bpf_getsockopt(ctx, level, opt, &old, sizeof(old)))
92                 return 1;
93         /* kernel initialized txrehash to 255 */
94         if (level == SOL_SOCKET && opt == SO_TXREHASH && old != 0 && old != 1)
95                 old = 1;
96
97         new = !old;
98         if (bpf_setsockopt(ctx, level, opt, &new, sizeof(new)))
99                 return 1;
100         if (bpf_getsockopt(ctx, level, opt, &tmp, sizeof(tmp)) ||
101             tmp != new)
102                 return 1;
103
104         if (bpf_setsockopt(ctx, level, opt, &old, sizeof(old)))
105                 return 1;
106
107         return 0;
108 }
109
110 static int bpf_test_sockopt_int(void *ctx, struct sock *sk,
111                                 const struct sockopt_test *t,
112                                 int level)
113 {
114         int old, tmp, new, expected, opt;
115
116         opt = t->opt;
117         new = t->new;
118         if (sk->sk_type == SOCK_STREAM && t->tcp_expected)
119                 expected = t->tcp_expected;
120         else
121                 expected = t->expected;
122
123         if (bpf_getsockopt(ctx, level, opt, &old, sizeof(old)) ||
124             old == new)
125                 return 1;
126
127         if (bpf_setsockopt(ctx, level, opt, &new, sizeof(new)))
128                 return 1;
129         if (bpf_getsockopt(ctx, level, opt, &tmp, sizeof(tmp)) ||
130             tmp != expected)
131                 return 1;
132
133         if (t->restore)
134                 old = t->restore;
135         if (bpf_setsockopt(ctx, level, opt, &old, sizeof(old)))
136                 return 1;
137
138         return 0;
139 }
140
141 static int bpf_test_socket_sockopt(__u32 i, struct loop_ctx *lc)
142 {
143         const struct sockopt_test *t;
144
145         if (i >= ARRAY_SIZE(sol_socket_tests))
146                 return 1;
147
148         t = &sol_socket_tests[i];
149         if (!t->opt)
150                 return 1;
151
152         if (t->flip)
153                 return bpf_test_sockopt_flip(lc->ctx, lc->sk, t, SOL_SOCKET);
154
155         return bpf_test_sockopt_int(lc->ctx, lc->sk, t, SOL_SOCKET);
156 }
157
158 static int bpf_test_ip_sockopt(__u32 i, struct loop_ctx *lc)
159 {
160         const struct sockopt_test *t;
161
162         if (i >= ARRAY_SIZE(sol_ip_tests))
163                 return 1;
164
165         t = &sol_ip_tests[i];
166         if (!t->opt)
167                 return 1;
168
169         if (t->flip)
170                 return bpf_test_sockopt_flip(lc->ctx, lc->sk, t, IPPROTO_IP);
171
172         return bpf_test_sockopt_int(lc->ctx, lc->sk, t, IPPROTO_IP);
173 }
174
175 static int bpf_test_ipv6_sockopt(__u32 i, struct loop_ctx *lc)
176 {
177         const struct sockopt_test *t;
178
179         if (i >= ARRAY_SIZE(sol_ipv6_tests))
180                 return 1;
181
182         t = &sol_ipv6_tests[i];
183         if (!t->opt)
184                 return 1;
185
186         if (t->flip)
187                 return bpf_test_sockopt_flip(lc->ctx, lc->sk, t, IPPROTO_IPV6);
188
189         return bpf_test_sockopt_int(lc->ctx, lc->sk, t, IPPROTO_IPV6);
190 }
191
192 static int bpf_test_tcp_sockopt(__u32 i, struct loop_ctx *lc)
193 {
194         const struct sockopt_test *t;
195         struct sock *sk;
196         void *ctx;
197
198         if (i >= ARRAY_SIZE(sol_tcp_tests))
199                 return 1;
200
201         t = &sol_tcp_tests[i];
202         if (!t->opt)
203                 return 1;
204
205         ctx = lc->ctx;
206         sk = lc->sk;
207
208         if (t->opt == TCP_CONGESTION) {
209                 char old_cc[16], tmp_cc[16];
210                 const char *new_cc;
211                 int new_cc_len;
212
213                 if (!bpf_setsockopt(ctx, IPPROTO_TCP, TCP_CONGESTION,
214                                     (void *)not_exist_cc, sizeof(not_exist_cc)))
215                         return 1;
216                 if (bpf_getsockopt(ctx, IPPROTO_TCP, TCP_CONGESTION, old_cc, sizeof(old_cc)))
217                         return 1;
218                 if (!bpf_strncmp(old_cc, sizeof(old_cc), cubic_cc)) {
219                         new_cc = reno_cc;
220                         new_cc_len = sizeof(reno_cc);
221                 } else {
222                         new_cc = cubic_cc;
223                         new_cc_len = sizeof(cubic_cc);
224                 }
225                 if (bpf_setsockopt(ctx, IPPROTO_TCP, TCP_CONGESTION, (void *)new_cc,
226                                    new_cc_len))
227                         return 1;
228                 if (bpf_getsockopt(ctx, IPPROTO_TCP, TCP_CONGESTION, tmp_cc, sizeof(tmp_cc)))
229                         return 1;
230                 if (bpf_strncmp(tmp_cc, sizeof(tmp_cc), new_cc))
231                         return 1;
232                 if (bpf_setsockopt(ctx, IPPROTO_TCP, TCP_CONGESTION, old_cc, sizeof(old_cc)))
233                         return 1;
234                 return 0;
235         }
236
237         if (t->flip)
238                 return bpf_test_sockopt_flip(ctx, sk, t, IPPROTO_TCP);
239
240         return bpf_test_sockopt_int(ctx, sk, t, IPPROTO_TCP);
241 }
242
243 static int bpf_test_sockopt(void *ctx, struct sock *sk)
244 {
245         struct loop_ctx lc = { .ctx = ctx, .sk = sk, };
246         __u16 family, proto;
247         int n;
248
249         family = sk->sk_family;
250         proto = sk->sk_protocol;
251
252         n = bpf_loop(ARRAY_SIZE(sol_socket_tests), bpf_test_socket_sockopt, &lc, 0);
253         if (n != ARRAY_SIZE(sol_socket_tests))
254                 return -1;
255
256         if (proto == IPPROTO_TCP) {
257                 n = bpf_loop(ARRAY_SIZE(sol_tcp_tests), bpf_test_tcp_sockopt, &lc, 0);
258                 if (n != ARRAY_SIZE(sol_tcp_tests))
259                         return -1;
260         }
261
262         if (family == AF_INET) {
263                 n = bpf_loop(ARRAY_SIZE(sol_ip_tests), bpf_test_ip_sockopt, &lc, 0);
264                 if (n != ARRAY_SIZE(sol_ip_tests))
265                         return -1;
266         } else {
267                 n = bpf_loop(ARRAY_SIZE(sol_ipv6_tests), bpf_test_ipv6_sockopt, &lc, 0);
268                 if (n != ARRAY_SIZE(sol_ipv6_tests))
269                         return -1;
270         }
271
272         return 0;
273 }
274
275 static int binddev_test(void *ctx)
276 {
277         const char empty_ifname[] = "";
278         int ifindex, zero = 0;
279
280         if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
281                            (void *)veth, sizeof(veth)))
282                 return -1;
283         if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
284                            &ifindex, sizeof(int)) ||
285             ifindex != veth_ifindex)
286                 return -1;
287
288         if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
289                            (void *)empty_ifname, sizeof(empty_ifname)))
290                 return -1;
291         if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
292                            &ifindex, sizeof(int)) ||
293             ifindex != 0)
294                 return -1;
295
296         if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
297                            (void *)&veth_ifindex, sizeof(int)))
298                 return -1;
299         if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
300                            &ifindex, sizeof(int)) ||
301             ifindex != veth_ifindex)
302                 return -1;
303
304         if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
305                            &zero, sizeof(int)))
306                 return -1;
307         if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
308                            &ifindex, sizeof(int)) ||
309             ifindex != 0)
310                 return -1;
311
312         return 0;
313 }
314
315 static int test_tcp_maxseg(void *ctx, struct sock *sk)
316 {
317         int val = 1314, tmp;
318
319         if (sk->sk_state != TCP_ESTABLISHED)
320                 return bpf_setsockopt(ctx, IPPROTO_TCP, TCP_MAXSEG,
321                                       &val, sizeof(val));
322
323         if (bpf_getsockopt(ctx, IPPROTO_TCP, TCP_MAXSEG, &tmp, sizeof(tmp)) ||
324             tmp > val)
325                 return -1;
326
327         return 0;
328 }
329
330 static int test_tcp_saved_syn(void *ctx, struct sock *sk)
331 {
332         __u8 saved_syn[20];
333         int one = 1;
334
335         if (sk->sk_state == TCP_LISTEN)
336                 return bpf_setsockopt(ctx, IPPROTO_TCP, TCP_SAVE_SYN,
337                                       &one, sizeof(one));
338
339         return bpf_getsockopt(ctx, IPPROTO_TCP, TCP_SAVED_SYN,
340                               saved_syn, sizeof(saved_syn));
341 }
342
343 SEC("lsm_cgroup/socket_post_create")
344 int BPF_PROG(socket_post_create, struct socket *sock, int family,
345              int type, int protocol, int kern)
346 {
347         struct sock *sk = sock->sk;
348
349         if (!sk)
350                 return 1;
351
352         nr_socket_post_create += !bpf_test_sockopt(sk, sk);
353         nr_binddev += !binddev_test(sk);
354
355         return 1;
356 }
357
358 SEC("cgroup/getsockopt")
359 int _getsockopt(struct bpf_sockopt *ctx)
360 {
361         struct bpf_sock *sk = ctx->sk;
362         int *optval = ctx->optval;
363         struct tcp_sock *tp;
364
365         if (!sk || ctx->level != SOL_TCP || ctx->optname != TCP_BPF_SOCK_OPS_CB_FLAGS)
366                 return 1;
367
368         tp = bpf_core_cast(sk, struct tcp_sock);
369         if (ctx->optval + sizeof(int) <= ctx->optval_end) {
370                 *optval = tp->bpf_sock_ops_cb_flags;
371                 ctx->retval = 0;
372         }
373         return 1;
374 }
375
376 SEC("sockops")
377 int skops_sockopt(struct bpf_sock_ops *skops)
378 {
379         struct bpf_sock *bpf_sk = skops->sk;
380         struct sock *sk;
381         int flags;
382
383         if (!bpf_sk)
384                 return 1;
385
386         sk = (struct sock *)bpf_skc_to_tcp_sock(bpf_sk);
387         if (!sk)
388                 return 1;
389
390         switch (skops->op) {
391         case BPF_SOCK_OPS_TCP_LISTEN_CB:
392                 nr_listen += !(bpf_test_sockopt(skops, sk) ||
393                                test_tcp_maxseg(skops, sk) ||
394                                test_tcp_saved_syn(skops, sk));
395                 break;
396         case BPF_SOCK_OPS_TCP_CONNECT_CB:
397                 nr_connect += !(bpf_test_sockopt(skops, sk) ||
398                                 test_tcp_maxseg(skops, sk));
399                 break;
400         case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
401                 nr_active += !(bpf_test_sockopt(skops, sk) ||
402                                test_tcp_maxseg(skops, sk));
403                 break;
404         case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
405                 nr_passive += !(bpf_test_sockopt(skops, sk) ||
406                                 test_tcp_maxseg(skops, sk) ||
407                                 test_tcp_saved_syn(skops, sk));
408                 flags = skops->bpf_sock_ops_cb_flags | BPF_SOCK_OPS_STATE_CB_FLAG;
409                 bpf_setsockopt(skops, SOL_TCP, TCP_BPF_SOCK_OPS_CB_FLAGS, &flags, sizeof(flags));
410                 break;
411         case BPF_SOCK_OPS_STATE_CB:
412                 if (skops->args[1] == BPF_TCP_CLOSE_WAIT)
413                         nr_fin_wait1 += !bpf_test_sockopt(skops, sk);
414                 break;
415         }
416
417         return 1;
418 }
419
420 char _license[] SEC("license") = "GPL";
This page took 0.054716 seconds and 4 git commands to generate.