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