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