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 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, }; 27 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; 34 35 struct sk_fds { 36 int srv_fd; 37 int passive_fd; 38 int active_fd; 39 int passive_lport; 40 int active_lport; 41 }; 42 43 static 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 57 static 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 77 static 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 84 static 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 90 static 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 97 static 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 118 static 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 173 error_close: 174 close(sk_fds->active_fd); 175 close(sk_fds->srv_fd); 176 177 error: 178 memset(sk_fds, -1, sizeof(*sk_fds)); 179 return -1; 180 } 181 182 static 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 196 static 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 216 static 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 241 static 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 279 check_linum: 280 CHECK_FAIL(check_error_linum(sk_fds)); 281 sk_fds_close(sk_fds); 282 } 283 284 static 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 293 static 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 331 static 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 (CHECK(IS_ERR(link), "attach_cgroup(estab)", "err: %ld\n", 357 PTR_ERR(link))) 358 return; 359 360 if (sk_fds_connect(&sk_fds, true)) { 361 bpf_link__destroy(link); 362 return; 363 } 364 365 check_hdr_and_close_fds(&sk_fds); 366 bpf_link__destroy(link); 367 } 368 369 static void syncookie_estab(void) 370 { 371 struct bpf_link *link; 372 struct sk_fds sk_fds; 373 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); 376 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; 380 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; 385 386 exp_passive_hdr_stg.syncookie = true; 387 exp_active_hdr_stg.resend_syn = true, 388 389 prepare_out(); 390 391 /* Clear the RESEND to ensure the bpf prog can learn 392 * want_cookie and set the RESEND by itself. 393 */ 394 skel->bss->passive_synack_out.flags &= ~OPTION_F_RESEND; 395 396 /* Enforce syncookie mode */ 397 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "2")) 398 return; 399 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; 404 405 if (sk_fds_connect(&sk_fds, false)) { 406 bpf_link__destroy(link); 407 return; 408 } 409 410 check_hdr_and_close_fds(&sk_fds); 411 bpf_link__destroy(link); 412 } 413 414 static void fin(void) 415 { 416 struct bpf_link *link; 417 struct sk_fds sk_fds; 418 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); 421 422 exp_passive_fin_in.flags = OPTION_F_RAND; 423 exp_passive_fin_in.rand = 0xfa; 424 425 exp_active_fin_in.flags = OPTION_F_RAND; 426 exp_active_fin_in.rand = 0xce; 427 428 prepare_out(); 429 430 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1")) 431 return; 432 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; 437 438 if (sk_fds_connect(&sk_fds, false)) { 439 bpf_link__destroy(link); 440 return; 441 } 442 443 check_hdr_and_close_fds(&sk_fds); 444 bpf_link__destroy(link); 445 } 446 447 static void __simple_estab(bool exprm) 448 { 449 struct bpf_link *link; 450 struct sk_fds sk_fds; 451 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); 454 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; 458 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; 462 463 prepare_out(); 464 465 if (!exprm) { 466 skel->data->test_kind = 0xB9; 467 skel->data->test_magic = 0; 468 } 469 470 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1")) 471 return; 472 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; 477 478 if (sk_fds_connect(&sk_fds, false)) { 479 bpf_link__destroy(link); 480 return; 481 } 482 483 check_hdr_and_close_fds(&sk_fds); 484 bpf_link__destroy(link); 485 } 486 487 static void no_exprm_estab(void) 488 { 489 __simple_estab(false); 490 } 491 492 static void simple_estab(void) 493 { 494 __simple_estab(true); 495 } 496 497 static void misc(void) 498 { 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; 505 506 lport_linum_map_fd = bpf_map__fd(misc_skel->maps.lport_linum_map); 507 508 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1")) 509 return; 510 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; 515 516 if (sk_fds_connect(&sk_fds, false)) { 517 bpf_link__destroy(link); 518 return; 519 } 520 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; 528 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; 533 } 534 535 if (sk_fds_shutdown(&sk_fds)) 536 goto check_linum; 537 538 CHECK(misc_skel->bss->nr_syn != 1, "unexpected nr_syn", 539 "expected (1) != actual (%u)\n", 540 misc_skel->bss->nr_syn); 541 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); 545 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); 552 553 CHECK(misc_skel->bss->nr_fin != 1, "unexpected nr_fin", 554 "expected (1) != actual (%u)\n", 555 misc_skel->bss->nr_fin); 556 557 check_linum: 558 CHECK_FAIL(check_error_linum(&sk_fds)); 559 sk_fds_close(&sk_fds); 560 bpf_link__destroy(link); 561 } 562 563 struct test { 564 const char *desc; 565 void (*run)(void); 566 }; 567 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), 576 }; 577 578 void test_tcp_hdr_options(void) 579 { 580 int i; 581 582 skel = test_tcp_hdr_options__open_and_load(); 583 if (CHECK(!skel, "open and load skel", "failed")) 584 return; 585 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; 589 590 cg_fd = test__join_cgroup(CG_NAME); 591 if (CHECK_FAIL(cg_fd < 0)) 592 goto skel_destroy; 593 594 for (i = 0; i < ARRAY_SIZE(tests); i++) { 595 if (!test__start_subtest(tests[i].desc)) 596 continue; 597 598 if (create_netns()) 599 break; 600 601 tests[i].run(); 602 603 reset_test(); 604 } 605 606 close(cg_fd); 607 skel_destroy: 608 test_misc_tcp_hdr_options__destroy(misc_skel); 609 test_tcp_hdr_options__destroy(skel); 610 } 611