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 "::eB9F" 19 #define CG_NAME "/tcpbpf-hdr-opt-test" 20 21 struct bpf_test_option exp_passive_estab_in; 22 struct bpf_test_option exp_active_estab_in; 23 struct bpf_test_option exp_passive_fin_in; 24 struct bpf_test_option exp_active_fin_in; 25 struct hdr_stg exp_passive_hdr_stg; 26 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 add_lo_addr(void) 44 { 45 char ip_addr_cmd[256]; 46 int cmdlen; 47 48 cmdlen = snprintf(ip_addr_cmd, sizeof(ip_addr_cmd), 49 "ip -6 addr add %s/128 dev lo scope host", 50 LO_ADDR6); 51 52 if (CHECK(cmdlen >= sizeof(ip_addr_cmd), "compile ip cmd", 53 "failed to add host addr %s to lo. ip cmdlen is too long\n", 54 LO_ADDR6)) 55 return -1; 56 57 if (CHECK(system(ip_addr_cmd), "run ip cmd", 58 "failed to add host addr %s to lo\n", LO_ADDR6)) 59 return -1; 60 61 return 0; 62 } 63 64 static int create_netns(void) 65 { 66 if (CHECK(unshare(CLONE_NEWNET), "create netns", 67 "unshare(CLONE_NEWNET): %s (%d)", 68 strerror(errno), errno)) 69 return -1; 70 71 if (CHECK(system("ip link set dev lo up"), "run ip cmd", 72 "failed to bring lo link up\n")) 73 return -1; 74 75 if (add_lo_addr()) 76 return -1; 77 78 return 0; 79 } 80 81 static int write_sysctl(const char *sysctl, const char *value) 82 { 83 int fd, err, len; 84 85 fd = open(sysctl, O_WRONLY); 86 if (CHECK(fd == -1, "open sysctl", "open(%s): %s (%d)\n", 87 sysctl, strerror(errno), errno)) 88 return -1; 89 90 len = strlen(value); 91 err = write(fd, value, len); 92 close(fd); 93 if (CHECK(err != len, "write sysctl", 94 "write(%s, %s): err:%d %s (%d)\n", 95 sysctl, value, err, strerror(errno), errno)) 96 return -1; 97 98 return 0; 99 } 100 101 static void print_hdr_stg(const struct hdr_stg *hdr_stg, const char *prefix) 102 { 103 fprintf(stderr, "%s{active:%u, resend_syn:%u, syncookie:%u, fastopen:%u}\n", 104 prefix ? : "", hdr_stg->active, hdr_stg->resend_syn, 105 hdr_stg->syncookie, hdr_stg->fastopen); 106 } 107 108 static void print_option(const struct bpf_test_option *opt, const char *prefix) 109 { 110 fprintf(stderr, "%s{flags:0x%x, max_delack_ms:%u, rand:0x%x}\n", 111 prefix ? : "", opt->flags, opt->max_delack_ms, opt->rand); 112 } 113 114 static void sk_fds_close(struct sk_fds *sk_fds) 115 { 116 close(sk_fds->srv_fd); 117 close(sk_fds->passive_fd); 118 close(sk_fds->active_fd); 119 } 120 121 static int sk_fds_shutdown(struct sk_fds *sk_fds) 122 { 123 int ret, abyte; 124 125 shutdown(sk_fds->active_fd, SHUT_WR); 126 ret = read(sk_fds->passive_fd, &abyte, sizeof(abyte)); 127 if (CHECK(ret != 0, "read-after-shutdown(passive_fd):", 128 "ret:%d %s (%d)\n", 129 ret, strerror(errno), errno)) 130 return -1; 131 132 shutdown(sk_fds->passive_fd, SHUT_WR); 133 ret = read(sk_fds->active_fd, &abyte, sizeof(abyte)); 134 if (CHECK(ret != 0, "read-after-shutdown(active_fd):", 135 "ret:%d %s (%d)\n", 136 ret, strerror(errno), errno)) 137 return -1; 138 139 return 0; 140 } 141 142 static int sk_fds_connect(struct sk_fds *sk_fds, bool fast_open) 143 { 144 const char fast[] = "FAST!!!"; 145 struct sockaddr_in6 addr6; 146 socklen_t len; 147 148 sk_fds->srv_fd = start_server(AF_INET6, SOCK_STREAM, LO_ADDR6, 0, 0); 149 if (CHECK(sk_fds->srv_fd == -1, "start_server", "%s (%d)\n", 150 strerror(errno), errno)) 151 goto error; 152 153 if (fast_open) 154 sk_fds->active_fd = fastopen_connect(sk_fds->srv_fd, fast, 155 sizeof(fast), 0); 156 else 157 sk_fds->active_fd = connect_to_fd(sk_fds->srv_fd, 0); 158 159 if (CHECK_FAIL(sk_fds->active_fd == -1)) { 160 close(sk_fds->srv_fd); 161 goto error; 162 } 163 164 len = sizeof(addr6); 165 if (CHECK(getsockname(sk_fds->srv_fd, (struct sockaddr *)&addr6, 166 &len), "getsockname(srv_fd)", "%s (%d)\n", 167 strerror(errno), errno)) 168 goto error_close; 169 sk_fds->passive_lport = ntohs(addr6.sin6_port); 170 171 len = sizeof(addr6); 172 if (CHECK(getsockname(sk_fds->active_fd, (struct sockaddr *)&addr6, 173 &len), "getsockname(active_fd)", "%s (%d)\n", 174 strerror(errno), errno)) 175 goto error_close; 176 sk_fds->active_lport = ntohs(addr6.sin6_port); 177 178 sk_fds->passive_fd = accept(sk_fds->srv_fd, NULL, 0); 179 if (CHECK(sk_fds->passive_fd == -1, "accept(srv_fd)", "%s (%d)\n", 180 strerror(errno), errno)) 181 goto error_close; 182 183 if (fast_open) { 184 char bytes_in[sizeof(fast)]; 185 int ret; 186 187 ret = read(sk_fds->passive_fd, bytes_in, sizeof(bytes_in)); 188 if (CHECK(ret != sizeof(fast), "read fastopen syn data", 189 "expected=%lu actual=%d\n", sizeof(fast), ret)) { 190 close(sk_fds->passive_fd); 191 goto error_close; 192 } 193 } 194 195 return 0; 196 197 error_close: 198 close(sk_fds->active_fd); 199 close(sk_fds->srv_fd); 200 201 error: 202 memset(sk_fds, -1, sizeof(*sk_fds)); 203 return -1; 204 } 205 206 static int check_hdr_opt(const struct bpf_test_option *exp, 207 const struct bpf_test_option *act, 208 const char *hdr_desc) 209 { 210 if (CHECK(memcmp(exp, act, sizeof(*exp)), 211 "expected-vs-actual", "unexpected %s\n", hdr_desc)) { 212 print_option(exp, "expected: "); 213 print_option(act, " actual: "); 214 return -1; 215 } 216 217 return 0; 218 } 219 220 static int check_hdr_stg(const struct hdr_stg *exp, int fd, 221 const char *stg_desc) 222 { 223 struct hdr_stg act; 224 225 if (CHECK(bpf_map_lookup_elem(hdr_stg_map_fd, &fd, &act), 226 "map_lookup(hdr_stg_map_fd)", "%s %s (%d)\n", 227 stg_desc, strerror(errno), errno)) 228 return -1; 229 230 if (CHECK(memcmp(exp, &act, sizeof(*exp)), 231 "expected-vs-actual", "unexpected %s\n", stg_desc)) { 232 print_hdr_stg(exp, "expected: "); 233 print_hdr_stg(&act, " actual: "); 234 return -1; 235 } 236 237 return 0; 238 } 239 240 static int check_error_linum(const struct sk_fds *sk_fds) 241 { 242 unsigned int nr_errors = 0; 243 struct linum_err linum_err; 244 int lport; 245 246 lport = sk_fds->passive_lport; 247 if (!bpf_map_lookup_elem(lport_linum_map_fd, &lport, &linum_err)) { 248 fprintf(stderr, 249 "bpf prog error out at lport:passive(%d), linum:%u err:%d\n", 250 lport, linum_err.linum, linum_err.err); 251 nr_errors++; 252 } 253 254 lport = sk_fds->active_lport; 255 if (!bpf_map_lookup_elem(lport_linum_map_fd, &lport, &linum_err)) { 256 fprintf(stderr, 257 "bpf prog error out at lport:active(%d), linum:%u err:%d\n", 258 lport, linum_err.linum, linum_err.err); 259 nr_errors++; 260 } 261 262 return nr_errors; 263 } 264 265 static void check_hdr_and_close_fds(struct sk_fds *sk_fds) 266 { 267 if (sk_fds_shutdown(sk_fds)) 268 goto check_linum; 269 270 if (check_hdr_stg(&exp_passive_hdr_stg, sk_fds->passive_fd, 271 "passive_hdr_stg")) 272 goto check_linum; 273 274 if (check_hdr_stg(&exp_active_hdr_stg, sk_fds->active_fd, 275 "active_hdr_stg")) 276 goto check_linum; 277 278 if (check_hdr_opt(&exp_passive_estab_in, &skel->bss->passive_estab_in, 279 "passive_estab_in")) 280 goto check_linum; 281 282 if (check_hdr_opt(&exp_active_estab_in, &skel->bss->active_estab_in, 283 "active_estab_in")) 284 goto check_linum; 285 286 if (check_hdr_opt(&exp_passive_fin_in, &skel->bss->passive_fin_in, 287 "passive_fin_in")) 288 goto check_linum; 289 290 check_hdr_opt(&exp_active_fin_in, &skel->bss->active_fin_in, 291 "active_fin_in"); 292 293 check_linum: 294 CHECK_FAIL(check_error_linum(sk_fds)); 295 sk_fds_close(sk_fds); 296 } 297 298 static void prepare_out(void) 299 { 300 skel->bss->active_syn_out = exp_passive_estab_in; 301 skel->bss->passive_synack_out = exp_active_estab_in; 302 303 skel->bss->active_fin_out = exp_passive_fin_in; 304 skel->bss->passive_fin_out = exp_active_fin_in; 305 } 306 307 static void reset_test(void) 308 { 309 size_t optsize = sizeof(struct bpf_test_option); 310 int lport, err; 311 312 memset(&skel->bss->passive_synack_out, 0, optsize); 313 memset(&skel->bss->passive_fin_out, 0, optsize); 314 315 memset(&skel->bss->passive_estab_in, 0, optsize); 316 memset(&skel->bss->passive_fin_in, 0, optsize); 317 318 memset(&skel->bss->active_syn_out, 0, optsize); 319 memset(&skel->bss->active_fin_out, 0, optsize); 320 321 memset(&skel->bss->active_estab_in, 0, optsize); 322 memset(&skel->bss->active_fin_in, 0, optsize); 323 324 skel->data->test_kind = TCPOPT_EXP; 325 skel->data->test_magic = 0xeB9F; 326 327 memset(&exp_passive_estab_in, 0, optsize); 328 memset(&exp_active_estab_in, 0, optsize); 329 memset(&exp_passive_fin_in, 0, optsize); 330 memset(&exp_active_fin_in, 0, optsize); 331 332 memset(&exp_passive_hdr_stg, 0, sizeof(exp_passive_hdr_stg)); 333 memset(&exp_active_hdr_stg, 0, sizeof(exp_active_hdr_stg)); 334 exp_active_hdr_stg.active = true; 335 336 err = bpf_map_get_next_key(lport_linum_map_fd, NULL, &lport); 337 while (!err) { 338 bpf_map_delete_elem(lport_linum_map_fd, &lport); 339 err = bpf_map_get_next_key(lport_linum_map_fd, &lport, &lport); 340 } 341 } 342 343 static void fastopen_estab(void) 344 { 345 struct bpf_link *link; 346 struct sk_fds sk_fds; 347 348 hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map); 349 lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map); 350 351 exp_passive_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS; 352 exp_passive_estab_in.rand = 0xfa; 353 exp_passive_estab_in.max_delack_ms = 11; 354 355 exp_active_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS; 356 exp_active_estab_in.rand = 0xce; 357 exp_active_estab_in.max_delack_ms = 22; 358 359 exp_passive_hdr_stg.fastopen = true; 360 361 prepare_out(); 362 363 /* Allow fastopen without fastopen cookie */ 364 if (write_sysctl("/proc/sys/net/ipv4/tcp_fastopen", "1543")) 365 return; 366 367 link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd); 368 if (CHECK(IS_ERR(link), "attach_cgroup(estab)", "err: %ld\n", 369 PTR_ERR(link))) 370 return; 371 372 if (sk_fds_connect(&sk_fds, true)) { 373 bpf_link__destroy(link); 374 return; 375 } 376 377 check_hdr_and_close_fds(&sk_fds); 378 bpf_link__destroy(link); 379 } 380 381 static void syncookie_estab(void) 382 { 383 struct bpf_link *link; 384 struct sk_fds sk_fds; 385 386 hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map); 387 lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map); 388 389 exp_passive_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS; 390 exp_passive_estab_in.rand = 0xfa; 391 exp_passive_estab_in.max_delack_ms = 11; 392 393 exp_active_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS | 394 OPTION_F_RESEND; 395 exp_active_estab_in.rand = 0xce; 396 exp_active_estab_in.max_delack_ms = 22; 397 398 exp_passive_hdr_stg.syncookie = true; 399 exp_active_hdr_stg.resend_syn = true, 400 401 prepare_out(); 402 403 /* Clear the RESEND to ensure the bpf prog can learn 404 * want_cookie and set the RESEND by itself. 405 */ 406 skel->bss->passive_synack_out.flags &= ~OPTION_F_RESEND; 407 408 /* Enforce syncookie mode */ 409 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "2")) 410 return; 411 412 link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd); 413 if (CHECK(IS_ERR(link), "attach_cgroup(estab)", "err: %ld\n", 414 PTR_ERR(link))) 415 return; 416 417 if (sk_fds_connect(&sk_fds, false)) { 418 bpf_link__destroy(link); 419 return; 420 } 421 422 check_hdr_and_close_fds(&sk_fds); 423 bpf_link__destroy(link); 424 } 425 426 static void fin(void) 427 { 428 struct bpf_link *link; 429 struct sk_fds sk_fds; 430 431 hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map); 432 lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map); 433 434 exp_passive_fin_in.flags = OPTION_F_RAND; 435 exp_passive_fin_in.rand = 0xfa; 436 437 exp_active_fin_in.flags = OPTION_F_RAND; 438 exp_active_fin_in.rand = 0xce; 439 440 prepare_out(); 441 442 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1")) 443 return; 444 445 link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd); 446 if (CHECK(IS_ERR(link), "attach_cgroup(estab)", "err: %ld\n", 447 PTR_ERR(link))) 448 return; 449 450 if (sk_fds_connect(&sk_fds, false)) { 451 bpf_link__destroy(link); 452 return; 453 } 454 455 check_hdr_and_close_fds(&sk_fds); 456 bpf_link__destroy(link); 457 } 458 459 static void __simple_estab(bool exprm) 460 { 461 struct bpf_link *link; 462 struct sk_fds sk_fds; 463 464 hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map); 465 lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map); 466 467 exp_passive_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS; 468 exp_passive_estab_in.rand = 0xfa; 469 exp_passive_estab_in.max_delack_ms = 11; 470 471 exp_active_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS; 472 exp_active_estab_in.rand = 0xce; 473 exp_active_estab_in.max_delack_ms = 22; 474 475 prepare_out(); 476 477 if (!exprm) { 478 skel->data->test_kind = 0xB9; 479 skel->data->test_magic = 0; 480 } 481 482 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1")) 483 return; 484 485 link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd); 486 if (CHECK(IS_ERR(link), "attach_cgroup(estab)", "err: %ld\n", 487 PTR_ERR(link))) 488 return; 489 490 if (sk_fds_connect(&sk_fds, false)) { 491 bpf_link__destroy(link); 492 return; 493 } 494 495 check_hdr_and_close_fds(&sk_fds); 496 bpf_link__destroy(link); 497 } 498 499 static void no_exprm_estab(void) 500 { 501 __simple_estab(false); 502 } 503 504 static void simple_estab(void) 505 { 506 __simple_estab(true); 507 } 508 509 static void misc(void) 510 { 511 const char send_msg[] = "MISC!!!"; 512 char recv_msg[sizeof(send_msg)]; 513 const unsigned int nr_data = 2; 514 struct bpf_link *link; 515 struct sk_fds sk_fds; 516 int i, ret; 517 518 lport_linum_map_fd = bpf_map__fd(misc_skel->maps.lport_linum_map); 519 520 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1")) 521 return; 522 523 link = bpf_program__attach_cgroup(misc_skel->progs.misc_estab, cg_fd); 524 if (CHECK(IS_ERR(link), "attach_cgroup(misc_estab)", "err: %ld\n", 525 PTR_ERR(link))) 526 return; 527 528 if (sk_fds_connect(&sk_fds, false)) { 529 bpf_link__destroy(link); 530 return; 531 } 532 533 for (i = 0; i < nr_data; i++) { 534 /* MSG_EOR to ensure skb will not be combined */ 535 ret = send(sk_fds.active_fd, send_msg, sizeof(send_msg), 536 MSG_EOR); 537 if (CHECK(ret != sizeof(send_msg), "send(msg)", "ret:%d\n", 538 ret)) 539 goto check_linum; 540 541 ret = read(sk_fds.passive_fd, recv_msg, sizeof(recv_msg)); 542 if (CHECK(ret != sizeof(send_msg), "read(msg)", "ret:%d\n", 543 ret)) 544 goto check_linum; 545 } 546 547 if (sk_fds_shutdown(&sk_fds)) 548 goto check_linum; 549 550 CHECK(misc_skel->bss->nr_syn != 1, "unexpected nr_syn", 551 "expected (1) != actual (%u)\n", 552 misc_skel->bss->nr_syn); 553 554 CHECK(misc_skel->bss->nr_data != nr_data, "unexpected nr_data", 555 "expected (%u) != actual (%u)\n", 556 nr_data, misc_skel->bss->nr_data); 557 558 /* The last ACK may have been delayed, so it is either 1 or 2. */ 559 CHECK(misc_skel->bss->nr_pure_ack != 1 && 560 misc_skel->bss->nr_pure_ack != 2, 561 "unexpected nr_pure_ack", 562 "expected (1 or 2) != actual (%u)\n", 563 misc_skel->bss->nr_pure_ack); 564 565 CHECK(misc_skel->bss->nr_fin != 1, "unexpected nr_fin", 566 "expected (1) != actual (%u)\n", 567 misc_skel->bss->nr_fin); 568 569 check_linum: 570 CHECK_FAIL(check_error_linum(&sk_fds)); 571 sk_fds_close(&sk_fds); 572 bpf_link__destroy(link); 573 } 574 575 struct test { 576 const char *desc; 577 void (*run)(void); 578 }; 579 580 #define DEF_TEST(name) { #name, name } 581 static struct test tests[] = { 582 DEF_TEST(simple_estab), 583 DEF_TEST(no_exprm_estab), 584 DEF_TEST(syncookie_estab), 585 DEF_TEST(fastopen_estab), 586 DEF_TEST(fin), 587 DEF_TEST(misc), 588 }; 589 590 void test_tcp_hdr_options(void) 591 { 592 int i; 593 594 skel = test_tcp_hdr_options__open_and_load(); 595 if (CHECK(!skel, "open and load skel", "failed")) 596 return; 597 598 misc_skel = test_misc_tcp_hdr_options__open_and_load(); 599 if (CHECK(!misc_skel, "open and load misc test skel", "failed")) 600 goto skel_destroy; 601 602 cg_fd = test__join_cgroup(CG_NAME); 603 if (CHECK_FAIL(cg_fd < 0)) 604 goto skel_destroy; 605 606 for (i = 0; i < ARRAY_SIZE(tests); i++) { 607 if (!test__start_subtest(tests[i].desc)) 608 continue; 609 610 if (create_netns()) 611 break; 612 613 tests[i].run(); 614 615 reset_test(); 616 } 617 618 close(cg_fd); 619 skel_destroy: 620 test_misc_tcp_hdr_options__destroy(misc_skel); 621 test_tcp_hdr_options__destroy(skel); 622 } 623