1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2018 Facebook */ 3 4 #include <stdlib.h> 5 #include <unistd.h> 6 #include <stdbool.h> 7 #include <string.h> 8 #include <errno.h> 9 #include <assert.h> 10 #include <fcntl.h> 11 #include <linux/bpf.h> 12 #include <linux/err.h> 13 #include <linux/types.h> 14 #include <linux/if_ether.h> 15 #include <sys/types.h> 16 #include <sys/epoll.h> 17 #include <sys/socket.h> 18 #include <netinet/in.h> 19 #include <bpf/bpf.h> 20 #include <bpf/libbpf.h> 21 #include "bpf_rlimit.h" 22 #include "bpf_util.h" 23 #include "test_select_reuseport_common.h" 24 25 #define MIN_TCPHDR_LEN 20 26 #define UDPHDR_LEN 8 27 28 #define TCP_SYNCOOKIE_SYSCTL "/proc/sys/net/ipv4/tcp_syncookies" 29 #define TCP_FO_SYSCTL "/proc/sys/net/ipv4/tcp_fastopen" 30 #define REUSEPORT_ARRAY_SIZE 32 31 32 static int result_map, tmp_index_ovr_map, linum_map, data_check_map; 33 static enum result expected_results[NR_RESULTS]; 34 static int sk_fds[REUSEPORT_ARRAY_SIZE]; 35 static int reuseport_array, outer_map; 36 static int select_by_skb_data_prog; 37 static int saved_tcp_syncookie; 38 static struct bpf_object *obj; 39 static int saved_tcp_fo; 40 static __u32 index_zero; 41 static int epfd; 42 43 static union sa46 { 44 struct sockaddr_in6 v6; 45 struct sockaddr_in v4; 46 sa_family_t family; 47 } srv_sa; 48 49 #define CHECK(condition, tag, format...) ({ \ 50 int __ret = !!(condition); \ 51 if (__ret) { \ 52 printf("%s(%d):FAIL:%s ", __func__, __LINE__, tag); \ 53 printf(format); \ 54 exit(-1); \ 55 } \ 56 }) 57 58 static void create_maps(void) 59 { 60 struct bpf_create_map_attr attr = {}; 61 62 /* Creating reuseport_array */ 63 attr.name = "reuseport_array"; 64 attr.map_type = BPF_MAP_TYPE_REUSEPORT_SOCKARRAY; 65 attr.key_size = sizeof(__u32); 66 attr.value_size = sizeof(__u32); 67 attr.max_entries = REUSEPORT_ARRAY_SIZE; 68 69 reuseport_array = bpf_create_map_xattr(&attr); 70 CHECK(reuseport_array == -1, "creating reuseport_array", 71 "reuseport_array:%d errno:%d\n", reuseport_array, errno); 72 73 /* Creating outer_map */ 74 attr.name = "outer_map"; 75 attr.map_type = BPF_MAP_TYPE_ARRAY_OF_MAPS; 76 attr.key_size = sizeof(__u32); 77 attr.value_size = sizeof(__u32); 78 attr.max_entries = 1; 79 attr.inner_map_fd = reuseport_array; 80 outer_map = bpf_create_map_xattr(&attr); 81 CHECK(outer_map == -1, "creating outer_map", 82 "outer_map:%d errno:%d\n", outer_map, errno); 83 } 84 85 static void prepare_bpf_obj(void) 86 { 87 struct bpf_program *prog; 88 struct bpf_map *map; 89 int err; 90 struct bpf_object_open_attr attr = { 91 .file = "test_select_reuseport_kern.o", 92 .prog_type = BPF_PROG_TYPE_SK_REUSEPORT, 93 }; 94 95 obj = bpf_object__open_xattr(&attr); 96 CHECK(IS_ERR_OR_NULL(obj), "open test_select_reuseport_kern.o", 97 "obj:%p PTR_ERR(obj):%ld\n", obj, PTR_ERR(obj)); 98 99 prog = bpf_program__next(NULL, obj); 100 CHECK(!prog, "get first bpf_program", "!prog\n"); 101 bpf_program__set_type(prog, attr.prog_type); 102 103 map = bpf_object__find_map_by_name(obj, "outer_map"); 104 CHECK(!map, "find outer_map", "!map\n"); 105 err = bpf_map__reuse_fd(map, outer_map); 106 CHECK(err, "reuse outer_map", "err:%d\n", err); 107 108 err = bpf_object__load(obj); 109 CHECK(err, "load bpf_object", "err:%d\n", err); 110 111 select_by_skb_data_prog = bpf_program__fd(prog); 112 CHECK(select_by_skb_data_prog == -1, "get prog fd", 113 "select_by_skb_data_prog:%d\n", select_by_skb_data_prog); 114 115 map = bpf_object__find_map_by_name(obj, "result_map"); 116 CHECK(!map, "find result_map", "!map\n"); 117 result_map = bpf_map__fd(map); 118 CHECK(result_map == -1, "get result_map fd", 119 "result_map:%d\n", result_map); 120 121 map = bpf_object__find_map_by_name(obj, "tmp_index_ovr_map"); 122 CHECK(!map, "find tmp_index_ovr_map", "!map\n"); 123 tmp_index_ovr_map = bpf_map__fd(map); 124 CHECK(tmp_index_ovr_map == -1, "get tmp_index_ovr_map fd", 125 "tmp_index_ovr_map:%d\n", tmp_index_ovr_map); 126 127 map = bpf_object__find_map_by_name(obj, "linum_map"); 128 CHECK(!map, "find linum_map", "!map\n"); 129 linum_map = bpf_map__fd(map); 130 CHECK(linum_map == -1, "get linum_map fd", 131 "linum_map:%d\n", linum_map); 132 133 map = bpf_object__find_map_by_name(obj, "data_check_map"); 134 CHECK(!map, "find data_check_map", "!map\n"); 135 data_check_map = bpf_map__fd(map); 136 CHECK(data_check_map == -1, "get data_check_map fd", 137 "data_check_map:%d\n", data_check_map); 138 } 139 140 static void sa46_init_loopback(union sa46 *sa, sa_family_t family) 141 { 142 memset(sa, 0, sizeof(*sa)); 143 sa->family = family; 144 if (sa->family == AF_INET6) 145 sa->v6.sin6_addr = in6addr_loopback; 146 else 147 sa->v4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 148 } 149 150 static void sa46_init_inany(union sa46 *sa, sa_family_t family) 151 { 152 memset(sa, 0, sizeof(*sa)); 153 sa->family = family; 154 if (sa->family == AF_INET6) 155 sa->v6.sin6_addr = in6addr_any; 156 else 157 sa->v4.sin_addr.s_addr = INADDR_ANY; 158 } 159 160 static int read_int_sysctl(const char *sysctl) 161 { 162 char buf[16]; 163 int fd, ret; 164 165 fd = open(sysctl, 0); 166 CHECK(fd == -1, "open(sysctl)", "sysctl:%s fd:%d errno:%d\n", 167 sysctl, fd, errno); 168 169 ret = read(fd, buf, sizeof(buf)); 170 CHECK(ret <= 0, "read(sysctl)", "sysctl:%s ret:%d errno:%d\n", 171 sysctl, ret, errno); 172 close(fd); 173 174 return atoi(buf); 175 } 176 177 static void write_int_sysctl(const char *sysctl, int v) 178 { 179 int fd, ret, size; 180 char buf[16]; 181 182 fd = open(sysctl, O_RDWR); 183 CHECK(fd == -1, "open(sysctl)", "sysctl:%s fd:%d errno:%d\n", 184 sysctl, fd, errno); 185 186 size = snprintf(buf, sizeof(buf), "%d", v); 187 ret = write(fd, buf, size); 188 CHECK(ret != size, "write(sysctl)", 189 "sysctl:%s ret:%d size:%d errno:%d\n", sysctl, ret, size, errno); 190 close(fd); 191 } 192 193 static void restore_sysctls(void) 194 { 195 write_int_sysctl(TCP_FO_SYSCTL, saved_tcp_fo); 196 write_int_sysctl(TCP_SYNCOOKIE_SYSCTL, saved_tcp_syncookie); 197 } 198 199 static void enable_fastopen(void) 200 { 201 int fo; 202 203 fo = read_int_sysctl(TCP_FO_SYSCTL); 204 write_int_sysctl(TCP_FO_SYSCTL, fo | 7); 205 } 206 207 static void enable_syncookie(void) 208 { 209 write_int_sysctl(TCP_SYNCOOKIE_SYSCTL, 2); 210 } 211 212 static void disable_syncookie(void) 213 { 214 write_int_sysctl(TCP_SYNCOOKIE_SYSCTL, 0); 215 } 216 217 static __u32 get_linum(void) 218 { 219 __u32 linum; 220 int err; 221 222 err = bpf_map_lookup_elem(linum_map, &index_zero, &linum); 223 CHECK(err == -1, "lookup_elem(linum_map)", "err:%d errno:%d\n", 224 err, errno); 225 226 return linum; 227 } 228 229 static void check_data(int type, sa_family_t family, const struct cmd *cmd, 230 int cli_fd) 231 { 232 struct data_check expected = {}, result; 233 union sa46 cli_sa; 234 socklen_t addrlen; 235 int err; 236 237 addrlen = sizeof(cli_sa); 238 err = getsockname(cli_fd, (struct sockaddr *)&cli_sa, 239 &addrlen); 240 CHECK(err == -1, "getsockname(cli_fd)", "err:%d errno:%d\n", 241 err, errno); 242 243 err = bpf_map_lookup_elem(data_check_map, &index_zero, &result); 244 CHECK(err == -1, "lookup_elem(data_check_map)", "err:%d errno:%d\n", 245 err, errno); 246 247 if (type == SOCK_STREAM) { 248 expected.len = MIN_TCPHDR_LEN; 249 expected.ip_protocol = IPPROTO_TCP; 250 } else { 251 expected.len = UDPHDR_LEN; 252 expected.ip_protocol = IPPROTO_UDP; 253 } 254 255 if (family == AF_INET6) { 256 expected.eth_protocol = htons(ETH_P_IPV6); 257 expected.bind_inany = !srv_sa.v6.sin6_addr.s6_addr32[3] && 258 !srv_sa.v6.sin6_addr.s6_addr32[2] && 259 !srv_sa.v6.sin6_addr.s6_addr32[1] && 260 !srv_sa.v6.sin6_addr.s6_addr32[0]; 261 262 memcpy(&expected.skb_addrs[0], cli_sa.v6.sin6_addr.s6_addr32, 263 sizeof(cli_sa.v6.sin6_addr)); 264 memcpy(&expected.skb_addrs[4], &in6addr_loopback, 265 sizeof(in6addr_loopback)); 266 expected.skb_ports[0] = cli_sa.v6.sin6_port; 267 expected.skb_ports[1] = srv_sa.v6.sin6_port; 268 } else { 269 expected.eth_protocol = htons(ETH_P_IP); 270 expected.bind_inany = !srv_sa.v4.sin_addr.s_addr; 271 272 expected.skb_addrs[0] = cli_sa.v4.sin_addr.s_addr; 273 expected.skb_addrs[1] = htonl(INADDR_LOOPBACK); 274 expected.skb_ports[0] = cli_sa.v4.sin_port; 275 expected.skb_ports[1] = srv_sa.v4.sin_port; 276 } 277 278 if (memcmp(&result, &expected, offsetof(struct data_check, 279 equal_check_end))) { 280 printf("unexpected data_check\n"); 281 printf(" result: (0x%x, %u, %u)\n", 282 result.eth_protocol, result.ip_protocol, 283 result.bind_inany); 284 printf("expected: (0x%x, %u, %u)\n", 285 expected.eth_protocol, expected.ip_protocol, 286 expected.bind_inany); 287 CHECK(1, "data_check result != expected", 288 "bpf_prog_linum:%u\n", get_linum()); 289 } 290 291 CHECK(!result.hash, "data_check result.hash empty", 292 "result.hash:%u", result.hash); 293 294 expected.len += cmd ? sizeof(*cmd) : 0; 295 if (type == SOCK_STREAM) 296 CHECK(expected.len > result.len, "expected.len > result.len", 297 "expected.len:%u result.len:%u bpf_prog_linum:%u\n", 298 expected.len, result.len, get_linum()); 299 else 300 CHECK(expected.len != result.len, "expected.len != result.len", 301 "expected.len:%u result.len:%u bpf_prog_linum:%u\n", 302 expected.len, result.len, get_linum()); 303 } 304 305 static void check_results(void) 306 { 307 __u32 results[NR_RESULTS]; 308 __u32 i, broken = 0; 309 int err; 310 311 for (i = 0; i < NR_RESULTS; i++) { 312 err = bpf_map_lookup_elem(result_map, &i, &results[i]); 313 CHECK(err == -1, "lookup_elem(result_map)", 314 "i:%u err:%d errno:%d\n", i, err, errno); 315 } 316 317 for (i = 0; i < NR_RESULTS; i++) { 318 if (results[i] != expected_results[i]) { 319 broken = i; 320 break; 321 } 322 } 323 324 if (i == NR_RESULTS) 325 return; 326 327 printf("unexpected result\n"); 328 printf(" result: ["); 329 printf("%u", results[0]); 330 for (i = 1; i < NR_RESULTS; i++) 331 printf(", %u", results[i]); 332 printf("]\n"); 333 334 printf("expected: ["); 335 printf("%u", expected_results[0]); 336 for (i = 1; i < NR_RESULTS; i++) 337 printf(", %u", expected_results[i]); 338 printf("]\n"); 339 340 CHECK(expected_results[broken] != results[broken], 341 "unexpected result", 342 "expected_results[%u] != results[%u] bpf_prog_linum:%u\n", 343 broken, broken, get_linum()); 344 } 345 346 static int send_data(int type, sa_family_t family, void *data, size_t len, 347 enum result expected) 348 { 349 union sa46 cli_sa; 350 int fd, err; 351 352 fd = socket(family, type, 0); 353 CHECK(fd == -1, "socket()", "fd:%d errno:%d\n", fd, errno); 354 355 sa46_init_loopback(&cli_sa, family); 356 err = bind(fd, (struct sockaddr *)&cli_sa, sizeof(cli_sa)); 357 CHECK(fd == -1, "bind(cli_sa)", "err:%d errno:%d\n", err, errno); 358 359 err = sendto(fd, data, len, MSG_FASTOPEN, (struct sockaddr *)&srv_sa, 360 sizeof(srv_sa)); 361 CHECK(err != len && expected >= PASS, 362 "sendto()", "family:%u err:%d errno:%d expected:%d\n", 363 family, err, errno, expected); 364 365 return fd; 366 } 367 368 static void do_test(int type, sa_family_t family, struct cmd *cmd, 369 enum result expected) 370 { 371 int nev, srv_fd, cli_fd; 372 struct epoll_event ev; 373 struct cmd rcv_cmd; 374 ssize_t nread; 375 376 cli_fd = send_data(type, family, cmd, cmd ? sizeof(*cmd) : 0, 377 expected); 378 nev = epoll_wait(epfd, &ev, 1, expected >= PASS ? 5 : 0); 379 CHECK((nev <= 0 && expected >= PASS) || 380 (nev > 0 && expected < PASS), 381 "nev <> expected", 382 "nev:%d expected:%d type:%d family:%d data:(%d, %d)\n", 383 nev, expected, type, family, 384 cmd ? cmd->reuseport_index : -1, 385 cmd ? cmd->pass_on_failure : -1); 386 check_results(); 387 check_data(type, family, cmd, cli_fd); 388 389 if (expected < PASS) 390 return; 391 392 CHECK(expected != PASS_ERR_SK_SELECT_REUSEPORT && 393 cmd->reuseport_index != ev.data.u32, 394 "check cmd->reuseport_index", 395 "cmd:(%u, %u) ev.data.u32:%u\n", 396 cmd->pass_on_failure, cmd->reuseport_index, ev.data.u32); 397 398 srv_fd = sk_fds[ev.data.u32]; 399 if (type == SOCK_STREAM) { 400 int new_fd = accept(srv_fd, NULL, 0); 401 402 CHECK(new_fd == -1, "accept(srv_fd)", 403 "ev.data.u32:%u new_fd:%d errno:%d\n", 404 ev.data.u32, new_fd, errno); 405 406 nread = recv(new_fd, &rcv_cmd, sizeof(rcv_cmd), MSG_DONTWAIT); 407 CHECK(nread != sizeof(rcv_cmd), 408 "recv(new_fd)", 409 "ev.data.u32:%u nread:%zd sizeof(rcv_cmd):%zu errno:%d\n", 410 ev.data.u32, nread, sizeof(rcv_cmd), errno); 411 412 close(new_fd); 413 } else { 414 nread = recv(srv_fd, &rcv_cmd, sizeof(rcv_cmd), MSG_DONTWAIT); 415 CHECK(nread != sizeof(rcv_cmd), 416 "recv(sk_fds)", 417 "ev.data.u32:%u nread:%zd sizeof(rcv_cmd):%zu errno:%d\n", 418 ev.data.u32, nread, sizeof(rcv_cmd), errno); 419 } 420 421 close(cli_fd); 422 } 423 424 static void test_err_inner_map(int type, sa_family_t family) 425 { 426 struct cmd cmd = { 427 .reuseport_index = 0, 428 .pass_on_failure = 0, 429 }; 430 431 printf("%s: ", __func__); 432 expected_results[DROP_ERR_INNER_MAP]++; 433 do_test(type, family, &cmd, DROP_ERR_INNER_MAP); 434 printf("OK\n"); 435 } 436 437 static void test_err_skb_data(int type, sa_family_t family) 438 { 439 printf("%s: ", __func__); 440 expected_results[DROP_ERR_SKB_DATA]++; 441 do_test(type, family, NULL, DROP_ERR_SKB_DATA); 442 printf("OK\n"); 443 } 444 445 static void test_err_sk_select_port(int type, sa_family_t family) 446 { 447 struct cmd cmd = { 448 .reuseport_index = REUSEPORT_ARRAY_SIZE, 449 .pass_on_failure = 0, 450 }; 451 452 printf("%s: ", __func__); 453 expected_results[DROP_ERR_SK_SELECT_REUSEPORT]++; 454 do_test(type, family, &cmd, DROP_ERR_SK_SELECT_REUSEPORT); 455 printf("OK\n"); 456 } 457 458 static void test_pass(int type, sa_family_t family) 459 { 460 struct cmd cmd; 461 int i; 462 463 printf("%s: ", __func__); 464 cmd.pass_on_failure = 0; 465 for (i = 0; i < REUSEPORT_ARRAY_SIZE; i++) { 466 expected_results[PASS]++; 467 cmd.reuseport_index = i; 468 do_test(type, family, &cmd, PASS); 469 } 470 printf("OK\n"); 471 } 472 473 static void test_syncookie(int type, sa_family_t family) 474 { 475 int err, tmp_index = 1; 476 struct cmd cmd = { 477 .reuseport_index = 0, 478 .pass_on_failure = 0, 479 }; 480 481 if (type != SOCK_STREAM) 482 return; 483 484 printf("%s: ", __func__); 485 /* 486 * +1 for TCP-SYN and 487 * +1 for the TCP-ACK (ack the syncookie) 488 */ 489 expected_results[PASS] += 2; 490 enable_syncookie(); 491 /* 492 * Simulate TCP-SYN and TCP-ACK are handled by two different sk: 493 * TCP-SYN: select sk_fds[tmp_index = 1] tmp_index is from the 494 * tmp_index_ovr_map 495 * TCP-ACK: select sk_fds[reuseport_index = 0] reuseport_index 496 * is from the cmd.reuseport_index 497 */ 498 err = bpf_map_update_elem(tmp_index_ovr_map, &index_zero, 499 &tmp_index, BPF_ANY); 500 CHECK(err == -1, "update_elem(tmp_index_ovr_map, 0, 1)", 501 "err:%d errno:%d\n", err, errno); 502 do_test(type, family, &cmd, PASS); 503 err = bpf_map_lookup_elem(tmp_index_ovr_map, &index_zero, 504 &tmp_index); 505 CHECK(err == -1 || tmp_index != -1, 506 "lookup_elem(tmp_index_ovr_map)", 507 "err:%d errno:%d tmp_index:%d\n", 508 err, errno, tmp_index); 509 disable_syncookie(); 510 printf("OK\n"); 511 } 512 513 static void test_pass_on_err(int type, sa_family_t family) 514 { 515 struct cmd cmd = { 516 .reuseport_index = REUSEPORT_ARRAY_SIZE, 517 .pass_on_failure = 1, 518 }; 519 520 printf("%s: ", __func__); 521 expected_results[PASS_ERR_SK_SELECT_REUSEPORT] += 1; 522 do_test(type, family, &cmd, PASS_ERR_SK_SELECT_REUSEPORT); 523 printf("OK\n"); 524 } 525 526 static void prepare_sk_fds(int type, sa_family_t family, bool inany) 527 { 528 const int first = REUSEPORT_ARRAY_SIZE - 1; 529 int i, err, optval = 1; 530 struct epoll_event ev; 531 socklen_t addrlen; 532 533 if (inany) 534 sa46_init_inany(&srv_sa, family); 535 else 536 sa46_init_loopback(&srv_sa, family); 537 addrlen = sizeof(srv_sa); 538 539 /* 540 * The sk_fds[] is filled from the back such that the order 541 * is exactly opposite to the (struct sock_reuseport *)reuse->socks[]. 542 */ 543 for (i = first; i >= 0; i--) { 544 sk_fds[i] = socket(family, type, 0); 545 CHECK(sk_fds[i] == -1, "socket()", "sk_fds[%d]:%d errno:%d\n", 546 i, sk_fds[i], errno); 547 err = setsockopt(sk_fds[i], SOL_SOCKET, SO_REUSEPORT, 548 &optval, sizeof(optval)); 549 CHECK(err == -1, "setsockopt(SO_REUSEPORT)", 550 "sk_fds[%d] err:%d errno:%d\n", 551 i, err, errno); 552 553 if (i == first) { 554 err = setsockopt(sk_fds[i], SOL_SOCKET, 555 SO_ATTACH_REUSEPORT_EBPF, 556 &select_by_skb_data_prog, 557 sizeof(select_by_skb_data_prog)); 558 CHECK(err == -1, "setsockopt(SO_ATTACH_REUEPORT_EBPF)", 559 "err:%d errno:%d\n", err, errno); 560 } 561 562 err = bind(sk_fds[i], (struct sockaddr *)&srv_sa, addrlen); 563 CHECK(err == -1, "bind()", "sk_fds[%d] err:%d errno:%d\n", 564 i, err, errno); 565 566 if (type == SOCK_STREAM) { 567 err = listen(sk_fds[i], 10); 568 CHECK(err == -1, "listen()", 569 "sk_fds[%d] err:%d errno:%d\n", 570 i, err, errno); 571 } 572 573 err = bpf_map_update_elem(reuseport_array, &i, &sk_fds[i], 574 BPF_NOEXIST); 575 CHECK(err == -1, "update_elem(reuseport_array)", 576 "sk_fds[%d] err:%d errno:%d\n", i, err, errno); 577 578 if (i == first) { 579 socklen_t addrlen = sizeof(srv_sa); 580 581 err = getsockname(sk_fds[i], (struct sockaddr *)&srv_sa, 582 &addrlen); 583 CHECK(err == -1, "getsockname()", 584 "sk_fds[%d] err:%d errno:%d\n", i, err, errno); 585 } 586 } 587 588 epfd = epoll_create(1); 589 CHECK(epfd == -1, "epoll_create(1)", 590 "epfd:%d errno:%d\n", epfd, errno); 591 592 ev.events = EPOLLIN; 593 for (i = 0; i < REUSEPORT_ARRAY_SIZE; i++) { 594 ev.data.u32 = i; 595 err = epoll_ctl(epfd, EPOLL_CTL_ADD, sk_fds[i], &ev); 596 CHECK(err, "epoll_ctl(EPOLL_CTL_ADD)", "sk_fds[%d]\n", i); 597 } 598 } 599 600 static void setup_per_test(int type, unsigned short family, bool inany) 601 { 602 int ovr = -1, err; 603 604 prepare_sk_fds(type, family, inany); 605 err = bpf_map_update_elem(tmp_index_ovr_map, &index_zero, &ovr, 606 BPF_ANY); 607 CHECK(err == -1, "update_elem(tmp_index_ovr_map, 0, -1)", 608 "err:%d errno:%d\n", err, errno); 609 } 610 611 static void cleanup_per_test(void) 612 { 613 int i, err; 614 615 for (i = 0; i < REUSEPORT_ARRAY_SIZE; i++) 616 close(sk_fds[i]); 617 close(epfd); 618 619 err = bpf_map_delete_elem(outer_map, &index_zero); 620 CHECK(err == -1, "delete_elem(outer_map)", 621 "err:%d errno:%d\n", err, errno); 622 } 623 624 static void cleanup(void) 625 { 626 close(outer_map); 627 close(reuseport_array); 628 bpf_object__close(obj); 629 } 630 631 static void test_all(void) 632 { 633 /* Extra SOCK_STREAM to test bind_inany==true */ 634 const int types[] = { SOCK_STREAM, SOCK_DGRAM, SOCK_STREAM }; 635 const char * const type_strings[] = { "TCP", "UDP", "TCP" }; 636 const char * const family_strings[] = { "IPv6", "IPv4" }; 637 const unsigned short families[] = { AF_INET6, AF_INET }; 638 const bool bind_inany[] = { false, false, true }; 639 int t, f, err; 640 641 for (f = 0; f < ARRAY_SIZE(families); f++) { 642 unsigned short family = families[f]; 643 644 for (t = 0; t < ARRAY_SIZE(types); t++) { 645 bool inany = bind_inany[t]; 646 int type = types[t]; 647 648 printf("######## %s/%s %s ########\n", 649 family_strings[f], type_strings[t], 650 inany ? " INANY " : "LOOPBACK"); 651 652 setup_per_test(type, family, inany); 653 654 test_err_inner_map(type, family); 655 656 /* Install reuseport_array to the outer_map */ 657 err = bpf_map_update_elem(outer_map, &index_zero, 658 &reuseport_array, BPF_ANY); 659 CHECK(err == -1, "update_elem(outer_map)", 660 "err:%d errno:%d\n", err, errno); 661 662 test_err_skb_data(type, family); 663 test_err_sk_select_port(type, family); 664 test_pass(type, family); 665 test_syncookie(type, family); 666 test_pass_on_err(type, family); 667 668 cleanup_per_test(); 669 printf("\n"); 670 } 671 } 672 } 673 674 int main(int argc, const char **argv) 675 { 676 create_maps(); 677 prepare_bpf_obj(); 678 saved_tcp_fo = read_int_sysctl(TCP_FO_SYSCTL); 679 saved_tcp_syncookie = read_int_sysctl(TCP_SYNCOOKIE_SYSCTL); 680 enable_fastopen(); 681 disable_syncookie(); 682 atexit(restore_sysctls); 683 684 test_all(); 685 686 cleanup(); 687 return 0; 688 } 689