860 lines · c
1// SPDX-License-Identifier: GPL-2.02/* 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_util.h"22 23#include "test_progs.h"24#include "test_select_reuseport_common.h"25 26#define MAX_TEST_NAME 8027#define MIN_TCPHDR_LEN 2028#define UDPHDR_LEN 829 30#define TCP_SYNCOOKIE_SYSCTL "/proc/sys/net/ipv4/tcp_syncookies"31#define TCP_FO_SYSCTL "/proc/sys/net/ipv4/tcp_fastopen"32#define REUSEPORT_ARRAY_SIZE 3233 34static int result_map, tmp_index_ovr_map, linum_map, data_check_map;35static __u32 expected_results[NR_RESULTS];36static int sk_fds[REUSEPORT_ARRAY_SIZE];37static int reuseport_array = -1, outer_map = -1;38static enum bpf_map_type inner_map_type;39static int select_by_skb_data_prog;40static struct bpf_object *obj;41static __u32 index_zero;42static int epfd;43 44static union sa46 {45 struct sockaddr_in6 v6;46 struct sockaddr_in v4;47 sa_family_t family;48} srv_sa;49 50#define RET_IF(condition, tag, format...) ({ \51 if (CHECK_FAIL(condition)) { \52 printf(tag " " format); \53 return; \54 } \55})56 57#define RET_ERR(condition, tag, format...) ({ \58 if (CHECK_FAIL(condition)) { \59 printf(tag " " format); \60 return -1; \61 } \62})63 64static int create_maps(enum bpf_map_type inner_type)65{66 LIBBPF_OPTS(bpf_map_create_opts, opts);67 68 inner_map_type = inner_type;69 70 /* Creating reuseport_array */71 reuseport_array = bpf_map_create(inner_type, "reuseport_array",72 sizeof(__u32), sizeof(__u32), REUSEPORT_ARRAY_SIZE, NULL);73 RET_ERR(reuseport_array < 0, "creating reuseport_array",74 "reuseport_array:%d errno:%d\n", reuseport_array, errno);75 76 /* Creating outer_map */77 opts.inner_map_fd = reuseport_array;78 outer_map = bpf_map_create(BPF_MAP_TYPE_ARRAY_OF_MAPS, "outer_map",79 sizeof(__u32), sizeof(__u32), 1, &opts);80 RET_ERR(outer_map < 0, "creating outer_map",81 "outer_map:%d errno:%d\n", outer_map, errno);82 83 return 0;84}85 86static int prepare_bpf_obj(void)87{88 struct bpf_program *prog;89 struct bpf_map *map;90 int err;91 92 obj = bpf_object__open("test_select_reuseport_kern.bpf.o");93 err = libbpf_get_error(obj);94 RET_ERR(err, "open test_select_reuseport_kern.bpf.o",95 "obj:%p PTR_ERR(obj):%d\n", obj, err);96 97 map = bpf_object__find_map_by_name(obj, "outer_map");98 RET_ERR(!map, "find outer_map", "!map\n");99 err = bpf_map__reuse_fd(map, outer_map);100 RET_ERR(err, "reuse outer_map", "err:%d\n", err);101 102 err = bpf_object__load(obj);103 RET_ERR(err, "load bpf_object", "err:%d\n", err);104 105 prog = bpf_object__next_program(obj, NULL);106 RET_ERR(!prog, "get first bpf_program", "!prog\n");107 select_by_skb_data_prog = bpf_program__fd(prog);108 RET_ERR(select_by_skb_data_prog < 0, "get prog fd",109 "select_by_skb_data_prog:%d\n", select_by_skb_data_prog);110 111 map = bpf_object__find_map_by_name(obj, "result_map");112 RET_ERR(!map, "find result_map", "!map\n");113 result_map = bpf_map__fd(map);114 RET_ERR(result_map < 0, "get result_map fd",115 "result_map:%d\n", result_map);116 117 map = bpf_object__find_map_by_name(obj, "tmp_index_ovr_map");118 RET_ERR(!map, "find tmp_index_ovr_map\n", "!map");119 tmp_index_ovr_map = bpf_map__fd(map);120 RET_ERR(tmp_index_ovr_map < 0, "get tmp_index_ovr_map fd",121 "tmp_index_ovr_map:%d\n", tmp_index_ovr_map);122 123 map = bpf_object__find_map_by_name(obj, "linum_map");124 RET_ERR(!map, "find linum_map", "!map\n");125 linum_map = bpf_map__fd(map);126 RET_ERR(linum_map < 0, "get linum_map fd",127 "linum_map:%d\n", linum_map);128 129 map = bpf_object__find_map_by_name(obj, "data_check_map");130 RET_ERR(!map, "find data_check_map", "!map\n");131 data_check_map = bpf_map__fd(map);132 RET_ERR(data_check_map < 0, "get data_check_map fd",133 "data_check_map:%d\n", data_check_map);134 135 return 0;136}137 138static void sa46_init_loopback(union sa46 *sa, sa_family_t family)139{140 memset(sa, 0, sizeof(*sa));141 sa->family = family;142 if (sa->family == AF_INET6)143 sa->v6.sin6_addr = in6addr_loopback;144 else145 sa->v4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);146}147 148static void sa46_init_inany(union sa46 *sa, sa_family_t family)149{150 memset(sa, 0, sizeof(*sa));151 sa->family = family;152 if (sa->family == AF_INET6)153 sa->v6.sin6_addr = in6addr_any;154 else155 sa->v4.sin_addr.s_addr = INADDR_ANY;156}157 158static int read_int_sysctl(const char *sysctl)159{160 char buf[16];161 int fd, ret;162 163 fd = open(sysctl, 0);164 RET_ERR(fd == -1, "open(sysctl)",165 "sysctl:%s fd:%d errno:%d\n", sysctl, fd, errno);166 167 ret = read(fd, buf, sizeof(buf));168 RET_ERR(ret <= 0, "read(sysctl)",169 "sysctl:%s ret:%d errno:%d\n", sysctl, ret, errno);170 171 close(fd);172 return atoi(buf);173}174 175static int write_int_sysctl(const char *sysctl, int v)176{177 int fd, ret, size;178 char buf[16];179 180 fd = open(sysctl, O_RDWR);181 RET_ERR(fd == -1, "open(sysctl)",182 "sysctl:%s fd:%d errno:%d\n", sysctl, fd, errno);183 184 size = snprintf(buf, sizeof(buf), "%d", v);185 ret = write(fd, buf, size);186 RET_ERR(ret != size, "write(sysctl)",187 "sysctl:%s ret:%d size:%d errno:%d\n",188 sysctl, ret, size, errno);189 190 close(fd);191 return 0;192}193 194static int enable_fastopen(void)195{196 int fo;197 198 fo = read_int_sysctl(TCP_FO_SYSCTL);199 if (fo < 0)200 return -1;201 202 return write_int_sysctl(TCP_FO_SYSCTL, fo | 7);203}204 205static int enable_syncookie(void)206{207 return write_int_sysctl(TCP_SYNCOOKIE_SYSCTL, 2);208}209 210static int disable_syncookie(void)211{212 return write_int_sysctl(TCP_SYNCOOKIE_SYSCTL, 0);213}214 215static long get_linum(void)216{217 __u32 linum;218 int err;219 220 err = bpf_map_lookup_elem(linum_map, &index_zero, &linum);221 RET_ERR(err < 0, "lookup_elem(linum_map)", "err:%d errno:%d\n",222 err, errno);223 224 return linum;225}226 227static void check_data(int type, sa_family_t family, const struct cmd *cmd,228 int cli_fd)229{230 struct data_check expected = {}, result;231 union sa46 cli_sa;232 socklen_t addrlen;233 int err;234 235 addrlen = sizeof(cli_sa);236 err = getsockname(cli_fd, (struct sockaddr *)&cli_sa,237 &addrlen);238 RET_IF(err < 0, "getsockname(cli_fd)", "err:%d errno:%d\n",239 err, errno);240 241 err = bpf_map_lookup_elem(data_check_map, &index_zero, &result);242 RET_IF(err < 0, "lookup_elem(data_check_map)", "err:%d errno:%d\n",243 err, errno);244 245 if (type == SOCK_STREAM) {246 expected.len = MIN_TCPHDR_LEN;247 expected.ip_protocol = IPPROTO_TCP;248 } else {249 expected.len = UDPHDR_LEN;250 expected.ip_protocol = IPPROTO_UDP;251 }252 253 if (family == AF_INET6) {254 expected.eth_protocol = htons(ETH_P_IPV6);255 expected.bind_inany = !srv_sa.v6.sin6_addr.s6_addr32[3] &&256 !srv_sa.v6.sin6_addr.s6_addr32[2] &&257 !srv_sa.v6.sin6_addr.s6_addr32[1] &&258 !srv_sa.v6.sin6_addr.s6_addr32[0];259 260 memcpy(&expected.skb_addrs[0], cli_sa.v6.sin6_addr.s6_addr32,261 sizeof(cli_sa.v6.sin6_addr));262 memcpy(&expected.skb_addrs[4], &in6addr_loopback,263 sizeof(in6addr_loopback));264 expected.skb_ports[0] = cli_sa.v6.sin6_port;265 expected.skb_ports[1] = srv_sa.v6.sin6_port;266 } else {267 expected.eth_protocol = htons(ETH_P_IP);268 expected.bind_inany = !srv_sa.v4.sin_addr.s_addr;269 270 expected.skb_addrs[0] = cli_sa.v4.sin_addr.s_addr;271 expected.skb_addrs[1] = htonl(INADDR_LOOPBACK);272 expected.skb_ports[0] = cli_sa.v4.sin_port;273 expected.skb_ports[1] = srv_sa.v4.sin_port;274 }275 276 if (memcmp(&result, &expected, offsetof(struct data_check,277 equal_check_end))) {278 printf("unexpected data_check\n");279 printf(" result: (0x%x, %u, %u)\n",280 result.eth_protocol, result.ip_protocol,281 result.bind_inany);282 printf("expected: (0x%x, %u, %u)\n",283 expected.eth_protocol, expected.ip_protocol,284 expected.bind_inany);285 RET_IF(1, "data_check result != expected",286 "bpf_prog_linum:%ld\n", get_linum());287 }288 289 RET_IF(!result.hash, "data_check result.hash empty",290 "result.hash:%u", result.hash);291 292 expected.len += cmd ? sizeof(*cmd) : 0;293 if (type == SOCK_STREAM)294 RET_IF(expected.len > result.len, "expected.len > result.len",295 "expected.len:%u result.len:%u bpf_prog_linum:%ld\n",296 expected.len, result.len, get_linum());297 else298 RET_IF(expected.len != result.len, "expected.len != result.len",299 "expected.len:%u result.len:%u bpf_prog_linum:%ld\n",300 expected.len, result.len, get_linum());301}302 303static const char *result_to_str(enum result res)304{305 switch (res) {306 case DROP_ERR_INNER_MAP:307 return "DROP_ERR_INNER_MAP";308 case DROP_ERR_SKB_DATA:309 return "DROP_ERR_SKB_DATA";310 case DROP_ERR_SK_SELECT_REUSEPORT:311 return "DROP_ERR_SK_SELECT_REUSEPORT";312 case DROP_MISC:313 return "DROP_MISC";314 case PASS:315 return "PASS";316 case PASS_ERR_SK_SELECT_REUSEPORT:317 return "PASS_ERR_SK_SELECT_REUSEPORT";318 default:319 return "UNKNOWN";320 }321}322 323static void check_results(void)324{325 __u32 results[NR_RESULTS];326 __u32 i, broken = 0;327 int err;328 329 for (i = 0; i < NR_RESULTS; i++) {330 err = bpf_map_lookup_elem(result_map, &i, &results[i]);331 RET_IF(err < 0, "lookup_elem(result_map)",332 "i:%u err:%d errno:%d\n", i, err, errno);333 }334 335 for (i = 0; i < NR_RESULTS; i++) {336 if (results[i] != expected_results[i]) {337 broken = i;338 break;339 }340 }341 342 if (i == NR_RESULTS)343 return;344 345 printf("unexpected result\n");346 printf(" result: [");347 printf("%u", results[0]);348 for (i = 1; i < NR_RESULTS; i++)349 printf(", %u", results[i]);350 printf("]\n");351 352 printf("expected: [");353 printf("%u", expected_results[0]);354 for (i = 1; i < NR_RESULTS; i++)355 printf(", %u", expected_results[i]);356 printf("]\n");357 358 printf("mismatch on %s (bpf_prog_linum:%ld)\n", result_to_str(broken),359 get_linum());360 361 CHECK_FAIL(true);362}363 364static int send_data(int type, sa_family_t family, void *data, size_t len,365 enum result expected)366{367 union sa46 cli_sa;368 int fd, err;369 370 fd = socket(family, type, 0);371 RET_ERR(fd == -1, "socket()", "fd:%d errno:%d\n", fd, errno);372 373 sa46_init_loopback(&cli_sa, family);374 err = bind(fd, (struct sockaddr *)&cli_sa, sizeof(cli_sa));375 RET_ERR(fd == -1, "bind(cli_sa)", "err:%d errno:%d\n", err, errno);376 377 err = sendto(fd, data, len, MSG_FASTOPEN, (struct sockaddr *)&srv_sa,378 sizeof(srv_sa));379 RET_ERR(err != len && expected >= PASS,380 "sendto()", "family:%u err:%d errno:%d expected:%d\n",381 family, err, errno, expected);382 383 return fd;384}385 386static void do_test(int type, sa_family_t family, struct cmd *cmd,387 enum result expected)388{389 int nev, srv_fd, cli_fd;390 struct epoll_event ev;391 struct cmd rcv_cmd;392 ssize_t nread;393 394 cli_fd = send_data(type, family, cmd, cmd ? sizeof(*cmd) : 0,395 expected);396 if (cli_fd < 0)397 return;398 nev = epoll_wait(epfd, &ev, 1, expected >= PASS ? 5 : 0);399 RET_IF((nev <= 0 && expected >= PASS) ||400 (nev > 0 && expected < PASS),401 "nev <> expected",402 "nev:%d expected:%d type:%d family:%d data:(%d, %d)\n",403 nev, expected, type, family,404 cmd ? cmd->reuseport_index : -1,405 cmd ? cmd->pass_on_failure : -1);406 check_results();407 check_data(type, family, cmd, cli_fd);408 409 if (expected < PASS)410 return;411 412 RET_IF(expected != PASS_ERR_SK_SELECT_REUSEPORT &&413 cmd->reuseport_index != ev.data.u32,414 "check cmd->reuseport_index",415 "cmd:(%u, %u) ev.data.u32:%u\n",416 cmd->pass_on_failure, cmd->reuseport_index, ev.data.u32);417 418 srv_fd = sk_fds[ev.data.u32];419 if (type == SOCK_STREAM) {420 int new_fd = accept(srv_fd, NULL, 0);421 422 RET_IF(new_fd == -1, "accept(srv_fd)",423 "ev.data.u32:%u new_fd:%d errno:%d\n",424 ev.data.u32, new_fd, errno);425 426 nread = recv(new_fd, &rcv_cmd, sizeof(rcv_cmd), MSG_DONTWAIT);427 RET_IF(nread != sizeof(rcv_cmd),428 "recv(new_fd)",429 "ev.data.u32:%u nread:%zd sizeof(rcv_cmd):%zu errno:%d\n",430 ev.data.u32, nread, sizeof(rcv_cmd), errno);431 432 close(new_fd);433 } else {434 nread = recv(srv_fd, &rcv_cmd, sizeof(rcv_cmd), MSG_DONTWAIT);435 RET_IF(nread != sizeof(rcv_cmd),436 "recv(sk_fds)",437 "ev.data.u32:%u nread:%zd sizeof(rcv_cmd):%zu errno:%d\n",438 ev.data.u32, nread, sizeof(rcv_cmd), errno);439 }440 441 close(cli_fd);442}443 444static void test_err_inner_map(int type, sa_family_t family)445{446 struct cmd cmd = {447 .reuseport_index = 0,448 .pass_on_failure = 0,449 };450 451 expected_results[DROP_ERR_INNER_MAP]++;452 do_test(type, family, &cmd, DROP_ERR_INNER_MAP);453}454 455static void test_err_skb_data(int type, sa_family_t family)456{457 expected_results[DROP_ERR_SKB_DATA]++;458 do_test(type, family, NULL, DROP_ERR_SKB_DATA);459}460 461static void test_err_sk_select_port(int type, sa_family_t family)462{463 struct cmd cmd = {464 .reuseport_index = REUSEPORT_ARRAY_SIZE,465 .pass_on_failure = 0,466 };467 468 expected_results[DROP_ERR_SK_SELECT_REUSEPORT]++;469 do_test(type, family, &cmd, DROP_ERR_SK_SELECT_REUSEPORT);470}471 472static void test_pass(int type, sa_family_t family)473{474 struct cmd cmd;475 int i;476 477 cmd.pass_on_failure = 0;478 for (i = 0; i < REUSEPORT_ARRAY_SIZE; i++) {479 expected_results[PASS]++;480 cmd.reuseport_index = i;481 do_test(type, family, &cmd, PASS);482 }483}484 485static void test_syncookie(int type, sa_family_t family)486{487 int err, tmp_index = 1;488 struct cmd cmd = {489 .reuseport_index = 0,490 .pass_on_failure = 0,491 };492 493 /*494 * +1 for TCP-SYN and495 * +1 for the TCP-ACK (ack the syncookie)496 */497 expected_results[PASS] += 2;498 enable_syncookie();499 /*500 * Simulate TCP-SYN and TCP-ACK are handled by two different sk:501 * TCP-SYN: select sk_fds[tmp_index = 1] tmp_index is from the502 * tmp_index_ovr_map503 * TCP-ACK: select sk_fds[reuseport_index = 0] reuseport_index504 * is from the cmd.reuseport_index505 */506 err = bpf_map_update_elem(tmp_index_ovr_map, &index_zero,507 &tmp_index, BPF_ANY);508 RET_IF(err < 0, "update_elem(tmp_index_ovr_map, 0, 1)",509 "err:%d errno:%d\n", err, errno);510 do_test(type, family, &cmd, PASS);511 err = bpf_map_lookup_elem(tmp_index_ovr_map, &index_zero,512 &tmp_index);513 RET_IF(err < 0 || tmp_index >= 0,514 "lookup_elem(tmp_index_ovr_map)",515 "err:%d errno:%d tmp_index:%d\n",516 err, errno, tmp_index);517 disable_syncookie();518}519 520static void test_pass_on_err(int type, sa_family_t family)521{522 struct cmd cmd = {523 .reuseport_index = REUSEPORT_ARRAY_SIZE,524 .pass_on_failure = 1,525 };526 527 expected_results[PASS_ERR_SK_SELECT_REUSEPORT] += 1;528 do_test(type, family, &cmd, PASS_ERR_SK_SELECT_REUSEPORT);529}530 531static void test_detach_bpf(int type, sa_family_t family)532{533#ifdef SO_DETACH_REUSEPORT_BPF534 __u32 nr_run_before = 0, nr_run_after = 0, tmp, i;535 struct epoll_event ev;536 int cli_fd, err, nev;537 struct cmd cmd = {};538 int optvalue = 0;539 540 err = setsockopt(sk_fds[0], SOL_SOCKET, SO_DETACH_REUSEPORT_BPF,541 &optvalue, sizeof(optvalue));542 RET_IF(err == -1, "setsockopt(SO_DETACH_REUSEPORT_BPF)",543 "err:%d errno:%d\n", err, errno);544 545 err = setsockopt(sk_fds[1], SOL_SOCKET, SO_DETACH_REUSEPORT_BPF,546 &optvalue, sizeof(optvalue));547 RET_IF(err == 0 || errno != ENOENT,548 "setsockopt(SO_DETACH_REUSEPORT_BPF)",549 "err:%d errno:%d\n", err, errno);550 551 for (i = 0; i < NR_RESULTS; i++) {552 err = bpf_map_lookup_elem(result_map, &i, &tmp);553 RET_IF(err < 0, "lookup_elem(result_map)",554 "i:%u err:%d errno:%d\n", i, err, errno);555 nr_run_before += tmp;556 }557 558 cli_fd = send_data(type, family, &cmd, sizeof(cmd), PASS);559 if (cli_fd < 0)560 return;561 nev = epoll_wait(epfd, &ev, 1, 5);562 RET_IF(nev <= 0, "nev <= 0",563 "nev:%d expected:1 type:%d family:%d data:(0, 0)\n",564 nev, type, family);565 566 for (i = 0; i < NR_RESULTS; i++) {567 err = bpf_map_lookup_elem(result_map, &i, &tmp);568 RET_IF(err < 0, "lookup_elem(result_map)",569 "i:%u err:%d errno:%d\n", i, err, errno);570 nr_run_after += tmp;571 }572 573 RET_IF(nr_run_before != nr_run_after,574 "nr_run_before != nr_run_after",575 "nr_run_before:%u nr_run_after:%u\n",576 nr_run_before, nr_run_after);577 578 close(cli_fd);579#else580 test__skip();581#endif582}583 584static void prepare_sk_fds(int type, sa_family_t family, bool inany)585{586 const int first = REUSEPORT_ARRAY_SIZE - 1;587 int i, err, optval = 1;588 struct epoll_event ev;589 socklen_t addrlen;590 591 if (inany)592 sa46_init_inany(&srv_sa, family);593 else594 sa46_init_loopback(&srv_sa, family);595 addrlen = sizeof(srv_sa);596 597 /*598 * The sk_fds[] is filled from the back such that the order599 * is exactly opposite to the (struct sock_reuseport *)reuse->socks[].600 */601 for (i = first; i >= 0; i--) {602 sk_fds[i] = socket(family, type, 0);603 RET_IF(sk_fds[i] == -1, "socket()", "sk_fds[%d]:%d errno:%d\n",604 i, sk_fds[i], errno);605 err = setsockopt(sk_fds[i], SOL_SOCKET, SO_REUSEPORT,606 &optval, sizeof(optval));607 RET_IF(err == -1, "setsockopt(SO_REUSEPORT)",608 "sk_fds[%d] err:%d errno:%d\n",609 i, err, errno);610 611 if (i == first) {612 err = setsockopt(sk_fds[i], SOL_SOCKET,613 SO_ATTACH_REUSEPORT_EBPF,614 &select_by_skb_data_prog,615 sizeof(select_by_skb_data_prog));616 RET_IF(err < 0, "setsockopt(SO_ATTACH_REUEPORT_EBPF)",617 "err:%d errno:%d\n", err, errno);618 }619 620 err = bind(sk_fds[i], (struct sockaddr *)&srv_sa, addrlen);621 RET_IF(err < 0, "bind()", "sk_fds[%d] err:%d errno:%d\n",622 i, err, errno);623 624 if (type == SOCK_STREAM) {625 err = listen(sk_fds[i], 10);626 RET_IF(err < 0, "listen()",627 "sk_fds[%d] err:%d errno:%d\n",628 i, err, errno);629 }630 631 err = bpf_map_update_elem(reuseport_array, &i, &sk_fds[i],632 BPF_NOEXIST);633 RET_IF(err < 0, "update_elem(reuseport_array)",634 "sk_fds[%d] err:%d errno:%d\n", i, err, errno);635 636 if (i == first) {637 socklen_t addrlen = sizeof(srv_sa);638 639 err = getsockname(sk_fds[i], (struct sockaddr *)&srv_sa,640 &addrlen);641 RET_IF(err == -1, "getsockname()",642 "sk_fds[%d] err:%d errno:%d\n", i, err, errno);643 }644 }645 646 epfd = epoll_create(1);647 RET_IF(epfd == -1, "epoll_create(1)",648 "epfd:%d errno:%d\n", epfd, errno);649 650 ev.events = EPOLLIN;651 for (i = 0; i < REUSEPORT_ARRAY_SIZE; i++) {652 ev.data.u32 = i;653 err = epoll_ctl(epfd, EPOLL_CTL_ADD, sk_fds[i], &ev);654 RET_IF(err, "epoll_ctl(EPOLL_CTL_ADD)", "sk_fds[%d]\n", i);655 }656}657 658static void setup_per_test(int type, sa_family_t family, bool inany,659 bool no_inner_map)660{661 int ovr = -1, err;662 663 prepare_sk_fds(type, family, inany);664 err = bpf_map_update_elem(tmp_index_ovr_map, &index_zero, &ovr,665 BPF_ANY);666 RET_IF(err < 0, "update_elem(tmp_index_ovr_map, 0, -1)",667 "err:%d errno:%d\n", err, errno);668 669 /* Install reuseport_array to outer_map? */670 if (no_inner_map)671 return;672 673 err = bpf_map_update_elem(outer_map, &index_zero, &reuseport_array,674 BPF_ANY);675 RET_IF(err < 0, "update_elem(outer_map, 0, reuseport_array)",676 "err:%d errno:%d\n", err, errno);677}678 679static void cleanup_per_test(bool no_inner_map)680{681 int i, err, zero = 0;682 683 memset(expected_results, 0, sizeof(expected_results));684 685 for (i = 0; i < NR_RESULTS; i++) {686 err = bpf_map_update_elem(result_map, &i, &zero, BPF_ANY);687 RET_IF(err, "reset elem in result_map",688 "i:%u err:%d errno:%d\n", i, err, errno);689 }690 691 err = bpf_map_update_elem(linum_map, &zero, &zero, BPF_ANY);692 RET_IF(err, "reset line number in linum_map", "err:%d errno:%d\n",693 err, errno);694 695 for (i = 0; i < REUSEPORT_ARRAY_SIZE; i++)696 close(sk_fds[i]);697 close(epfd);698 699 /* Delete reuseport_array from outer_map? */700 if (no_inner_map)701 return;702 703 err = bpf_map_delete_elem(outer_map, &index_zero);704 RET_IF(err < 0, "delete_elem(outer_map)",705 "err:%d errno:%d\n", err, errno);706}707 708static void cleanup(void)709{710 if (outer_map >= 0) {711 close(outer_map);712 outer_map = -1;713 }714 715 if (reuseport_array >= 0) {716 close(reuseport_array);717 reuseport_array = -1;718 }719 720 if (obj) {721 bpf_object__close(obj);722 obj = NULL;723 }724 725 memset(expected_results, 0, sizeof(expected_results));726}727 728static const char *maptype_str(enum bpf_map_type type)729{730 switch (type) {731 case BPF_MAP_TYPE_REUSEPORT_SOCKARRAY:732 return "reuseport_sockarray";733 case BPF_MAP_TYPE_SOCKMAP:734 return "sockmap";735 case BPF_MAP_TYPE_SOCKHASH:736 return "sockhash";737 default:738 return "unknown";739 }740}741 742static const char *family_str(sa_family_t family)743{744 switch (family) {745 case AF_INET:746 return "IPv4";747 case AF_INET6:748 return "IPv6";749 default:750 return "unknown";751 }752}753 754static const char *sotype_str(int sotype)755{756 switch (sotype) {757 case SOCK_STREAM:758 return "TCP";759 case SOCK_DGRAM:760 return "UDP";761 default:762 return "unknown";763 }764}765 766#define TEST_INIT(fn_, ...) { .fn = fn_, .name = #fn_, __VA_ARGS__ }767 768static void test_config(int sotype, sa_family_t family, bool inany)769{770 const struct test {771 void (*fn)(int sotype, sa_family_t family);772 const char *name;773 bool no_inner_map;774 int need_sotype;775 } tests[] = {776 TEST_INIT(test_err_inner_map,777 .no_inner_map = true),778 TEST_INIT(test_err_skb_data),779 TEST_INIT(test_err_sk_select_port),780 TEST_INIT(test_pass),781 TEST_INIT(test_syncookie,782 .need_sotype = SOCK_STREAM),783 TEST_INIT(test_pass_on_err),784 TEST_INIT(test_detach_bpf),785 };786 struct netns_obj *netns;787 char s[MAX_TEST_NAME];788 const struct test *t;789 790 for (t = tests; t < tests + ARRAY_SIZE(tests); t++) {791 if (t->need_sotype && t->need_sotype != sotype)792 continue; /* test not compatible with socket type */793 794 snprintf(s, sizeof(s), "%s %s/%s %s %s",795 maptype_str(inner_map_type),796 family_str(family), sotype_str(sotype),797 inany ? "INANY" : "LOOPBACK", t->name);798 799 if (!test__start_subtest(s))800 continue;801 802 netns = netns_new("select_reuseport", true);803 if (!ASSERT_OK_PTR(netns, "netns_new"))804 continue;805 806 if (CHECK_FAIL(enable_fastopen()))807 goto out;808 if (CHECK_FAIL(disable_syncookie()))809 goto out;810 811 setup_per_test(sotype, family, inany, t->no_inner_map);812 t->fn(sotype, family);813 cleanup_per_test(t->no_inner_map);814 815out:816 netns_free(netns);817 }818}819 820#define BIND_INANY true821 822static void test_all(void)823{824 const struct config {825 int sotype;826 sa_family_t family;827 bool inany;828 } configs[] = {829 { SOCK_STREAM, AF_INET },830 { SOCK_STREAM, AF_INET, BIND_INANY },831 { SOCK_STREAM, AF_INET6 },832 { SOCK_STREAM, AF_INET6, BIND_INANY },833 { SOCK_DGRAM, AF_INET },834 { SOCK_DGRAM, AF_INET6 },835 };836 const struct config *c;837 838 for (c = configs; c < configs + ARRAY_SIZE(configs); c++)839 test_config(c->sotype, c->family, c->inany);840}841 842void test_map_type(enum bpf_map_type mt)843{844 if (create_maps(mt))845 goto out;846 if (prepare_bpf_obj())847 goto out;848 849 test_all();850out:851 cleanup();852}853 854void serial_test_select_reuseport(void)855{856 test_map_type(BPF_MAP_TYPE_REUSEPORT_SOCKARRAY);857 test_map_type(BPF_MAP_TYPE_SOCKMAP);858 test_map_type(BPF_MAP_TYPE_SOCKHASH);859}860