1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2020 Facebook */
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
;
43 static int create_netns(void)
45 if (CHECK(unshare(CLONE_NEWNET
), "create netns",
46 "unshare(CLONE_NEWNET): %s (%d)",
47 strerror(errno
), errno
))
50 if (CHECK(system("ip link set dev lo up"), "run ip cmd",
51 "failed to bring lo link up\n"))
57 static int write_sysctl(const char *sysctl
, const char *value
)
61 fd
= open(sysctl
, O_WRONLY
);
62 if (CHECK(fd
== -1, "open sysctl", "open(%s): %s (%d)\n",
63 sysctl
, strerror(errno
), errno
))
67 err
= write(fd
, value
, len
);
69 if (CHECK(err
!= len
, "write sysctl",
70 "write(%s, %s): err:%d %s (%d)\n",
71 sysctl
, value
, err
, strerror(errno
), errno
))
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
)
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):",
105 ret
, strerror(errno
), errno
))
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):",
112 ret
, strerror(errno
), errno
))
118 static int sk_fds_connect(struct sk_fds
*sk_fds
, bool fast_open
)
120 const char fast
[] = "FAST!!!";
121 struct sockaddr_in6 addr6
;
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
))
130 sk_fds
->active_fd
= fastopen_connect(sk_fds
->srv_fd
, fast
,
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
);
141 if (CHECK(getsockname(sk_fds
->srv_fd
, (struct sockaddr
*)&addr6
,
142 &len
), "getsockname(srv_fd)", "%s (%d)\n",
143 strerror(errno
), errno
))
145 sk_fds
->passive_lport
= ntohs(addr6
.sin6_port
);
148 if (CHECK(getsockname(sk_fds
->active_fd
, (struct sockaddr
*)&addr6
,
149 &len
), "getsockname(active_fd)", "%s (%d)\n",
150 strerror(errno
), errno
))
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
))
160 char bytes_in
[sizeof(fast
)];
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
);
174 close(sk_fds
->active_fd
);
175 close(sk_fds
->srv_fd
);
178 memset(sk_fds
, -1, sizeof(*sk_fds
));
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: ");
196 static int check_hdr_stg(const struct hdr_stg
*exp
, int fd
,
197 const char *stg_desc
)
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
))
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: ");
216 static int check_error_linum(const struct sk_fds
*sk_fds
)
218 unsigned int nr_errors
= 0;
219 struct linum_err linum_err
;
222 lport
= sk_fds
->passive_lport
;
223 if (!bpf_map_lookup_elem(lport_linum_map_fd
, &lport
, &linum_err
)) {
225 "bpf prog error out at lport:passive(%d), linum:%u err:%d\n",
226 lport
, linum_err
.linum
, linum_err
.err
);
230 lport
= sk_fds
->active_lport
;
231 if (!bpf_map_lookup_elem(lport_linum_map_fd
, &lport
, &linum_err
)) {
233 "bpf prog error out at lport:active(%d), linum:%u err:%d\n",
234 lport
, linum_err
.linum
, linum_err
.err
);
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
))
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
))
256 if (check_hdr_stg(&exp_passive_hdr_stg
, sk_fds
->passive_fd
,
260 if (check_hdr_stg(&exp_active_hdr_stg
, sk_fds
->active_fd
,
264 if (check_hdr_opt(&exp_passive_estab_in
, &skel
->bss
->passive_estab_in
,
268 if (check_hdr_opt(&exp_active_estab_in
, &skel
->bss
->active_estab_in
,
272 if (check_hdr_opt(&exp_passive_fin_in
, &skel
->bss
->passive_fin_in
,
276 check_hdr_opt(&exp_active_fin_in
, &skel
->bss
->active_fin_in
,
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
);
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
);
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;
351 /* Allow fastopen without fastopen cookie */
352 if (write_sysctl("/proc/sys/net/ipv4/tcp_fastopen", "1543"))
355 link
= bpf_program__attach_cgroup(skel
->progs
.estab
, cg_fd
);
356 if (CHECK(IS_ERR(link
), "attach_cgroup(estab)", "err: %ld\n",
360 if (sk_fds_connect(&sk_fds
, true)) {
361 bpf_link__destroy(link
);
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
|
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,
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"))
400 link
= bpf_program__attach_cgroup(skel
->progs
.estab
, cg_fd
);
401 if (CHECK(IS_ERR(link
), "attach_cgroup(estab)", "err: %ld\n",
405 if (sk_fds_connect(&sk_fds
, false)) {
406 bpf_link__destroy(link
);
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;
430 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1"))
433 link
= bpf_program__attach_cgroup(skel
->progs
.estab
, cg_fd
);
434 if (CHECK(IS_ERR(link
), "attach_cgroup(estab)", "err: %ld\n",
438 if (sk_fds_connect(&sk_fds
, false)) {
439 bpf_link__destroy(link
);
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;
466 skel
->data
->test_kind
= 0xB9;
467 skel
->data
->test_magic
= 0;
470 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1"))
473 link
= bpf_program__attach_cgroup(skel
->progs
.estab
, cg_fd
);
474 if (CHECK(IS_ERR(link
), "attach_cgroup(estab)", "err: %ld\n",
478 if (sk_fds_connect(&sk_fds
, false)) {
479 bpf_link__destroy(link
);
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
;
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"))
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",
516 if (sk_fds_connect(&sk_fds
, false)) {
517 bpf_link__destroy(link
);
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
),
525 if (CHECK(ret
!= sizeof(send_msg
), "send(msg)", "ret:%d\n",
529 ret
= read(sk_fds
.passive_fd
, recv_msg
, sizeof(recv_msg
));
530 if (CHECK(ret
!= sizeof(send_msg
), "read(msg)", "ret:%d\n",
535 if (sk_fds_shutdown(&sk_fds
))
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
);
558 CHECK_FAIL(check_error_linum(&sk_fds
));
559 sk_fds_close(&sk_fds
);
560 bpf_link__destroy(link
);
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
),
578 void test_tcp_hdr_options(void)
582 skel
= test_tcp_hdr_options__open_and_load();
583 if (CHECK(!skel
, "open and load skel", "failed"))
586 misc_skel
= test_misc_tcp_hdr_options__open_and_load();
587 if (CHECK(!misc_skel
, "open and load misc test skel", "failed"))
590 cg_fd
= test__join_cgroup(CG_NAME
);
591 if (CHECK_FAIL(cg_fd
< 0))
594 for (i
= 0; i
< ARRAY_SIZE(tests
); i
++) {
595 if (!test__start_subtest(tests
[i
].desc
))
608 test_misc_tcp_hdr_options__destroy(misc_skel
);
609 test_tcp_hdr_options__destroy(skel
);