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