WIP FPC-III support
[linux/fpc-iii.git] / tools / testing / selftests / bpf / prog_tests / tcp_hdr_options.c
blob08d19cafd5e862998182ba6cc66b25513ab5ccb1
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2020 Facebook */
4 #define _GNU_SOURCE
5 #include <sched.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <sys/socket.h>
9 #include <linux/compiler.h>
11 #include "test_progs.h"
12 #include "cgroup_helpers.h"
13 #include "network_helpers.h"
14 #include "test_tcp_hdr_options.h"
15 #include "test_tcp_hdr_options.skel.h"
16 #include "test_misc_tcp_hdr_options.skel.h"
18 #define LO_ADDR6 "::1"
19 #define CG_NAME "/tcpbpf-hdr-opt-test"
21 static struct bpf_test_option exp_passive_estab_in;
22 static struct bpf_test_option exp_active_estab_in;
23 static struct bpf_test_option exp_passive_fin_in;
24 static struct bpf_test_option exp_active_fin_in;
25 static struct hdr_stg exp_passive_hdr_stg;
26 static struct hdr_stg exp_active_hdr_stg = { .active = true, };
28 static struct test_misc_tcp_hdr_options *misc_skel;
29 static struct test_tcp_hdr_options *skel;
30 static int lport_linum_map_fd;
31 static int hdr_stg_map_fd;
32 static __u32 duration;
33 static int cg_fd;
35 struct sk_fds {
36 int srv_fd;
37 int passive_fd;
38 int active_fd;
39 int passive_lport;
40 int active_lport;
43 static int create_netns(void)
45 if (CHECK(unshare(CLONE_NEWNET), "create netns",
46 "unshare(CLONE_NEWNET): %s (%d)",
47 strerror(errno), errno))
48 return -1;
50 if (CHECK(system("ip link set dev lo up"), "run ip cmd",
51 "failed to bring lo link up\n"))
52 return -1;
54 return 0;
57 static int write_sysctl(const char *sysctl, const char *value)
59 int fd, err, len;
61 fd = open(sysctl, O_WRONLY);
62 if (CHECK(fd == -1, "open sysctl", "open(%s): %s (%d)\n",
63 sysctl, strerror(errno), errno))
64 return -1;
66 len = strlen(value);
67 err = write(fd, value, len);
68 close(fd);
69 if (CHECK(err != len, "write sysctl",
70 "write(%s, %s): err:%d %s (%d)\n",
71 sysctl, value, err, strerror(errno), errno))
72 return -1;
74 return 0;
77 static void print_hdr_stg(const struct hdr_stg *hdr_stg, const char *prefix)
79 fprintf(stderr, "%s{active:%u, resend_syn:%u, syncookie:%u, fastopen:%u}\n",
80 prefix ? : "", hdr_stg->active, hdr_stg->resend_syn,
81 hdr_stg->syncookie, hdr_stg->fastopen);
84 static void print_option(const struct bpf_test_option *opt, const char *prefix)
86 fprintf(stderr, "%s{flags:0x%x, max_delack_ms:%u, rand:0x%x}\n",
87 prefix ? : "", opt->flags, opt->max_delack_ms, opt->rand);
90 static void sk_fds_close(struct sk_fds *sk_fds)
92 close(sk_fds->srv_fd);
93 close(sk_fds->passive_fd);
94 close(sk_fds->active_fd);
97 static int sk_fds_shutdown(struct sk_fds *sk_fds)
99 int ret, abyte;
101 shutdown(sk_fds->active_fd, SHUT_WR);
102 ret = read(sk_fds->passive_fd, &abyte, sizeof(abyte));
103 if (CHECK(ret != 0, "read-after-shutdown(passive_fd):",
104 "ret:%d %s (%d)\n",
105 ret, strerror(errno), errno))
106 return -1;
108 shutdown(sk_fds->passive_fd, SHUT_WR);
109 ret = read(sk_fds->active_fd, &abyte, sizeof(abyte));
110 if (CHECK(ret != 0, "read-after-shutdown(active_fd):",
111 "ret:%d %s (%d)\n",
112 ret, strerror(errno), errno))
113 return -1;
115 return 0;
118 static int sk_fds_connect(struct sk_fds *sk_fds, bool fast_open)
120 const char fast[] = "FAST!!!";
121 struct sockaddr_in6 addr6;
122 socklen_t len;
124 sk_fds->srv_fd = start_server(AF_INET6, SOCK_STREAM, LO_ADDR6, 0, 0);
125 if (CHECK(sk_fds->srv_fd == -1, "start_server", "%s (%d)\n",
126 strerror(errno), errno))
127 goto error;
129 if (fast_open)
130 sk_fds->active_fd = fastopen_connect(sk_fds->srv_fd, fast,
131 sizeof(fast), 0);
132 else
133 sk_fds->active_fd = connect_to_fd(sk_fds->srv_fd, 0);
135 if (CHECK_FAIL(sk_fds->active_fd == -1)) {
136 close(sk_fds->srv_fd);
137 goto error;
140 len = sizeof(addr6);
141 if (CHECK(getsockname(sk_fds->srv_fd, (struct sockaddr *)&addr6,
142 &len), "getsockname(srv_fd)", "%s (%d)\n",
143 strerror(errno), errno))
144 goto error_close;
145 sk_fds->passive_lport = ntohs(addr6.sin6_port);
147 len = sizeof(addr6);
148 if (CHECK(getsockname(sk_fds->active_fd, (struct sockaddr *)&addr6,
149 &len), "getsockname(active_fd)", "%s (%d)\n",
150 strerror(errno), errno))
151 goto error_close;
152 sk_fds->active_lport = ntohs(addr6.sin6_port);
154 sk_fds->passive_fd = accept(sk_fds->srv_fd, NULL, 0);
155 if (CHECK(sk_fds->passive_fd == -1, "accept(srv_fd)", "%s (%d)\n",
156 strerror(errno), errno))
157 goto error_close;
159 if (fast_open) {
160 char bytes_in[sizeof(fast)];
161 int ret;
163 ret = read(sk_fds->passive_fd, bytes_in, sizeof(bytes_in));
164 if (CHECK(ret != sizeof(fast), "read fastopen syn data",
165 "expected=%lu actual=%d\n", sizeof(fast), ret)) {
166 close(sk_fds->passive_fd);
167 goto error_close;
171 return 0;
173 error_close:
174 close(sk_fds->active_fd);
175 close(sk_fds->srv_fd);
177 error:
178 memset(sk_fds, -1, sizeof(*sk_fds));
179 return -1;
182 static int check_hdr_opt(const struct bpf_test_option *exp,
183 const struct bpf_test_option *act,
184 const char *hdr_desc)
186 if (CHECK(memcmp(exp, act, sizeof(*exp)),
187 "expected-vs-actual", "unexpected %s\n", hdr_desc)) {
188 print_option(exp, "expected: ");
189 print_option(act, " actual: ");
190 return -1;
193 return 0;
196 static int check_hdr_stg(const struct hdr_stg *exp, int fd,
197 const char *stg_desc)
199 struct hdr_stg act;
201 if (CHECK(bpf_map_lookup_elem(hdr_stg_map_fd, &fd, &act),
202 "map_lookup(hdr_stg_map_fd)", "%s %s (%d)\n",
203 stg_desc, strerror(errno), errno))
204 return -1;
206 if (CHECK(memcmp(exp, &act, sizeof(*exp)),
207 "expected-vs-actual", "unexpected %s\n", stg_desc)) {
208 print_hdr_stg(exp, "expected: ");
209 print_hdr_stg(&act, " actual: ");
210 return -1;
213 return 0;
216 static int check_error_linum(const struct sk_fds *sk_fds)
218 unsigned int nr_errors = 0;
219 struct linum_err linum_err;
220 int lport;
222 lport = sk_fds->passive_lport;
223 if (!bpf_map_lookup_elem(lport_linum_map_fd, &lport, &linum_err)) {
224 fprintf(stderr,
225 "bpf prog error out at lport:passive(%d), linum:%u err:%d\n",
226 lport, linum_err.linum, linum_err.err);
227 nr_errors++;
230 lport = sk_fds->active_lport;
231 if (!bpf_map_lookup_elem(lport_linum_map_fd, &lport, &linum_err)) {
232 fprintf(stderr,
233 "bpf prog error out at lport:active(%d), linum:%u err:%d\n",
234 lport, linum_err.linum, linum_err.err);
235 nr_errors++;
238 return nr_errors;
241 static void check_hdr_and_close_fds(struct sk_fds *sk_fds)
243 const __u32 expected_inherit_cb_flags =
244 BPF_SOCK_OPS_PARSE_UNKNOWN_HDR_OPT_CB_FLAG |
245 BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG |
246 BPF_SOCK_OPS_STATE_CB_FLAG;
248 if (sk_fds_shutdown(sk_fds))
249 goto check_linum;
251 if (CHECK(expected_inherit_cb_flags != skel->bss->inherit_cb_flags,
252 "Unexpected inherit_cb_flags", "0x%x != 0x%x\n",
253 skel->bss->inherit_cb_flags, expected_inherit_cb_flags))
254 goto check_linum;
256 if (check_hdr_stg(&exp_passive_hdr_stg, sk_fds->passive_fd,
257 "passive_hdr_stg"))
258 goto check_linum;
260 if (check_hdr_stg(&exp_active_hdr_stg, sk_fds->active_fd,
261 "active_hdr_stg"))
262 goto check_linum;
264 if (check_hdr_opt(&exp_passive_estab_in, &skel->bss->passive_estab_in,
265 "passive_estab_in"))
266 goto check_linum;
268 if (check_hdr_opt(&exp_active_estab_in, &skel->bss->active_estab_in,
269 "active_estab_in"))
270 goto check_linum;
272 if (check_hdr_opt(&exp_passive_fin_in, &skel->bss->passive_fin_in,
273 "passive_fin_in"))
274 goto check_linum;
276 check_hdr_opt(&exp_active_fin_in, &skel->bss->active_fin_in,
277 "active_fin_in");
279 check_linum:
280 CHECK_FAIL(check_error_linum(sk_fds));
281 sk_fds_close(sk_fds);
284 static void prepare_out(void)
286 skel->bss->active_syn_out = exp_passive_estab_in;
287 skel->bss->passive_synack_out = exp_active_estab_in;
289 skel->bss->active_fin_out = exp_passive_fin_in;
290 skel->bss->passive_fin_out = exp_active_fin_in;
293 static void reset_test(void)
295 size_t optsize = sizeof(struct bpf_test_option);
296 int lport, err;
298 memset(&skel->bss->passive_synack_out, 0, optsize);
299 memset(&skel->bss->passive_fin_out, 0, optsize);
301 memset(&skel->bss->passive_estab_in, 0, optsize);
302 memset(&skel->bss->passive_fin_in, 0, optsize);
304 memset(&skel->bss->active_syn_out, 0, optsize);
305 memset(&skel->bss->active_fin_out, 0, optsize);
307 memset(&skel->bss->active_estab_in, 0, optsize);
308 memset(&skel->bss->active_fin_in, 0, optsize);
310 skel->bss->inherit_cb_flags = 0;
312 skel->data->test_kind = TCPOPT_EXP;
313 skel->data->test_magic = 0xeB9F;
315 memset(&exp_passive_estab_in, 0, optsize);
316 memset(&exp_active_estab_in, 0, optsize);
317 memset(&exp_passive_fin_in, 0, optsize);
318 memset(&exp_active_fin_in, 0, optsize);
320 memset(&exp_passive_hdr_stg, 0, sizeof(exp_passive_hdr_stg));
321 memset(&exp_active_hdr_stg, 0, sizeof(exp_active_hdr_stg));
322 exp_active_hdr_stg.active = true;
324 err = bpf_map_get_next_key(lport_linum_map_fd, NULL, &lport);
325 while (!err) {
326 bpf_map_delete_elem(lport_linum_map_fd, &lport);
327 err = bpf_map_get_next_key(lport_linum_map_fd, &lport, &lport);
331 static void fastopen_estab(void)
333 struct bpf_link *link;
334 struct sk_fds sk_fds;
336 hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map);
337 lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map);
339 exp_passive_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
340 exp_passive_estab_in.rand = 0xfa;
341 exp_passive_estab_in.max_delack_ms = 11;
343 exp_active_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
344 exp_active_estab_in.rand = 0xce;
345 exp_active_estab_in.max_delack_ms = 22;
347 exp_passive_hdr_stg.fastopen = true;
349 prepare_out();
351 /* Allow fastopen without fastopen cookie */
352 if (write_sysctl("/proc/sys/net/ipv4/tcp_fastopen", "1543"))
353 return;
355 link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd);
356 if (CHECK(IS_ERR(link), "attach_cgroup(estab)", "err: %ld\n",
357 PTR_ERR(link)))
358 return;
360 if (sk_fds_connect(&sk_fds, true)) {
361 bpf_link__destroy(link);
362 return;
365 check_hdr_and_close_fds(&sk_fds);
366 bpf_link__destroy(link);
369 static void syncookie_estab(void)
371 struct bpf_link *link;
372 struct sk_fds sk_fds;
374 hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map);
375 lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map);
377 exp_passive_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
378 exp_passive_estab_in.rand = 0xfa;
379 exp_passive_estab_in.max_delack_ms = 11;
381 exp_active_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS |
382 OPTION_F_RESEND;
383 exp_active_estab_in.rand = 0xce;
384 exp_active_estab_in.max_delack_ms = 22;
386 exp_passive_hdr_stg.syncookie = true;
387 exp_active_hdr_stg.resend_syn = true,
389 prepare_out();
391 /* Clear the RESEND to ensure the bpf prog can learn
392 * want_cookie and set the RESEND by itself.
394 skel->bss->passive_synack_out.flags &= ~OPTION_F_RESEND;
396 /* Enforce syncookie mode */
397 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "2"))
398 return;
400 link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd);
401 if (CHECK(IS_ERR(link), "attach_cgroup(estab)", "err: %ld\n",
402 PTR_ERR(link)))
403 return;
405 if (sk_fds_connect(&sk_fds, false)) {
406 bpf_link__destroy(link);
407 return;
410 check_hdr_and_close_fds(&sk_fds);
411 bpf_link__destroy(link);
414 static void fin(void)
416 struct bpf_link *link;
417 struct sk_fds sk_fds;
419 hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map);
420 lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map);
422 exp_passive_fin_in.flags = OPTION_F_RAND;
423 exp_passive_fin_in.rand = 0xfa;
425 exp_active_fin_in.flags = OPTION_F_RAND;
426 exp_active_fin_in.rand = 0xce;
428 prepare_out();
430 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1"))
431 return;
433 link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd);
434 if (CHECK(IS_ERR(link), "attach_cgroup(estab)", "err: %ld\n",
435 PTR_ERR(link)))
436 return;
438 if (sk_fds_connect(&sk_fds, false)) {
439 bpf_link__destroy(link);
440 return;
443 check_hdr_and_close_fds(&sk_fds);
444 bpf_link__destroy(link);
447 static void __simple_estab(bool exprm)
449 struct bpf_link *link;
450 struct sk_fds sk_fds;
452 hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map);
453 lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map);
455 exp_passive_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
456 exp_passive_estab_in.rand = 0xfa;
457 exp_passive_estab_in.max_delack_ms = 11;
459 exp_active_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
460 exp_active_estab_in.rand = 0xce;
461 exp_active_estab_in.max_delack_ms = 22;
463 prepare_out();
465 if (!exprm) {
466 skel->data->test_kind = 0xB9;
467 skel->data->test_magic = 0;
470 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1"))
471 return;
473 link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd);
474 if (CHECK(IS_ERR(link), "attach_cgroup(estab)", "err: %ld\n",
475 PTR_ERR(link)))
476 return;
478 if (sk_fds_connect(&sk_fds, false)) {
479 bpf_link__destroy(link);
480 return;
483 check_hdr_and_close_fds(&sk_fds);
484 bpf_link__destroy(link);
487 static void no_exprm_estab(void)
489 __simple_estab(false);
492 static void simple_estab(void)
494 __simple_estab(true);
497 static void misc(void)
499 const char send_msg[] = "MISC!!!";
500 char recv_msg[sizeof(send_msg)];
501 const unsigned int nr_data = 2;
502 struct bpf_link *link;
503 struct sk_fds sk_fds;
504 int i, ret;
506 lport_linum_map_fd = bpf_map__fd(misc_skel->maps.lport_linum_map);
508 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1"))
509 return;
511 link = bpf_program__attach_cgroup(misc_skel->progs.misc_estab, cg_fd);
512 if (CHECK(IS_ERR(link), "attach_cgroup(misc_estab)", "err: %ld\n",
513 PTR_ERR(link)))
514 return;
516 if (sk_fds_connect(&sk_fds, false)) {
517 bpf_link__destroy(link);
518 return;
521 for (i = 0; i < nr_data; i++) {
522 /* MSG_EOR to ensure skb will not be combined */
523 ret = send(sk_fds.active_fd, send_msg, sizeof(send_msg),
524 MSG_EOR);
525 if (CHECK(ret != sizeof(send_msg), "send(msg)", "ret:%d\n",
526 ret))
527 goto check_linum;
529 ret = read(sk_fds.passive_fd, recv_msg, sizeof(recv_msg));
530 if (CHECK(ret != sizeof(send_msg), "read(msg)", "ret:%d\n",
531 ret))
532 goto check_linum;
535 if (sk_fds_shutdown(&sk_fds))
536 goto check_linum;
538 CHECK(misc_skel->bss->nr_syn != 1, "unexpected nr_syn",
539 "expected (1) != actual (%u)\n",
540 misc_skel->bss->nr_syn);
542 CHECK(misc_skel->bss->nr_data != nr_data, "unexpected nr_data",
543 "expected (%u) != actual (%u)\n",
544 nr_data, misc_skel->bss->nr_data);
546 /* The last ACK may have been delayed, so it is either 1 or 2. */
547 CHECK(misc_skel->bss->nr_pure_ack != 1 &&
548 misc_skel->bss->nr_pure_ack != 2,
549 "unexpected nr_pure_ack",
550 "expected (1 or 2) != actual (%u)\n",
551 misc_skel->bss->nr_pure_ack);
553 CHECK(misc_skel->bss->nr_fin != 1, "unexpected nr_fin",
554 "expected (1) != actual (%u)\n",
555 misc_skel->bss->nr_fin);
557 check_linum:
558 CHECK_FAIL(check_error_linum(&sk_fds));
559 sk_fds_close(&sk_fds);
560 bpf_link__destroy(link);
563 struct test {
564 const char *desc;
565 void (*run)(void);
568 #define DEF_TEST(name) { #name, name }
569 static struct test tests[] = {
570 DEF_TEST(simple_estab),
571 DEF_TEST(no_exprm_estab),
572 DEF_TEST(syncookie_estab),
573 DEF_TEST(fastopen_estab),
574 DEF_TEST(fin),
575 DEF_TEST(misc),
578 void test_tcp_hdr_options(void)
580 int i;
582 skel = test_tcp_hdr_options__open_and_load();
583 if (CHECK(!skel, "open and load skel", "failed"))
584 return;
586 misc_skel = test_misc_tcp_hdr_options__open_and_load();
587 if (CHECK(!misc_skel, "open and load misc test skel", "failed"))
588 goto skel_destroy;
590 cg_fd = test__join_cgroup(CG_NAME);
591 if (CHECK_FAIL(cg_fd < 0))
592 goto skel_destroy;
594 for (i = 0; i < ARRAY_SIZE(tests); i++) {
595 if (!test__start_subtest(tests[i].desc))
596 continue;
598 if (create_netns())
599 break;
601 tests[i].run();
603 reset_test();
606 close(cg_fd);
607 skel_destroy:
608 test_misc_tcp_hdr_options__destroy(misc_skel);
609 test_tcp_hdr_options__destroy(skel);