tcp_hdr_options.c (15210B)
1// SPDX-License-Identifier: GPL-2.0 2/* Copyright (c) 2020 Facebook */ 3 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> 10 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" 17 18#define LO_ADDR6 "::1" 19#define CG_NAME "/tcpbpf-hdr-opt-test" 20 21static struct bpf_test_option exp_passive_estab_in; 22static struct bpf_test_option exp_active_estab_in; 23static struct bpf_test_option exp_passive_fin_in; 24static struct bpf_test_option exp_active_fin_in; 25static struct hdr_stg exp_passive_hdr_stg; 26static struct hdr_stg exp_active_hdr_stg = { .active = true, }; 27 28static struct test_misc_tcp_hdr_options *misc_skel; 29static struct test_tcp_hdr_options *skel; 30static int lport_linum_map_fd; 31static int hdr_stg_map_fd; 32static __u32 duration; 33static int cg_fd; 34 35struct sk_fds { 36 int srv_fd; 37 int passive_fd; 38 int active_fd; 39 int passive_lport; 40 int active_lport; 41}; 42 43static int create_netns(void) 44{ 45 if (CHECK(unshare(CLONE_NEWNET), "create netns", 46 "unshare(CLONE_NEWNET): %s (%d)", 47 strerror(errno), errno)) 48 return -1; 49 50 if (CHECK(system("ip link set dev lo up"), "run ip cmd", 51 "failed to bring lo link up\n")) 52 return -1; 53 54 return 0; 55} 56 57static int write_sysctl(const char *sysctl, const char *value) 58{ 59 int fd, err, len; 60 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; 65 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; 73 74 return 0; 75} 76 77static void print_hdr_stg(const struct hdr_stg *hdr_stg, const char *prefix) 78{ 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); 82} 83 84static void print_option(const struct bpf_test_option *opt, const char *prefix) 85{ 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); 88} 89 90static void sk_fds_close(struct sk_fds *sk_fds) 91{ 92 close(sk_fds->srv_fd); 93 close(sk_fds->passive_fd); 94 close(sk_fds->active_fd); 95} 96 97static int sk_fds_shutdown(struct sk_fds *sk_fds) 98{ 99 int ret, abyte; 100 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; 107 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; 114 115 return 0; 116} 117 118static int sk_fds_connect(struct sk_fds *sk_fds, bool fast_open) 119{ 120 const char fast[] = "FAST!!!"; 121 struct sockaddr_in6 addr6; 122 socklen_t len; 123 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; 128 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); 134 135 if (CHECK_FAIL(sk_fds->active_fd == -1)) { 136 close(sk_fds->srv_fd); 137 goto error; 138 } 139 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); 146 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); 153 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; 158 159 if (fast_open) { 160 char bytes_in[sizeof(fast)]; 161 int ret; 162 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; 168 } 169 } 170 171 return 0; 172 173error_close: 174 close(sk_fds->active_fd); 175 close(sk_fds->srv_fd); 176 177error: 178 memset(sk_fds, -1, sizeof(*sk_fds)); 179 return -1; 180} 181 182static int check_hdr_opt(const struct bpf_test_option *exp, 183 const struct bpf_test_option *act, 184 const char *hdr_desc) 185{ 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; 191 } 192 193 return 0; 194} 195 196static int check_hdr_stg(const struct hdr_stg *exp, int fd, 197 const char *stg_desc) 198{ 199 struct hdr_stg act; 200 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; 205 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; 211 } 212 213 return 0; 214} 215 216static int check_error_linum(const struct sk_fds *sk_fds) 217{ 218 unsigned int nr_errors = 0; 219 struct linum_err linum_err; 220 int lport; 221 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++; 228 } 229 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++; 236 } 237 238 return nr_errors; 239} 240 241static void check_hdr_and_close_fds(struct sk_fds *sk_fds) 242{ 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; 247 248 if (sk_fds_shutdown(sk_fds)) 249 goto check_linum; 250 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; 255 256 if (check_hdr_stg(&exp_passive_hdr_stg, sk_fds->passive_fd, 257 "passive_hdr_stg")) 258 goto check_linum; 259 260 if (check_hdr_stg(&exp_active_hdr_stg, sk_fds->active_fd, 261 "active_hdr_stg")) 262 goto check_linum; 263 264 if (check_hdr_opt(&exp_passive_estab_in, &skel->bss->passive_estab_in, 265 "passive_estab_in")) 266 goto check_linum; 267 268 if (check_hdr_opt(&exp_active_estab_in, &skel->bss->active_estab_in, 269 "active_estab_in")) 270 goto check_linum; 271 272 if (check_hdr_opt(&exp_passive_fin_in, &skel->bss->passive_fin_in, 273 "passive_fin_in")) 274 goto check_linum; 275 276 check_hdr_opt(&exp_active_fin_in, &skel->bss->active_fin_in, 277 "active_fin_in"); 278 279check_linum: 280 CHECK_FAIL(check_error_linum(sk_fds)); 281 sk_fds_close(sk_fds); 282} 283 284static void prepare_out(void) 285{ 286 skel->bss->active_syn_out = exp_passive_estab_in; 287 skel->bss->passive_synack_out = exp_active_estab_in; 288 289 skel->bss->active_fin_out = exp_passive_fin_in; 290 skel->bss->passive_fin_out = exp_active_fin_in; 291} 292 293static void reset_test(void) 294{ 295 size_t optsize = sizeof(struct bpf_test_option); 296 int lport, err; 297 298 memset(&skel->bss->passive_synack_out, 0, optsize); 299 memset(&skel->bss->passive_fin_out, 0, optsize); 300 301 memset(&skel->bss->passive_estab_in, 0, optsize); 302 memset(&skel->bss->passive_fin_in, 0, optsize); 303 304 memset(&skel->bss->active_syn_out, 0, optsize); 305 memset(&skel->bss->active_fin_out, 0, optsize); 306 307 memset(&skel->bss->active_estab_in, 0, optsize); 308 memset(&skel->bss->active_fin_in, 0, optsize); 309 310 skel->bss->inherit_cb_flags = 0; 311 312 skel->data->test_kind = TCPOPT_EXP; 313 skel->data->test_magic = 0xeB9F; 314 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); 319 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; 323 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); 328 } 329} 330 331static void fastopen_estab(void) 332{ 333 struct bpf_link *link; 334 struct sk_fds sk_fds; 335 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); 338 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; 342 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; 346 347 exp_passive_hdr_stg.fastopen = true; 348 349 prepare_out(); 350 351 /* Allow fastopen without fastopen cookie */ 352 if (write_sysctl("/proc/sys/net/ipv4/tcp_fastopen", "1543")) 353 return; 354 355 link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd); 356 if (!ASSERT_OK_PTR(link, "attach_cgroup(estab)")) 357 return; 358 359 if (sk_fds_connect(&sk_fds, true)) { 360 bpf_link__destroy(link); 361 return; 362 } 363 364 check_hdr_and_close_fds(&sk_fds); 365 bpf_link__destroy(link); 366} 367 368static void syncookie_estab(void) 369{ 370 struct bpf_link *link; 371 struct sk_fds sk_fds; 372 373 hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map); 374 lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map); 375 376 exp_passive_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS; 377 exp_passive_estab_in.rand = 0xfa; 378 exp_passive_estab_in.max_delack_ms = 11; 379 380 exp_active_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS | 381 OPTION_F_RESEND; 382 exp_active_estab_in.rand = 0xce; 383 exp_active_estab_in.max_delack_ms = 22; 384 385 exp_passive_hdr_stg.syncookie = true; 386 exp_active_hdr_stg.resend_syn = true, 387 388 prepare_out(); 389 390 /* Clear the RESEND to ensure the bpf prog can learn 391 * want_cookie and set the RESEND by itself. 392 */ 393 skel->bss->passive_synack_out.flags &= ~OPTION_F_RESEND; 394 395 /* Enforce syncookie mode */ 396 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "2")) 397 return; 398 399 link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd); 400 if (!ASSERT_OK_PTR(link, "attach_cgroup(estab)")) 401 return; 402 403 if (sk_fds_connect(&sk_fds, false)) { 404 bpf_link__destroy(link); 405 return; 406 } 407 408 check_hdr_and_close_fds(&sk_fds); 409 bpf_link__destroy(link); 410} 411 412static void fin(void) 413{ 414 struct bpf_link *link; 415 struct sk_fds sk_fds; 416 417 hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map); 418 lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map); 419 420 exp_passive_fin_in.flags = OPTION_F_RAND; 421 exp_passive_fin_in.rand = 0xfa; 422 423 exp_active_fin_in.flags = OPTION_F_RAND; 424 exp_active_fin_in.rand = 0xce; 425 426 prepare_out(); 427 428 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1")) 429 return; 430 431 link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd); 432 if (!ASSERT_OK_PTR(link, "attach_cgroup(estab)")) 433 return; 434 435 if (sk_fds_connect(&sk_fds, false)) { 436 bpf_link__destroy(link); 437 return; 438 } 439 440 check_hdr_and_close_fds(&sk_fds); 441 bpf_link__destroy(link); 442} 443 444static void __simple_estab(bool exprm) 445{ 446 struct bpf_link *link; 447 struct sk_fds sk_fds; 448 449 hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map); 450 lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map); 451 452 exp_passive_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS; 453 exp_passive_estab_in.rand = 0xfa; 454 exp_passive_estab_in.max_delack_ms = 11; 455 456 exp_active_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS; 457 exp_active_estab_in.rand = 0xce; 458 exp_active_estab_in.max_delack_ms = 22; 459 460 prepare_out(); 461 462 if (!exprm) { 463 skel->data->test_kind = 0xB9; 464 skel->data->test_magic = 0; 465 } 466 467 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1")) 468 return; 469 470 link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd); 471 if (!ASSERT_OK_PTR(link, "attach_cgroup(estab)")) 472 return; 473 474 if (sk_fds_connect(&sk_fds, false)) { 475 bpf_link__destroy(link); 476 return; 477 } 478 479 check_hdr_and_close_fds(&sk_fds); 480 bpf_link__destroy(link); 481} 482 483static void no_exprm_estab(void) 484{ 485 __simple_estab(false); 486} 487 488static void simple_estab(void) 489{ 490 __simple_estab(true); 491} 492 493static void misc(void) 494{ 495 const char send_msg[] = "MISC!!!"; 496 char recv_msg[sizeof(send_msg)]; 497 const unsigned int nr_data = 2; 498 struct bpf_link *link; 499 struct sk_fds sk_fds; 500 int i, ret; 501 502 lport_linum_map_fd = bpf_map__fd(misc_skel->maps.lport_linum_map); 503 504 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1")) 505 return; 506 507 link = bpf_program__attach_cgroup(misc_skel->progs.misc_estab, cg_fd); 508 if (!ASSERT_OK_PTR(link, "attach_cgroup(misc_estab)")) 509 return; 510 511 if (sk_fds_connect(&sk_fds, false)) { 512 bpf_link__destroy(link); 513 return; 514 } 515 516 for (i = 0; i < nr_data; i++) { 517 /* MSG_EOR to ensure skb will not be combined */ 518 ret = send(sk_fds.active_fd, send_msg, sizeof(send_msg), 519 MSG_EOR); 520 if (CHECK(ret != sizeof(send_msg), "send(msg)", "ret:%d\n", 521 ret)) 522 goto check_linum; 523 524 ret = read(sk_fds.passive_fd, recv_msg, sizeof(recv_msg)); 525 if (CHECK(ret != sizeof(send_msg), "read(msg)", "ret:%d\n", 526 ret)) 527 goto check_linum; 528 } 529 530 if (sk_fds_shutdown(&sk_fds)) 531 goto check_linum; 532 533 CHECK(misc_skel->bss->nr_syn != 1, "unexpected nr_syn", 534 "expected (1) != actual (%u)\n", 535 misc_skel->bss->nr_syn); 536 537 CHECK(misc_skel->bss->nr_data != nr_data, "unexpected nr_data", 538 "expected (%u) != actual (%u)\n", 539 nr_data, misc_skel->bss->nr_data); 540 541 /* The last ACK may have been delayed, so it is either 1 or 2. */ 542 CHECK(misc_skel->bss->nr_pure_ack != 1 && 543 misc_skel->bss->nr_pure_ack != 2, 544 "unexpected nr_pure_ack", 545 "expected (1 or 2) != actual (%u)\n", 546 misc_skel->bss->nr_pure_ack); 547 548 CHECK(misc_skel->bss->nr_fin != 1, "unexpected nr_fin", 549 "expected (1) != actual (%u)\n", 550 misc_skel->bss->nr_fin); 551 552check_linum: 553 CHECK_FAIL(check_error_linum(&sk_fds)); 554 sk_fds_close(&sk_fds); 555 bpf_link__destroy(link); 556} 557 558struct test { 559 const char *desc; 560 void (*run)(void); 561}; 562 563#define DEF_TEST(name) { #name, name } 564static struct test tests[] = { 565 DEF_TEST(simple_estab), 566 DEF_TEST(no_exprm_estab), 567 DEF_TEST(syncookie_estab), 568 DEF_TEST(fastopen_estab), 569 DEF_TEST(fin), 570 DEF_TEST(misc), 571}; 572 573void test_tcp_hdr_options(void) 574{ 575 int i; 576 577 skel = test_tcp_hdr_options__open_and_load(); 578 if (CHECK(!skel, "open and load skel", "failed")) 579 return; 580 581 misc_skel = test_misc_tcp_hdr_options__open_and_load(); 582 if (CHECK(!misc_skel, "open and load misc test skel", "failed")) 583 goto skel_destroy; 584 585 cg_fd = test__join_cgroup(CG_NAME); 586 if (CHECK_FAIL(cg_fd < 0)) 587 goto skel_destroy; 588 589 for (i = 0; i < ARRAY_SIZE(tests); i++) { 590 if (!test__start_subtest(tests[i].desc)) 591 continue; 592 593 if (create_netns()) 594 break; 595 596 tests[i].run(); 597 598 reset_test(); 599 } 600 601 close(cg_fd); 602skel_destroy: 603 test_misc_tcp_hdr_options__destroy(misc_skel); 604 test_tcp_hdr_options__destroy(skel); 605}