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