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