1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 // Copyright (c) 2020 Cloudflare 3 /* 4 * Test BPF attach point for INET socket lookup (BPF_SK_LOOKUP). 5 * 6 * Tests exercise: 7 * - attaching/detaching/querying programs to BPF_SK_LOOKUP hook, 8 * - redirecting socket lookup to a socket selected by BPF program, 9 * - failing a socket lookup on BPF program's request, 10 * - error scenarios for selecting a socket from BPF program, 11 * - accessing BPF program context, 12 * - attaching and running multiple BPF programs. 13 * 14 * Tests run in a dedicated network namespace. 15 */ 16 17 #define _GNU_SOURCE 18 #include <arpa/inet.h> 19 #include <assert.h> 20 #include <errno.h> 21 #include <error.h> 22 #include <fcntl.h> 23 #include <sched.h> 24 #include <stdio.h> 25 #include <sys/types.h> 26 #include <sys/stat.h> 27 #include <unistd.h> 28 29 #include <bpf/libbpf.h> 30 #include <bpf/bpf.h> 31 32 #include "test_progs.h" 33 #include "bpf_util.h" 34 #include "cgroup_helpers.h" 35 #include "network_helpers.h" 36 #include "testing_helpers.h" 37 #include "test_sk_lookup.skel.h" 38 39 /* External (address, port) pairs the client sends packets to. */ 40 #define EXT_IP4 "127.0.0.1" 41 #define EXT_IP6 "fd00::1" 42 #define EXT_PORT 7007 43 44 /* Internal (address, port) pairs the server listens/receives at. */ 45 #define INT_IP4 "127.0.0.2" 46 #define INT_IP4_V6 "::ffff:127.0.0.2" 47 #define INT_IP6 "fd00::2" 48 #define INT_PORT 8008 49 50 #define IO_TIMEOUT_SEC 3 51 52 enum server { 53 SERVER_A = 0, 54 SERVER_B = 1, 55 MAX_SERVERS, 56 }; 57 58 enum { 59 PROG1 = 0, 60 PROG2, 61 }; 62 63 struct inet_addr { 64 const char *ip; 65 unsigned short port; 66 }; 67 68 struct test { 69 const char *desc; 70 struct bpf_program *lookup_prog; 71 struct bpf_program *reuseport_prog; 72 struct bpf_map *sock_map; 73 int sotype; 74 struct inet_addr connect_to; 75 struct inet_addr listen_at; 76 enum server accept_on; 77 bool reuseport_has_conns; /* Add a connected socket to reuseport group */ 78 }; 79 80 struct cb_opts { 81 int family; 82 int sotype; 83 bool reuseport; 84 }; 85 86 static __u32 duration; /* for CHECK macro */ 87 88 static bool is_ipv6(const char *ip) 89 { 90 return !!strchr(ip, ':'); 91 } 92 93 static int attach_reuseport(int sock_fd, struct bpf_program *reuseport_prog) 94 { 95 int err, prog_fd; 96 97 prog_fd = bpf_program__fd(reuseport_prog); 98 if (prog_fd < 0) { 99 errno = -prog_fd; 100 return -1; 101 } 102 103 err = setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF, 104 &prog_fd, sizeof(prog_fd)); 105 if (err) 106 return -1; 107 108 return 0; 109 } 110 111 static socklen_t inetaddr_len(const struct sockaddr_storage *addr) 112 { 113 return (addr->ss_family == AF_INET ? sizeof(struct sockaddr_in) : 114 addr->ss_family == AF_INET6 ? sizeof(struct sockaddr_in6) : 0); 115 } 116 117 static int make_socket(int sotype, const char *ip, int port, 118 struct sockaddr_storage *addr) 119 { 120 struct timeval timeo = { .tv_sec = IO_TIMEOUT_SEC }; 121 int err, family, fd; 122 123 family = is_ipv6(ip) ? AF_INET6 : AF_INET; 124 err = make_sockaddr(family, ip, port, addr, NULL); 125 if (CHECK(err, "make_address", "failed\n")) 126 return -1; 127 128 fd = socket(addr->ss_family, sotype, 0); 129 if (CHECK(fd < 0, "socket", "failed\n")) { 130 log_err("failed to make socket"); 131 return -1; 132 } 133 134 err = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo)); 135 if (CHECK(err, "setsockopt(SO_SNDTIMEO)", "failed\n")) { 136 log_err("failed to set SNDTIMEO"); 137 close(fd); 138 return -1; 139 } 140 141 err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo)); 142 if (CHECK(err, "setsockopt(SO_RCVTIMEO)", "failed\n")) { 143 log_err("failed to set RCVTIMEO"); 144 close(fd); 145 return -1; 146 } 147 148 return fd; 149 } 150 151 static int setsockopts(int fd, void *opts) 152 { 153 struct cb_opts *co = (struct cb_opts *)opts; 154 const int one = 1; 155 int err = 0; 156 157 /* Enabled for UDPv6 sockets for IPv4-mapped IPv6 to work. */ 158 if (co->sotype == SOCK_DGRAM) { 159 err = setsockopt(fd, SOL_IP, IP_RECVORIGDSTADDR, &one, 160 sizeof(one)); 161 if (CHECK(err, "setsockopt(IP_RECVORIGDSTADDR)", "failed\n")) { 162 log_err("failed to enable IP_RECVORIGDSTADDR"); 163 goto fail; 164 } 165 } 166 167 if (co->sotype == SOCK_DGRAM && co->family == AF_INET6) { 168 err = setsockopt(fd, SOL_IPV6, IPV6_RECVORIGDSTADDR, &one, 169 sizeof(one)); 170 if (CHECK(err, "setsockopt(IPV6_RECVORIGDSTADDR)", "failed\n")) { 171 log_err("failed to enable IPV6_RECVORIGDSTADDR"); 172 goto fail; 173 } 174 } 175 176 if (co->sotype == SOCK_STREAM) { 177 err = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, 178 sizeof(one)); 179 if (CHECK(err, "setsockopt(SO_REUSEADDR)", "failed\n")) { 180 log_err("failed to enable SO_REUSEADDR"); 181 goto fail; 182 } 183 } 184 185 if (co->reuseport) { 186 err = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, 187 sizeof(one)); 188 if (CHECK(err, "setsockopt(SO_REUSEPORT)", "failed\n")) { 189 log_err("failed to enable SO_REUSEPORT"); 190 goto fail; 191 } 192 } 193 194 fail: 195 return err; 196 } 197 198 static int make_server(int sotype, const char *ip, int port, 199 struct bpf_program *reuseport_prog) 200 { 201 struct cb_opts cb_opts = { 202 .family = is_ipv6(ip) ? AF_INET6 : AF_INET, 203 .sotype = sotype, 204 .reuseport = reuseport_prog, 205 }; 206 struct network_helper_opts opts = { 207 .backlog = SOMAXCONN, 208 .post_socket_cb = setsockopts, 209 .cb_opts = &cb_opts, 210 }; 211 int err, fd; 212 213 fd = start_server_str(cb_opts.family, sotype, ip, port, &opts); 214 if (!ASSERT_OK_FD(fd, "start_server_str")) 215 return -1; 216 217 /* Late attach reuseport prog so we can have one init path */ 218 if (reuseport_prog) { 219 err = attach_reuseport(fd, reuseport_prog); 220 if (CHECK(err, "attach_reuseport", "failed\n")) { 221 log_err("failed to attach reuseport prog"); 222 goto fail; 223 } 224 } 225 226 return fd; 227 fail: 228 close(fd); 229 return -1; 230 } 231 232 static int make_client(int sotype, const char *ip, int port) 233 { 234 struct sockaddr_storage addr = {0}; 235 int err, fd; 236 237 fd = make_socket(sotype, ip, port, &addr); 238 if (fd < 0) 239 return -1; 240 241 err = connect(fd, (void *)&addr, inetaddr_len(&addr)); 242 if (CHECK(err, "make_client", "connect")) { 243 log_err("failed to connect client socket"); 244 goto fail; 245 } 246 247 return fd; 248 fail: 249 close(fd); 250 return -1; 251 } 252 253 static __u64 socket_cookie(int fd) 254 { 255 __u64 cookie; 256 socklen_t cookie_len = sizeof(cookie); 257 258 if (CHECK(getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len) < 0, 259 "getsockopt(SO_COOKIE)", "%s\n", strerror(errno))) 260 return 0; 261 return cookie; 262 } 263 264 static int fill_sk_lookup_ctx(struct bpf_sk_lookup *ctx, const char *local_ip, __u16 local_port, 265 const char *remote_ip, __u16 remote_port) 266 { 267 void *local, *remote; 268 int err; 269 270 memset(ctx, 0, sizeof(*ctx)); 271 ctx->local_port = local_port; 272 ctx->remote_port = htons(remote_port); 273 274 if (is_ipv6(local_ip)) { 275 ctx->family = AF_INET6; 276 local = &ctx->local_ip6[0]; 277 remote = &ctx->remote_ip6[0]; 278 } else { 279 ctx->family = AF_INET; 280 local = &ctx->local_ip4; 281 remote = &ctx->remote_ip4; 282 } 283 284 err = inet_pton(ctx->family, local_ip, local); 285 if (CHECK(err != 1, "inet_pton", "local_ip failed\n")) 286 return 1; 287 288 err = inet_pton(ctx->family, remote_ip, remote); 289 if (CHECK(err != 1, "inet_pton", "remote_ip failed\n")) 290 return 1; 291 292 return 0; 293 } 294 295 static int send_byte(int fd) 296 { 297 ssize_t n; 298 299 errno = 0; 300 n = send(fd, "a", 1, 0); 301 if (CHECK(n <= 0, "send_byte", "send")) { 302 log_err("failed/partial send"); 303 return -1; 304 } 305 return 0; 306 } 307 308 static int recv_byte(int fd) 309 { 310 char buf[1]; 311 ssize_t n; 312 313 n = recv(fd, buf, sizeof(buf), 0); 314 if (CHECK(n <= 0, "recv_byte", "recv")) { 315 log_err("failed/partial recv"); 316 return -1; 317 } 318 return 0; 319 } 320 321 static int tcp_recv_send(int server_fd) 322 { 323 char buf[1]; 324 int ret, fd; 325 ssize_t n; 326 327 fd = accept(server_fd, NULL, NULL); 328 if (CHECK(fd < 0, "accept", "failed\n")) { 329 log_err("failed to accept"); 330 return -1; 331 } 332 333 n = recv(fd, buf, sizeof(buf), 0); 334 if (CHECK(n <= 0, "recv", "failed\n")) { 335 log_err("failed/partial recv"); 336 ret = -1; 337 goto close; 338 } 339 340 n = send(fd, buf, n, 0); 341 if (CHECK(n <= 0, "send", "failed\n")) { 342 log_err("failed/partial send"); 343 ret = -1; 344 goto close; 345 } 346 347 ret = 0; 348 close: 349 close(fd); 350 return ret; 351 } 352 353 static void v4_to_v6(struct sockaddr_storage *ss) 354 { 355 struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)ss; 356 struct sockaddr_in v4 = *(struct sockaddr_in *)ss; 357 358 v6->sin6_family = AF_INET6; 359 v6->sin6_port = v4.sin_port; 360 v6->sin6_addr.s6_addr[10] = 0xff; 361 v6->sin6_addr.s6_addr[11] = 0xff; 362 memcpy(&v6->sin6_addr.s6_addr[12], &v4.sin_addr.s_addr, 4); 363 memset(&v6->sin6_addr.s6_addr[0], 0, 10); 364 } 365 366 static int udp_recv_send(int server_fd) 367 { 368 char cmsg_buf[CMSG_SPACE(sizeof(struct sockaddr_storage))]; 369 struct sockaddr_storage _src_addr = { 0 }; 370 struct sockaddr_storage *src_addr = &_src_addr; 371 struct sockaddr_storage *dst_addr = NULL; 372 struct msghdr msg = { 0 }; 373 struct iovec iov = { 0 }; 374 struct cmsghdr *cm; 375 char buf[1]; 376 int ret, fd; 377 ssize_t n; 378 379 iov.iov_base = buf; 380 iov.iov_len = sizeof(buf); 381 382 msg.msg_name = src_addr; 383 msg.msg_namelen = sizeof(*src_addr); 384 msg.msg_iov = &iov; 385 msg.msg_iovlen = 1; 386 msg.msg_control = cmsg_buf; 387 msg.msg_controllen = sizeof(cmsg_buf); 388 389 errno = 0; 390 n = recvmsg(server_fd, &msg, 0); 391 if (CHECK(n <= 0, "recvmsg", "failed\n")) { 392 log_err("failed to receive"); 393 return -1; 394 } 395 if (CHECK(msg.msg_flags & MSG_CTRUNC, "recvmsg", "truncated cmsg\n")) 396 return -1; 397 398 for (cm = CMSG_FIRSTHDR(&msg); cm; cm = CMSG_NXTHDR(&msg, cm)) { 399 if ((cm->cmsg_level == SOL_IP && 400 cm->cmsg_type == IP_ORIGDSTADDR) || 401 (cm->cmsg_level == SOL_IPV6 && 402 cm->cmsg_type == IPV6_ORIGDSTADDR)) { 403 dst_addr = (struct sockaddr_storage *)CMSG_DATA(cm); 404 break; 405 } 406 log_err("warning: ignored cmsg at level %d type %d", 407 cm->cmsg_level, cm->cmsg_type); 408 } 409 if (CHECK(!dst_addr, "recvmsg", "missing ORIGDSTADDR\n")) 410 return -1; 411 412 /* Server socket bound to IPv4-mapped IPv6 address */ 413 if (src_addr->ss_family == AF_INET6 && 414 dst_addr->ss_family == AF_INET) { 415 v4_to_v6(dst_addr); 416 } 417 418 /* Reply from original destination address. */ 419 fd = start_server_addr(SOCK_DGRAM, dst_addr, sizeof(*dst_addr), NULL); 420 if (!ASSERT_OK_FD(fd, "start_server_addr")) { 421 log_err("failed to create tx socket"); 422 return -1; 423 } 424 425 msg.msg_control = NULL; 426 msg.msg_controllen = 0; 427 n = sendmsg(fd, &msg, 0); 428 if (CHECK(n <= 0, "sendmsg", "failed\n")) { 429 log_err("failed to send echo reply"); 430 ret = -1; 431 goto out; 432 } 433 434 ret = 0; 435 out: 436 close(fd); 437 return ret; 438 } 439 440 static int tcp_echo_test(int client_fd, int server_fd) 441 { 442 int err; 443 444 err = send_byte(client_fd); 445 if (err) 446 return -1; 447 err = tcp_recv_send(server_fd); 448 if (err) 449 return -1; 450 err = recv_byte(client_fd); 451 if (err) 452 return -1; 453 454 return 0; 455 } 456 457 static int udp_echo_test(int client_fd, int server_fd) 458 { 459 int err; 460 461 err = send_byte(client_fd); 462 if (err) 463 return -1; 464 err = udp_recv_send(server_fd); 465 if (err) 466 return -1; 467 err = recv_byte(client_fd); 468 if (err) 469 return -1; 470 471 return 0; 472 } 473 474 static struct bpf_link *attach_lookup_prog(struct bpf_program *prog) 475 { 476 struct bpf_link *link; 477 int net_fd; 478 479 net_fd = open("/proc/self/ns/net", O_RDONLY); 480 if (CHECK(net_fd < 0, "open", "failed\n")) { 481 log_err("failed to open /proc/self/ns/net"); 482 return NULL; 483 } 484 485 link = bpf_program__attach_netns(prog, net_fd); 486 if (!ASSERT_OK_PTR(link, "bpf_program__attach_netns")) { 487 errno = -PTR_ERR(link); 488 log_err("failed to attach program '%s' to netns", 489 bpf_program__name(prog)); 490 link = NULL; 491 } 492 493 close(net_fd); 494 return link; 495 } 496 497 static int update_lookup_map(struct bpf_map *map, int index, int sock_fd) 498 { 499 int err, map_fd; 500 uint64_t value; 501 502 map_fd = bpf_map__fd(map); 503 if (CHECK(map_fd < 0, "bpf_map__fd", "failed\n")) { 504 errno = -map_fd; 505 log_err("failed to get map FD"); 506 return -1; 507 } 508 509 value = (uint64_t)sock_fd; 510 err = bpf_map_update_elem(map_fd, &index, &value, BPF_NOEXIST); 511 if (CHECK(err, "bpf_map_update_elem", "failed\n")) { 512 log_err("failed to update redir_map @ %d", index); 513 return -1; 514 } 515 516 return 0; 517 } 518 519 static void query_lookup_prog(struct test_sk_lookup *skel) 520 { 521 struct bpf_link *link[3] = {}; 522 struct bpf_link_info info; 523 __u32 attach_flags = 0; 524 __u32 prog_ids[3] = {}; 525 __u32 prog_cnt = 3; 526 __u32 prog_id; 527 int net_fd; 528 int err; 529 530 net_fd = open("/proc/self/ns/net", O_RDONLY); 531 if (CHECK(net_fd < 0, "open", "failed\n")) { 532 log_err("failed to open /proc/self/ns/net"); 533 return; 534 } 535 536 link[0] = attach_lookup_prog(skel->progs.lookup_pass); 537 if (!link[0]) 538 goto close; 539 link[1] = attach_lookup_prog(skel->progs.lookup_pass); 540 if (!link[1]) 541 goto detach; 542 link[2] = attach_lookup_prog(skel->progs.lookup_drop); 543 if (!link[2]) 544 goto detach; 545 546 err = bpf_prog_query(net_fd, BPF_SK_LOOKUP, 0 /* query flags */, 547 &attach_flags, prog_ids, &prog_cnt); 548 if (CHECK(err, "bpf_prog_query", "failed\n")) { 549 log_err("failed to query lookup prog"); 550 goto detach; 551 } 552 553 errno = 0; 554 if (CHECK(attach_flags != 0, "bpf_prog_query", 555 "wrong attach_flags on query: %u", attach_flags)) 556 goto detach; 557 if (CHECK(prog_cnt != 3, "bpf_prog_query", 558 "wrong program count on query: %u", prog_cnt)) 559 goto detach; 560 prog_id = link_info_prog_id(link[0], &info); 561 CHECK(prog_ids[0] != prog_id, "bpf_prog_query", 562 "invalid program #0 id on query: %u != %u\n", 563 prog_ids[0], prog_id); 564 CHECK(info.netns.netns_ino == 0, "netns_ino", 565 "unexpected netns_ino: %u\n", info.netns.netns_ino); 566 prog_id = link_info_prog_id(link[1], &info); 567 CHECK(prog_ids[1] != prog_id, "bpf_prog_query", 568 "invalid program #1 id on query: %u != %u\n", 569 prog_ids[1], prog_id); 570 CHECK(info.netns.netns_ino == 0, "netns_ino", 571 "unexpected netns_ino: %u\n", info.netns.netns_ino); 572 prog_id = link_info_prog_id(link[2], &info); 573 CHECK(prog_ids[2] != prog_id, "bpf_prog_query", 574 "invalid program #2 id on query: %u != %u\n", 575 prog_ids[2], prog_id); 576 CHECK(info.netns.netns_ino == 0, "netns_ino", 577 "unexpected netns_ino: %u\n", info.netns.netns_ino); 578 579 err = bpf_link__detach(link[0]); 580 if (CHECK(err, "link_detach", "failed %d\n", err)) 581 goto detach; 582 583 /* prog id is still there, but netns_ino is zeroed out */ 584 prog_id = link_info_prog_id(link[0], &info); 585 CHECK(prog_ids[0] != prog_id, "bpf_prog_query", 586 "invalid program #0 id on query: %u != %u\n", 587 prog_ids[0], prog_id); 588 CHECK(info.netns.netns_ino != 0, "netns_ino", 589 "unexpected netns_ino: %u\n", info.netns.netns_ino); 590 591 detach: 592 if (link[2]) 593 bpf_link__destroy(link[2]); 594 if (link[1]) 595 bpf_link__destroy(link[1]); 596 if (link[0]) 597 bpf_link__destroy(link[0]); 598 close: 599 close(net_fd); 600 } 601 602 static void run_lookup_prog(const struct test *t) 603 { 604 int server_fds[] = { [0 ... MAX_SERVERS - 1] = -1 }; 605 int client_fd, reuse_conn_fd = -1; 606 struct bpf_link *lookup_link; 607 int i, err; 608 609 lookup_link = attach_lookup_prog(t->lookup_prog); 610 if (!lookup_link) 611 return; 612 613 for (i = 0; i < ARRAY_SIZE(server_fds); i++) { 614 server_fds[i] = make_server(t->sotype, t->listen_at.ip, 615 t->listen_at.port, 616 t->reuseport_prog); 617 if (server_fds[i] < 0) 618 goto close; 619 620 err = update_lookup_map(t->sock_map, i, server_fds[i]); 621 if (err) 622 goto close; 623 624 /* want just one server for non-reuseport test */ 625 if (!t->reuseport_prog) 626 break; 627 } 628 629 /* Regular UDP socket lookup with reuseport behaves 630 * differently when reuseport group contains connected 631 * sockets. Check that adding a connected UDP socket to the 632 * reuseport group does not affect how reuseport works with 633 * BPF socket lookup. 634 */ 635 if (t->reuseport_has_conns) { 636 /* Add an extra socket to reuseport group */ 637 reuse_conn_fd = make_server(t->sotype, t->listen_at.ip, 638 t->listen_at.port, 639 t->reuseport_prog); 640 if (reuse_conn_fd < 0) 641 goto close; 642 643 /* Connect the extra socket to itself */ 644 err = connect_fd_to_fd(reuse_conn_fd, reuse_conn_fd, 0); 645 if (!ASSERT_OK(err, "connect_fd_to_fd")) 646 goto close; 647 } 648 649 client_fd = make_client(t->sotype, t->connect_to.ip, t->connect_to.port); 650 if (client_fd < 0) 651 goto close; 652 653 if (t->sotype == SOCK_STREAM) 654 tcp_echo_test(client_fd, server_fds[t->accept_on]); 655 else 656 udp_echo_test(client_fd, server_fds[t->accept_on]); 657 658 close(client_fd); 659 close: 660 if (reuse_conn_fd != -1) 661 close(reuse_conn_fd); 662 for (i = 0; i < ARRAY_SIZE(server_fds); i++) { 663 if (server_fds[i] != -1) 664 close(server_fds[i]); 665 } 666 bpf_link__destroy(lookup_link); 667 } 668 669 static void test_redirect_lookup(struct test_sk_lookup *skel) 670 { 671 const struct test tests[] = { 672 { 673 .desc = "TCP IPv4 redir port", 674 .lookup_prog = skel->progs.redir_port, 675 .sock_map = skel->maps.redir_map, 676 .sotype = SOCK_STREAM, 677 .connect_to = { EXT_IP4, EXT_PORT }, 678 .listen_at = { EXT_IP4, INT_PORT }, 679 }, 680 { 681 .desc = "TCP IPv4 redir addr", 682 .lookup_prog = skel->progs.redir_ip4, 683 .sock_map = skel->maps.redir_map, 684 .sotype = SOCK_STREAM, 685 .connect_to = { EXT_IP4, EXT_PORT }, 686 .listen_at = { INT_IP4, EXT_PORT }, 687 }, 688 { 689 .desc = "TCP IPv4 redir with reuseport", 690 .lookup_prog = skel->progs.select_sock_a, 691 .reuseport_prog = skel->progs.select_sock_b, 692 .sock_map = skel->maps.redir_map, 693 .sotype = SOCK_STREAM, 694 .connect_to = { EXT_IP4, EXT_PORT }, 695 .listen_at = { INT_IP4, INT_PORT }, 696 .accept_on = SERVER_B, 697 }, 698 { 699 .desc = "TCP IPv4 redir skip reuseport", 700 .lookup_prog = skel->progs.select_sock_a_no_reuseport, 701 .reuseport_prog = skel->progs.select_sock_b, 702 .sock_map = skel->maps.redir_map, 703 .sotype = SOCK_STREAM, 704 .connect_to = { EXT_IP4, EXT_PORT }, 705 .listen_at = { INT_IP4, INT_PORT }, 706 .accept_on = SERVER_A, 707 }, 708 { 709 .desc = "TCP IPv6 redir port", 710 .lookup_prog = skel->progs.redir_port, 711 .sock_map = skel->maps.redir_map, 712 .sotype = SOCK_STREAM, 713 .connect_to = { EXT_IP6, EXT_PORT }, 714 .listen_at = { EXT_IP6, INT_PORT }, 715 }, 716 { 717 .desc = "TCP IPv6 redir addr", 718 .lookup_prog = skel->progs.redir_ip6, 719 .sock_map = skel->maps.redir_map, 720 .sotype = SOCK_STREAM, 721 .connect_to = { EXT_IP6, EXT_PORT }, 722 .listen_at = { INT_IP6, EXT_PORT }, 723 }, 724 { 725 .desc = "TCP IPv4->IPv6 redir port", 726 .lookup_prog = skel->progs.redir_port, 727 .sock_map = skel->maps.redir_map, 728 .sotype = SOCK_STREAM, 729 .connect_to = { EXT_IP4, EXT_PORT }, 730 .listen_at = { INT_IP4_V6, INT_PORT }, 731 }, 732 { 733 .desc = "TCP IPv6 redir with reuseport", 734 .lookup_prog = skel->progs.select_sock_a, 735 .reuseport_prog = skel->progs.select_sock_b, 736 .sock_map = skel->maps.redir_map, 737 .sotype = SOCK_STREAM, 738 .connect_to = { EXT_IP6, EXT_PORT }, 739 .listen_at = { INT_IP6, INT_PORT }, 740 .accept_on = SERVER_B, 741 }, 742 { 743 .desc = "TCP IPv6 redir skip reuseport", 744 .lookup_prog = skel->progs.select_sock_a_no_reuseport, 745 .reuseport_prog = skel->progs.select_sock_b, 746 .sock_map = skel->maps.redir_map, 747 .sotype = SOCK_STREAM, 748 .connect_to = { EXT_IP6, EXT_PORT }, 749 .listen_at = { INT_IP6, INT_PORT }, 750 .accept_on = SERVER_A, 751 }, 752 { 753 .desc = "UDP IPv4 redir port", 754 .lookup_prog = skel->progs.redir_port, 755 .sock_map = skel->maps.redir_map, 756 .sotype = SOCK_DGRAM, 757 .connect_to = { EXT_IP4, EXT_PORT }, 758 .listen_at = { EXT_IP4, INT_PORT }, 759 }, 760 { 761 .desc = "UDP IPv4 redir addr", 762 .lookup_prog = skel->progs.redir_ip4, 763 .sock_map = skel->maps.redir_map, 764 .sotype = SOCK_DGRAM, 765 .connect_to = { EXT_IP4, EXT_PORT }, 766 .listen_at = { INT_IP4, EXT_PORT }, 767 }, 768 { 769 .desc = "UDP IPv4 redir with reuseport", 770 .lookup_prog = skel->progs.select_sock_a, 771 .reuseport_prog = skel->progs.select_sock_b, 772 .sock_map = skel->maps.redir_map, 773 .sotype = SOCK_DGRAM, 774 .connect_to = { EXT_IP4, EXT_PORT }, 775 .listen_at = { INT_IP4, INT_PORT }, 776 .accept_on = SERVER_B, 777 }, 778 { 779 .desc = "UDP IPv4 redir and reuseport with conns", 780 .lookup_prog = skel->progs.select_sock_a, 781 .reuseport_prog = skel->progs.select_sock_b, 782 .sock_map = skel->maps.redir_map, 783 .sotype = SOCK_DGRAM, 784 .connect_to = { EXT_IP4, EXT_PORT }, 785 .listen_at = { INT_IP4, INT_PORT }, 786 .accept_on = SERVER_B, 787 .reuseport_has_conns = true, 788 }, 789 { 790 .desc = "UDP IPv4 redir skip reuseport", 791 .lookup_prog = skel->progs.select_sock_a_no_reuseport, 792 .reuseport_prog = skel->progs.select_sock_b, 793 .sock_map = skel->maps.redir_map, 794 .sotype = SOCK_DGRAM, 795 .connect_to = { EXT_IP4, EXT_PORT }, 796 .listen_at = { INT_IP4, INT_PORT }, 797 .accept_on = SERVER_A, 798 }, 799 { 800 .desc = "UDP IPv6 redir port", 801 .lookup_prog = skel->progs.redir_port, 802 .sock_map = skel->maps.redir_map, 803 .sotype = SOCK_DGRAM, 804 .connect_to = { EXT_IP6, EXT_PORT }, 805 .listen_at = { EXT_IP6, INT_PORT }, 806 }, 807 { 808 .desc = "UDP IPv6 redir addr", 809 .lookup_prog = skel->progs.redir_ip6, 810 .sock_map = skel->maps.redir_map, 811 .sotype = SOCK_DGRAM, 812 .connect_to = { EXT_IP6, EXT_PORT }, 813 .listen_at = { INT_IP6, EXT_PORT }, 814 }, 815 { 816 .desc = "UDP IPv4->IPv6 redir port", 817 .lookup_prog = skel->progs.redir_port, 818 .sock_map = skel->maps.redir_map, 819 .sotype = SOCK_DGRAM, 820 .listen_at = { INT_IP4_V6, INT_PORT }, 821 .connect_to = { EXT_IP4, EXT_PORT }, 822 }, 823 { 824 .desc = "UDP IPv6 redir and reuseport", 825 .lookup_prog = skel->progs.select_sock_a, 826 .reuseport_prog = skel->progs.select_sock_b, 827 .sock_map = skel->maps.redir_map, 828 .sotype = SOCK_DGRAM, 829 .connect_to = { EXT_IP6, EXT_PORT }, 830 .listen_at = { INT_IP6, INT_PORT }, 831 .accept_on = SERVER_B, 832 }, 833 { 834 .desc = "UDP IPv6 redir and reuseport with conns", 835 .lookup_prog = skel->progs.select_sock_a, 836 .reuseport_prog = skel->progs.select_sock_b, 837 .sock_map = skel->maps.redir_map, 838 .sotype = SOCK_DGRAM, 839 .connect_to = { EXT_IP6, EXT_PORT }, 840 .listen_at = { INT_IP6, INT_PORT }, 841 .accept_on = SERVER_B, 842 .reuseport_has_conns = true, 843 }, 844 { 845 .desc = "UDP IPv6 redir skip reuseport", 846 .lookup_prog = skel->progs.select_sock_a_no_reuseport, 847 .reuseport_prog = skel->progs.select_sock_b, 848 .sock_map = skel->maps.redir_map, 849 .sotype = SOCK_DGRAM, 850 .connect_to = { EXT_IP6, EXT_PORT }, 851 .listen_at = { INT_IP6, INT_PORT }, 852 .accept_on = SERVER_A, 853 }, 854 }; 855 const struct test *t; 856 857 for (t = tests; t < tests + ARRAY_SIZE(tests); t++) { 858 if (test__start_subtest(t->desc)) 859 run_lookup_prog(t); 860 } 861 } 862 863 static void drop_on_lookup(const struct test *t) 864 { 865 struct sockaddr_storage dst = {}; 866 int client_fd, server_fd, err; 867 struct bpf_link *lookup_link; 868 ssize_t n; 869 870 lookup_link = attach_lookup_prog(t->lookup_prog); 871 if (!lookup_link) 872 return; 873 874 server_fd = make_server(t->sotype, t->listen_at.ip, t->listen_at.port, 875 t->reuseport_prog); 876 if (server_fd < 0) 877 goto detach; 878 879 client_fd = make_socket(t->sotype, t->connect_to.ip, 880 t->connect_to.port, &dst); 881 if (client_fd < 0) 882 goto close_srv; 883 884 err = connect(client_fd, (void *)&dst, inetaddr_len(&dst)); 885 if (t->sotype == SOCK_DGRAM) { 886 err = send_byte(client_fd); 887 if (err) 888 goto close_all; 889 890 /* Read out asynchronous error */ 891 n = recv(client_fd, NULL, 0, 0); 892 err = n == -1; 893 } 894 if (CHECK(!err || errno != ECONNREFUSED, "connect", 895 "unexpected success or error\n")) 896 log_err("expected ECONNREFUSED on connect"); 897 898 close_all: 899 close(client_fd); 900 close_srv: 901 close(server_fd); 902 detach: 903 bpf_link__destroy(lookup_link); 904 } 905 906 static void test_drop_on_lookup(struct test_sk_lookup *skel) 907 { 908 const struct test tests[] = { 909 { 910 .desc = "TCP IPv4 drop on lookup", 911 .lookup_prog = skel->progs.lookup_drop, 912 .sotype = SOCK_STREAM, 913 .connect_to = { EXT_IP4, EXT_PORT }, 914 .listen_at = { EXT_IP4, EXT_PORT }, 915 }, 916 { 917 .desc = "TCP IPv6 drop on lookup", 918 .lookup_prog = skel->progs.lookup_drop, 919 .sotype = SOCK_STREAM, 920 .connect_to = { EXT_IP6, EXT_PORT }, 921 .listen_at = { EXT_IP6, EXT_PORT }, 922 }, 923 { 924 .desc = "UDP IPv4 drop on lookup", 925 .lookup_prog = skel->progs.lookup_drop, 926 .sotype = SOCK_DGRAM, 927 .connect_to = { EXT_IP4, EXT_PORT }, 928 .listen_at = { EXT_IP4, EXT_PORT }, 929 }, 930 { 931 .desc = "UDP IPv6 drop on lookup", 932 .lookup_prog = skel->progs.lookup_drop, 933 .sotype = SOCK_DGRAM, 934 .connect_to = { EXT_IP6, EXT_PORT }, 935 .listen_at = { EXT_IP6, INT_PORT }, 936 }, 937 /* The program will drop on success, meaning that the ifindex 938 * was 1. 939 */ 940 { 941 .desc = "TCP IPv4 drop on valid ifindex", 942 .lookup_prog = skel->progs.check_ifindex, 943 .sotype = SOCK_STREAM, 944 .connect_to = { EXT_IP4, EXT_PORT }, 945 .listen_at = { EXT_IP4, EXT_PORT }, 946 }, 947 { 948 .desc = "TCP IPv6 drop on valid ifindex", 949 .lookup_prog = skel->progs.check_ifindex, 950 .sotype = SOCK_STREAM, 951 .connect_to = { EXT_IP6, EXT_PORT }, 952 .listen_at = { EXT_IP6, EXT_PORT }, 953 }, 954 { 955 .desc = "UDP IPv4 drop on valid ifindex", 956 .lookup_prog = skel->progs.check_ifindex, 957 .sotype = SOCK_DGRAM, 958 .connect_to = { EXT_IP4, EXT_PORT }, 959 .listen_at = { EXT_IP4, EXT_PORT }, 960 }, 961 { 962 .desc = "UDP IPv6 drop on valid ifindex", 963 .lookup_prog = skel->progs.check_ifindex, 964 .sotype = SOCK_DGRAM, 965 .connect_to = { EXT_IP6, EXT_PORT }, 966 .listen_at = { EXT_IP6, EXT_PORT }, 967 }, 968 }; 969 const struct test *t; 970 971 for (t = tests; t < tests + ARRAY_SIZE(tests); t++) { 972 if (test__start_subtest(t->desc)) 973 drop_on_lookup(t); 974 } 975 } 976 977 static void drop_on_reuseport(const struct test *t) 978 { 979 struct sockaddr_storage dst = { 0 }; 980 int client, server1, server2, err; 981 struct bpf_link *lookup_link; 982 ssize_t n; 983 984 lookup_link = attach_lookup_prog(t->lookup_prog); 985 if (!lookup_link) 986 return; 987 988 server1 = make_server(t->sotype, t->listen_at.ip, t->listen_at.port, 989 t->reuseport_prog); 990 if (server1 < 0) 991 goto detach; 992 993 err = update_lookup_map(t->sock_map, SERVER_A, server1); 994 if (err) 995 goto close_srv1; 996 997 /* second server on destination address we should never reach */ 998 server2 = make_server(t->sotype, t->connect_to.ip, t->connect_to.port, 999 NULL /* reuseport prog */); 1000 if (server2 < 0) 1001 goto close_srv1; 1002 1003 client = make_socket(t->sotype, t->connect_to.ip, 1004 t->connect_to.port, &dst); 1005 if (client < 0) 1006 goto close_srv2; 1007 1008 err = connect(client, (void *)&dst, inetaddr_len(&dst)); 1009 if (t->sotype == SOCK_DGRAM) { 1010 err = send_byte(client); 1011 if (err) 1012 goto close_all; 1013 1014 /* Read out asynchronous error */ 1015 n = recv(client, NULL, 0, 0); 1016 err = n == -1; 1017 } 1018 if (CHECK(!err || errno != ECONNREFUSED, "connect", 1019 "unexpected success or error\n")) 1020 log_err("expected ECONNREFUSED on connect"); 1021 1022 close_all: 1023 close(client); 1024 close_srv2: 1025 close(server2); 1026 close_srv1: 1027 close(server1); 1028 detach: 1029 bpf_link__destroy(lookup_link); 1030 } 1031 1032 static void test_drop_on_reuseport(struct test_sk_lookup *skel) 1033 { 1034 const struct test tests[] = { 1035 { 1036 .desc = "TCP IPv4 drop on reuseport", 1037 .lookup_prog = skel->progs.select_sock_a, 1038 .reuseport_prog = skel->progs.reuseport_drop, 1039 .sock_map = skel->maps.redir_map, 1040 .sotype = SOCK_STREAM, 1041 .connect_to = { EXT_IP4, EXT_PORT }, 1042 .listen_at = { INT_IP4, INT_PORT }, 1043 }, 1044 { 1045 .desc = "TCP IPv6 drop on reuseport", 1046 .lookup_prog = skel->progs.select_sock_a, 1047 .reuseport_prog = skel->progs.reuseport_drop, 1048 .sock_map = skel->maps.redir_map, 1049 .sotype = SOCK_STREAM, 1050 .connect_to = { EXT_IP6, EXT_PORT }, 1051 .listen_at = { INT_IP6, INT_PORT }, 1052 }, 1053 { 1054 .desc = "UDP IPv4 drop on reuseport", 1055 .lookup_prog = skel->progs.select_sock_a, 1056 .reuseport_prog = skel->progs.reuseport_drop, 1057 .sock_map = skel->maps.redir_map, 1058 .sotype = SOCK_DGRAM, 1059 .connect_to = { EXT_IP4, EXT_PORT }, 1060 .listen_at = { INT_IP4, INT_PORT }, 1061 }, 1062 { 1063 .desc = "TCP IPv6 drop on reuseport", 1064 .lookup_prog = skel->progs.select_sock_a, 1065 .reuseport_prog = skel->progs.reuseport_drop, 1066 .sock_map = skel->maps.redir_map, 1067 .sotype = SOCK_STREAM, 1068 .connect_to = { EXT_IP6, EXT_PORT }, 1069 .listen_at = { INT_IP6, INT_PORT }, 1070 }, 1071 }; 1072 const struct test *t; 1073 1074 for (t = tests; t < tests + ARRAY_SIZE(tests); t++) { 1075 if (test__start_subtest(t->desc)) 1076 drop_on_reuseport(t); 1077 } 1078 } 1079 1080 static void run_sk_assign(struct test_sk_lookup *skel, 1081 struct bpf_program *lookup_prog, 1082 const char *remote_ip, const char *local_ip) 1083 { 1084 int server_fds[] = { [0 ... MAX_SERVERS - 1] = -1 }; 1085 struct bpf_sk_lookup ctx; 1086 __u64 server_cookie; 1087 int i, err; 1088 1089 DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts, 1090 .ctx_in = &ctx, 1091 .ctx_size_in = sizeof(ctx), 1092 .ctx_out = &ctx, 1093 .ctx_size_out = sizeof(ctx), 1094 ); 1095 1096 if (fill_sk_lookup_ctx(&ctx, local_ip, EXT_PORT, remote_ip, INT_PORT)) 1097 return; 1098 1099 ctx.protocol = IPPROTO_TCP; 1100 1101 for (i = 0; i < ARRAY_SIZE(server_fds); i++) { 1102 server_fds[i] = make_server(SOCK_STREAM, local_ip, 0, NULL); 1103 if (server_fds[i] < 0) 1104 goto close_servers; 1105 1106 err = update_lookup_map(skel->maps.redir_map, i, 1107 server_fds[i]); 1108 if (err) 1109 goto close_servers; 1110 } 1111 1112 server_cookie = socket_cookie(server_fds[SERVER_B]); 1113 if (!server_cookie) 1114 return; 1115 1116 err = bpf_prog_test_run_opts(bpf_program__fd(lookup_prog), &opts); 1117 if (CHECK(err, "test_run", "failed with error %d\n", errno)) 1118 goto close_servers; 1119 1120 if (CHECK(ctx.cookie == 0, "ctx.cookie", "no socket selected\n")) 1121 goto close_servers; 1122 1123 CHECK(ctx.cookie != server_cookie, "ctx.cookie", 1124 "selected sk %llu instead of %llu\n", ctx.cookie, server_cookie); 1125 1126 close_servers: 1127 for (i = 0; i < ARRAY_SIZE(server_fds); i++) { 1128 if (server_fds[i] != -1) 1129 close(server_fds[i]); 1130 } 1131 } 1132 1133 static void run_sk_assign_v4(struct test_sk_lookup *skel, 1134 struct bpf_program *lookup_prog) 1135 { 1136 run_sk_assign(skel, lookup_prog, INT_IP4, EXT_IP4); 1137 } 1138 1139 static void run_sk_assign_v6(struct test_sk_lookup *skel, 1140 struct bpf_program *lookup_prog) 1141 { 1142 run_sk_assign(skel, lookup_prog, INT_IP6, EXT_IP6); 1143 } 1144 1145 static void run_sk_assign_connected(struct test_sk_lookup *skel, 1146 int sotype) 1147 { 1148 int err, client_fd, connected_fd, server_fd; 1149 struct bpf_link *lookup_link; 1150 1151 server_fd = make_server(sotype, EXT_IP4, EXT_PORT, NULL); 1152 if (server_fd < 0) 1153 return; 1154 1155 connected_fd = make_client(sotype, EXT_IP4, EXT_PORT); 1156 if (connected_fd < 0) 1157 goto out_close_server; 1158 1159 /* Put a connected socket in redirect map */ 1160 err = update_lookup_map(skel->maps.redir_map, SERVER_A, connected_fd); 1161 if (err) 1162 goto out_close_connected; 1163 1164 lookup_link = attach_lookup_prog(skel->progs.sk_assign_esocknosupport); 1165 if (!lookup_link) 1166 goto out_close_connected; 1167 1168 /* Try to redirect TCP SYN / UDP packet to a connected socket */ 1169 client_fd = make_client(sotype, EXT_IP4, EXT_PORT); 1170 if (client_fd < 0) 1171 goto out_unlink_prog; 1172 if (sotype == SOCK_DGRAM) { 1173 send_byte(client_fd); 1174 recv_byte(server_fd); 1175 } 1176 1177 close(client_fd); 1178 out_unlink_prog: 1179 bpf_link__destroy(lookup_link); 1180 out_close_connected: 1181 close(connected_fd); 1182 out_close_server: 1183 close(server_fd); 1184 } 1185 1186 static void test_sk_assign_helper(struct test_sk_lookup *skel) 1187 { 1188 if (test__start_subtest("sk_assign returns EEXIST")) 1189 run_sk_assign_v4(skel, skel->progs.sk_assign_eexist); 1190 if (test__start_subtest("sk_assign honors F_REPLACE")) 1191 run_sk_assign_v4(skel, skel->progs.sk_assign_replace_flag); 1192 if (test__start_subtest("sk_assign accepts NULL socket")) 1193 run_sk_assign_v4(skel, skel->progs.sk_assign_null); 1194 if (test__start_subtest("access ctx->sk")) 1195 run_sk_assign_v4(skel, skel->progs.access_ctx_sk); 1196 if (test__start_subtest("narrow access to ctx v4")) 1197 run_sk_assign_v4(skel, skel->progs.ctx_narrow_access); 1198 if (test__start_subtest("narrow access to ctx v6")) 1199 run_sk_assign_v6(skel, skel->progs.ctx_narrow_access); 1200 if (test__start_subtest("sk_assign rejects TCP established")) 1201 run_sk_assign_connected(skel, SOCK_STREAM); 1202 if (test__start_subtest("sk_assign rejects UDP connected")) 1203 run_sk_assign_connected(skel, SOCK_DGRAM); 1204 } 1205 1206 struct test_multi_prog { 1207 const char *desc; 1208 struct bpf_program *prog1; 1209 struct bpf_program *prog2; 1210 struct bpf_map *redir_map; 1211 struct bpf_map *run_map; 1212 int expect_errno; 1213 struct inet_addr listen_at; 1214 }; 1215 1216 static void run_multi_prog_lookup(const struct test_multi_prog *t) 1217 { 1218 struct sockaddr_storage dst = {}; 1219 int map_fd, server_fd, client_fd; 1220 struct bpf_link *link1, *link2; 1221 int prog_idx, done, err; 1222 1223 map_fd = bpf_map__fd(t->run_map); 1224 1225 done = 0; 1226 prog_idx = PROG1; 1227 err = bpf_map_update_elem(map_fd, &prog_idx, &done, BPF_ANY); 1228 if (CHECK(err, "bpf_map_update_elem", "failed\n")) 1229 return; 1230 prog_idx = PROG2; 1231 err = bpf_map_update_elem(map_fd, &prog_idx, &done, BPF_ANY); 1232 if (CHECK(err, "bpf_map_update_elem", "failed\n")) 1233 return; 1234 1235 link1 = attach_lookup_prog(t->prog1); 1236 if (!link1) 1237 return; 1238 link2 = attach_lookup_prog(t->prog2); 1239 if (!link2) 1240 goto out_unlink1; 1241 1242 server_fd = make_server(SOCK_STREAM, t->listen_at.ip, 1243 t->listen_at.port, NULL); 1244 if (server_fd < 0) 1245 goto out_unlink2; 1246 1247 err = update_lookup_map(t->redir_map, SERVER_A, server_fd); 1248 if (err) 1249 goto out_close_server; 1250 1251 client_fd = make_socket(SOCK_STREAM, EXT_IP4, EXT_PORT, &dst); 1252 if (client_fd < 0) 1253 goto out_close_server; 1254 1255 err = connect(client_fd, (void *)&dst, inetaddr_len(&dst)); 1256 if (CHECK(err && !t->expect_errno, "connect", 1257 "unexpected error %d\n", errno)) 1258 goto out_close_client; 1259 if (CHECK(err && t->expect_errno && errno != t->expect_errno, 1260 "connect", "unexpected error %d\n", errno)) 1261 goto out_close_client; 1262 1263 done = 0; 1264 prog_idx = PROG1; 1265 err = bpf_map_lookup_elem(map_fd, &prog_idx, &done); 1266 CHECK(err, "bpf_map_lookup_elem", "failed\n"); 1267 CHECK(!done, "bpf_map_lookup_elem", "PROG1 !done\n"); 1268 1269 done = 0; 1270 prog_idx = PROG2; 1271 err = bpf_map_lookup_elem(map_fd, &prog_idx, &done); 1272 CHECK(err, "bpf_map_lookup_elem", "failed\n"); 1273 CHECK(!done, "bpf_map_lookup_elem", "PROG2 !done\n"); 1274 1275 out_close_client: 1276 close(client_fd); 1277 out_close_server: 1278 close(server_fd); 1279 out_unlink2: 1280 bpf_link__destroy(link2); 1281 out_unlink1: 1282 bpf_link__destroy(link1); 1283 } 1284 1285 static void test_multi_prog_lookup(struct test_sk_lookup *skel) 1286 { 1287 struct test_multi_prog tests[] = { 1288 { 1289 .desc = "multi prog - pass, pass", 1290 .prog1 = skel->progs.multi_prog_pass1, 1291 .prog2 = skel->progs.multi_prog_pass2, 1292 .listen_at = { EXT_IP4, EXT_PORT }, 1293 }, 1294 { 1295 .desc = "multi prog - drop, drop", 1296 .prog1 = skel->progs.multi_prog_drop1, 1297 .prog2 = skel->progs.multi_prog_drop2, 1298 .listen_at = { EXT_IP4, EXT_PORT }, 1299 .expect_errno = ECONNREFUSED, 1300 }, 1301 { 1302 .desc = "multi prog - pass, drop", 1303 .prog1 = skel->progs.multi_prog_pass1, 1304 .prog2 = skel->progs.multi_prog_drop2, 1305 .listen_at = { EXT_IP4, EXT_PORT }, 1306 .expect_errno = ECONNREFUSED, 1307 }, 1308 { 1309 .desc = "multi prog - drop, pass", 1310 .prog1 = skel->progs.multi_prog_drop1, 1311 .prog2 = skel->progs.multi_prog_pass2, 1312 .listen_at = { EXT_IP4, EXT_PORT }, 1313 .expect_errno = ECONNREFUSED, 1314 }, 1315 { 1316 .desc = "multi prog - pass, redir", 1317 .prog1 = skel->progs.multi_prog_pass1, 1318 .prog2 = skel->progs.multi_prog_redir2, 1319 .listen_at = { INT_IP4, INT_PORT }, 1320 }, 1321 { 1322 .desc = "multi prog - redir, pass", 1323 .prog1 = skel->progs.multi_prog_redir1, 1324 .prog2 = skel->progs.multi_prog_pass2, 1325 .listen_at = { INT_IP4, INT_PORT }, 1326 }, 1327 { 1328 .desc = "multi prog - drop, redir", 1329 .prog1 = skel->progs.multi_prog_drop1, 1330 .prog2 = skel->progs.multi_prog_redir2, 1331 .listen_at = { INT_IP4, INT_PORT }, 1332 }, 1333 { 1334 .desc = "multi prog - redir, drop", 1335 .prog1 = skel->progs.multi_prog_redir1, 1336 .prog2 = skel->progs.multi_prog_drop2, 1337 .listen_at = { INT_IP4, INT_PORT }, 1338 }, 1339 { 1340 .desc = "multi prog - redir, redir", 1341 .prog1 = skel->progs.multi_prog_redir1, 1342 .prog2 = skel->progs.multi_prog_redir2, 1343 .listen_at = { INT_IP4, INT_PORT }, 1344 }, 1345 }; 1346 struct test_multi_prog *t; 1347 1348 for (t = tests; t < tests + ARRAY_SIZE(tests); t++) { 1349 t->redir_map = skel->maps.redir_map; 1350 t->run_map = skel->maps.run_map; 1351 if (test__start_subtest(t->desc)) 1352 run_multi_prog_lookup(t); 1353 } 1354 } 1355 1356 static void run_tests(struct test_sk_lookup *skel) 1357 { 1358 if (test__start_subtest("query lookup prog")) 1359 query_lookup_prog(skel); 1360 test_redirect_lookup(skel); 1361 test_drop_on_lookup(skel); 1362 test_drop_on_reuseport(skel); 1363 test_sk_assign_helper(skel); 1364 test_multi_prog_lookup(skel); 1365 } 1366 1367 static int switch_netns(void) 1368 { 1369 static const char * const setup_script[] = { 1370 "ip -6 addr add dev lo " EXT_IP6 "/128", 1371 "ip -6 addr add dev lo " INT_IP6 "/128", 1372 "ip link set dev lo up", 1373 NULL, 1374 }; 1375 const char * const *cmd; 1376 int err; 1377 1378 err = unshare(CLONE_NEWNET); 1379 if (CHECK(err, "unshare", "failed\n")) { 1380 log_err("unshare(CLONE_NEWNET)"); 1381 return -1; 1382 } 1383 1384 for (cmd = setup_script; *cmd; cmd++) { 1385 err = system(*cmd); 1386 if (CHECK(err, "system", "failed\n")) { 1387 log_err("system(%s)", *cmd); 1388 return -1; 1389 } 1390 } 1391 1392 return 0; 1393 } 1394 1395 void test_sk_lookup(void) 1396 { 1397 struct test_sk_lookup *skel; 1398 int err; 1399 1400 err = switch_netns(); 1401 if (err) 1402 return; 1403 1404 skel = test_sk_lookup__open_and_load(); 1405 if (CHECK(!skel, "skel open_and_load", "failed\n")) 1406 return; 1407 1408 run_tests(skel); 1409 1410 test_sk_lookup__destroy(skel); 1411 } 1412