Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
0002 // Copyright (c) 2020 Cloudflare
0003 /*
0004  * Test BPF attach point for INET socket lookup (BPF_SK_LOOKUP).
0005  *
0006  * Tests exercise:
0007  *  - attaching/detaching/querying programs to BPF_SK_LOOKUP hook,
0008  *  - redirecting socket lookup to a socket selected by BPF program,
0009  *  - failing a socket lookup on BPF program's request,
0010  *  - error scenarios for selecting a socket from BPF program,
0011  *  - accessing BPF program context,
0012  *  - attaching and running multiple BPF programs.
0013  *
0014  * Tests run in a dedicated network namespace.
0015  */
0016 
0017 #define _GNU_SOURCE
0018 #include <arpa/inet.h>
0019 #include <assert.h>
0020 #include <errno.h>
0021 #include <error.h>
0022 #include <fcntl.h>
0023 #include <sched.h>
0024 #include <stdio.h>
0025 #include <sys/types.h>
0026 #include <sys/stat.h>
0027 #include <unistd.h>
0028 
0029 #include <bpf/libbpf.h>
0030 #include <bpf/bpf.h>
0031 
0032 #include "test_progs.h"
0033 #include "bpf_util.h"
0034 #include "cgroup_helpers.h"
0035 #include "network_helpers.h"
0036 #include "testing_helpers.h"
0037 #include "test_sk_lookup.skel.h"
0038 
0039 /* External (address, port) pairs the client sends packets to. */
0040 #define EXT_IP4     "127.0.0.1"
0041 #define EXT_IP6     "fd00::1"
0042 #define EXT_PORT    7007
0043 
0044 /* Internal (address, port) pairs the server listens/receives at. */
0045 #define INT_IP4     "127.0.0.2"
0046 #define INT_IP4_V6  "::ffff:127.0.0.2"
0047 #define INT_IP6     "fd00::2"
0048 #define INT_PORT    8008
0049 
0050 #define IO_TIMEOUT_SEC  3
0051 
0052 enum server {
0053     SERVER_A = 0,
0054     SERVER_B = 1,
0055     MAX_SERVERS,
0056 };
0057 
0058 enum {
0059     PROG1 = 0,
0060     PROG2,
0061 };
0062 
0063 struct inet_addr {
0064     const char *ip;
0065     unsigned short port;
0066 };
0067 
0068 struct test {
0069     const char *desc;
0070     struct bpf_program *lookup_prog;
0071     struct bpf_program *reuseport_prog;
0072     struct bpf_map *sock_map;
0073     int sotype;
0074     struct inet_addr connect_to;
0075     struct inet_addr listen_at;
0076     enum server accept_on;
0077     bool reuseport_has_conns; /* Add a connected socket to reuseport group */
0078 };
0079 
0080 static __u32 duration;      /* for CHECK macro */
0081 
0082 static bool is_ipv6(const char *ip)
0083 {
0084     return !!strchr(ip, ':');
0085 }
0086 
0087 static int attach_reuseport(int sock_fd, struct bpf_program *reuseport_prog)
0088 {
0089     int err, prog_fd;
0090 
0091     prog_fd = bpf_program__fd(reuseport_prog);
0092     if (prog_fd < 0) {
0093         errno = -prog_fd;
0094         return -1;
0095     }
0096 
0097     err = setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_REUSEPORT_EBPF,
0098              &prog_fd, sizeof(prog_fd));
0099     if (err)
0100         return -1;
0101 
0102     return 0;
0103 }
0104 
0105 static socklen_t inetaddr_len(const struct sockaddr_storage *addr)
0106 {
0107     return (addr->ss_family == AF_INET ? sizeof(struct sockaddr_in) :
0108         addr->ss_family == AF_INET6 ? sizeof(struct sockaddr_in6) : 0);
0109 }
0110 
0111 static int make_socket(int sotype, const char *ip, int port,
0112                struct sockaddr_storage *addr)
0113 {
0114     struct timeval timeo = { .tv_sec = IO_TIMEOUT_SEC };
0115     int err, family, fd;
0116 
0117     family = is_ipv6(ip) ? AF_INET6 : AF_INET;
0118     err = make_sockaddr(family, ip, port, addr, NULL);
0119     if (CHECK(err, "make_address", "failed\n"))
0120         return -1;
0121 
0122     fd = socket(addr->ss_family, sotype, 0);
0123     if (CHECK(fd < 0, "socket", "failed\n")) {
0124         log_err("failed to make socket");
0125         return -1;
0126     }
0127 
0128     err = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo));
0129     if (CHECK(err, "setsockopt(SO_SNDTIMEO)", "failed\n")) {
0130         log_err("failed to set SNDTIMEO");
0131         close(fd);
0132         return -1;
0133     }
0134 
0135     err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
0136     if (CHECK(err, "setsockopt(SO_RCVTIMEO)", "failed\n")) {
0137         log_err("failed to set RCVTIMEO");
0138         close(fd);
0139         return -1;
0140     }
0141 
0142     return fd;
0143 }
0144 
0145 static int make_server(int sotype, const char *ip, int port,
0146                struct bpf_program *reuseport_prog)
0147 {
0148     struct sockaddr_storage addr = {0};
0149     const int one = 1;
0150     int err, fd = -1;
0151 
0152     fd = make_socket(sotype, ip, port, &addr);
0153     if (fd < 0)
0154         return -1;
0155 
0156     /* Enabled for UDPv6 sockets for IPv4-mapped IPv6 to work. */
0157     if (sotype == SOCK_DGRAM) {
0158         err = setsockopt(fd, SOL_IP, IP_RECVORIGDSTADDR, &one,
0159                  sizeof(one));
0160         if (CHECK(err, "setsockopt(IP_RECVORIGDSTADDR)", "failed\n")) {
0161             log_err("failed to enable IP_RECVORIGDSTADDR");
0162             goto fail;
0163         }
0164     }
0165 
0166     if (sotype == SOCK_DGRAM && addr.ss_family == AF_INET6) {
0167         err = setsockopt(fd, SOL_IPV6, IPV6_RECVORIGDSTADDR, &one,
0168                  sizeof(one));
0169         if (CHECK(err, "setsockopt(IPV6_RECVORIGDSTADDR)", "failed\n")) {
0170             log_err("failed to enable IPV6_RECVORIGDSTADDR");
0171             goto fail;
0172         }
0173     }
0174 
0175     if (sotype == SOCK_STREAM) {
0176         err = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one,
0177                  sizeof(one));
0178         if (CHECK(err, "setsockopt(SO_REUSEADDR)", "failed\n")) {
0179             log_err("failed to enable SO_REUSEADDR");
0180             goto fail;
0181         }
0182     }
0183 
0184     if (reuseport_prog) {
0185         err = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one,
0186                  sizeof(one));
0187         if (CHECK(err, "setsockopt(SO_REUSEPORT)", "failed\n")) {
0188             log_err("failed to enable SO_REUSEPORT");
0189             goto fail;
0190         }
0191     }
0192 
0193     err = bind(fd, (void *)&addr, inetaddr_len(&addr));
0194     if (CHECK(err, "bind", "failed\n")) {
0195         log_err("failed to bind listen socket");
0196         goto fail;
0197     }
0198 
0199     if (sotype == SOCK_STREAM) {
0200         err = listen(fd, SOMAXCONN);
0201         if (CHECK(err, "make_server", "listen")) {
0202             log_err("failed to listen on port %d", port);
0203             goto fail;
0204         }
0205     }
0206 
0207     /* Late attach reuseport prog so we can have one init path */
0208     if (reuseport_prog) {
0209         err = attach_reuseport(fd, reuseport_prog);
0210         if (CHECK(err, "attach_reuseport", "failed\n")) {
0211             log_err("failed to attach reuseport prog");
0212             goto fail;
0213         }
0214     }
0215 
0216     return fd;
0217 fail:
0218     close(fd);
0219     return -1;
0220 }
0221 
0222 static int make_client(int sotype, const char *ip, int port)
0223 {
0224     struct sockaddr_storage addr = {0};
0225     int err, fd;
0226 
0227     fd = make_socket(sotype, ip, port, &addr);
0228     if (fd < 0)
0229         return -1;
0230 
0231     err = connect(fd, (void *)&addr, inetaddr_len(&addr));
0232     if (CHECK(err, "make_client", "connect")) {
0233         log_err("failed to connect client socket");
0234         goto fail;
0235     }
0236 
0237     return fd;
0238 fail:
0239     close(fd);
0240     return -1;
0241 }
0242 
0243 static __u64 socket_cookie(int fd)
0244 {
0245     __u64 cookie;
0246     socklen_t cookie_len = sizeof(cookie);
0247 
0248     if (CHECK(getsockopt(fd, SOL_SOCKET, SO_COOKIE, &cookie, &cookie_len) < 0,
0249           "getsockopt(SO_COOKIE)", "%s\n", strerror(errno)))
0250         return 0;
0251     return cookie;
0252 }
0253 
0254 static int fill_sk_lookup_ctx(struct bpf_sk_lookup *ctx, const char *local_ip, __u16 local_port,
0255                   const char *remote_ip, __u16 remote_port)
0256 {
0257     void *local, *remote;
0258     int err;
0259 
0260     memset(ctx, 0, sizeof(*ctx));
0261     ctx->local_port = local_port;
0262     ctx->remote_port = htons(remote_port);
0263 
0264     if (is_ipv6(local_ip)) {
0265         ctx->family = AF_INET6;
0266         local = &ctx->local_ip6[0];
0267         remote = &ctx->remote_ip6[0];
0268     } else {
0269         ctx->family = AF_INET;
0270         local = &ctx->local_ip4;
0271         remote = &ctx->remote_ip4;
0272     }
0273 
0274     err = inet_pton(ctx->family, local_ip, local);
0275     if (CHECK(err != 1, "inet_pton", "local_ip failed\n"))
0276         return 1;
0277 
0278     err = inet_pton(ctx->family, remote_ip, remote);
0279     if (CHECK(err != 1, "inet_pton", "remote_ip failed\n"))
0280         return 1;
0281 
0282     return 0;
0283 }
0284 
0285 static int send_byte(int fd)
0286 {
0287     ssize_t n;
0288 
0289     errno = 0;
0290     n = send(fd, "a", 1, 0);
0291     if (CHECK(n <= 0, "send_byte", "send")) {
0292         log_err("failed/partial send");
0293         return -1;
0294     }
0295     return 0;
0296 }
0297 
0298 static int recv_byte(int fd)
0299 {
0300     char buf[1];
0301     ssize_t n;
0302 
0303     n = recv(fd, buf, sizeof(buf), 0);
0304     if (CHECK(n <= 0, "recv_byte", "recv")) {
0305         log_err("failed/partial recv");
0306         return -1;
0307     }
0308     return 0;
0309 }
0310 
0311 static int tcp_recv_send(int server_fd)
0312 {
0313     char buf[1];
0314     int ret, fd;
0315     ssize_t n;
0316 
0317     fd = accept(server_fd, NULL, NULL);
0318     if (CHECK(fd < 0, "accept", "failed\n")) {
0319         log_err("failed to accept");
0320         return -1;
0321     }
0322 
0323     n = recv(fd, buf, sizeof(buf), 0);
0324     if (CHECK(n <= 0, "recv", "failed\n")) {
0325         log_err("failed/partial recv");
0326         ret = -1;
0327         goto close;
0328     }
0329 
0330     n = send(fd, buf, n, 0);
0331     if (CHECK(n <= 0, "send", "failed\n")) {
0332         log_err("failed/partial send");
0333         ret = -1;
0334         goto close;
0335     }
0336 
0337     ret = 0;
0338 close:
0339     close(fd);
0340     return ret;
0341 }
0342 
0343 static void v4_to_v6(struct sockaddr_storage *ss)
0344 {
0345     struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)ss;
0346     struct sockaddr_in v4 = *(struct sockaddr_in *)ss;
0347 
0348     v6->sin6_family = AF_INET6;
0349     v6->sin6_port = v4.sin_port;
0350     v6->sin6_addr.s6_addr[10] = 0xff;
0351     v6->sin6_addr.s6_addr[11] = 0xff;
0352     memcpy(&v6->sin6_addr.s6_addr[12], &v4.sin_addr.s_addr, 4);
0353     memset(&v6->sin6_addr.s6_addr[0], 0, 10);
0354 }
0355 
0356 static int udp_recv_send(int server_fd)
0357 {
0358     char cmsg_buf[CMSG_SPACE(sizeof(struct sockaddr_storage))];
0359     struct sockaddr_storage _src_addr = { 0 };
0360     struct sockaddr_storage *src_addr = &_src_addr;
0361     struct sockaddr_storage *dst_addr = NULL;
0362     struct msghdr msg = { 0 };
0363     struct iovec iov = { 0 };
0364     struct cmsghdr *cm;
0365     char buf[1];
0366     int ret, fd;
0367     ssize_t n;
0368 
0369     iov.iov_base = buf;
0370     iov.iov_len = sizeof(buf);
0371 
0372     msg.msg_name = src_addr;
0373     msg.msg_namelen = sizeof(*src_addr);
0374     msg.msg_iov = &iov;
0375     msg.msg_iovlen = 1;
0376     msg.msg_control = cmsg_buf;
0377     msg.msg_controllen = sizeof(cmsg_buf);
0378 
0379     errno = 0;
0380     n = recvmsg(server_fd, &msg, 0);
0381     if (CHECK(n <= 0, "recvmsg", "failed\n")) {
0382         log_err("failed to receive");
0383         return -1;
0384     }
0385     if (CHECK(msg.msg_flags & MSG_CTRUNC, "recvmsg", "truncated cmsg\n"))
0386         return -1;
0387 
0388     for (cm = CMSG_FIRSTHDR(&msg); cm; cm = CMSG_NXTHDR(&msg, cm)) {
0389         if ((cm->cmsg_level == SOL_IP &&
0390              cm->cmsg_type == IP_ORIGDSTADDR) ||
0391             (cm->cmsg_level == SOL_IPV6 &&
0392              cm->cmsg_type == IPV6_ORIGDSTADDR)) {
0393             dst_addr = (struct sockaddr_storage *)CMSG_DATA(cm);
0394             break;
0395         }
0396         log_err("warning: ignored cmsg at level %d type %d",
0397             cm->cmsg_level, cm->cmsg_type);
0398     }
0399     if (CHECK(!dst_addr, "recvmsg", "missing ORIGDSTADDR\n"))
0400         return -1;
0401 
0402     /* Server socket bound to IPv4-mapped IPv6 address */
0403     if (src_addr->ss_family == AF_INET6 &&
0404         dst_addr->ss_family == AF_INET) {
0405         v4_to_v6(dst_addr);
0406     }
0407 
0408     /* Reply from original destination address. */
0409     fd = socket(dst_addr->ss_family, SOCK_DGRAM, 0);
0410     if (CHECK(fd < 0, "socket", "failed\n")) {
0411         log_err("failed to create tx socket");
0412         return -1;
0413     }
0414 
0415     ret = bind(fd, (struct sockaddr *)dst_addr, sizeof(*dst_addr));
0416     if (CHECK(ret, "bind", "failed\n")) {
0417         log_err("failed to bind tx socket");
0418         goto out;
0419     }
0420 
0421     msg.msg_control = NULL;
0422     msg.msg_controllen = 0;
0423     n = sendmsg(fd, &msg, 0);
0424     if (CHECK(n <= 0, "sendmsg", "failed\n")) {
0425         log_err("failed to send echo reply");
0426         ret = -1;
0427         goto out;
0428     }
0429 
0430     ret = 0;
0431 out:
0432     close(fd);
0433     return ret;
0434 }
0435 
0436 static int tcp_echo_test(int client_fd, int server_fd)
0437 {
0438     int err;
0439 
0440     err = send_byte(client_fd);
0441     if (err)
0442         return -1;
0443     err = tcp_recv_send(server_fd);
0444     if (err)
0445         return -1;
0446     err = recv_byte(client_fd);
0447     if (err)
0448         return -1;
0449 
0450     return 0;
0451 }
0452 
0453 static int udp_echo_test(int client_fd, int server_fd)
0454 {
0455     int err;
0456 
0457     err = send_byte(client_fd);
0458     if (err)
0459         return -1;
0460     err = udp_recv_send(server_fd);
0461     if (err)
0462         return -1;
0463     err = recv_byte(client_fd);
0464     if (err)
0465         return -1;
0466 
0467     return 0;
0468 }
0469 
0470 static struct bpf_link *attach_lookup_prog(struct bpf_program *prog)
0471 {
0472     struct bpf_link *link;
0473     int net_fd;
0474 
0475     net_fd = open("/proc/self/ns/net", O_RDONLY);
0476     if (CHECK(net_fd < 0, "open", "failed\n")) {
0477         log_err("failed to open /proc/self/ns/net");
0478         return NULL;
0479     }
0480 
0481     link = bpf_program__attach_netns(prog, net_fd);
0482     if (!ASSERT_OK_PTR(link, "bpf_program__attach_netns")) {
0483         errno = -PTR_ERR(link);
0484         log_err("failed to attach program '%s' to netns",
0485             bpf_program__name(prog));
0486         link = NULL;
0487     }
0488 
0489     close(net_fd);
0490     return link;
0491 }
0492 
0493 static int update_lookup_map(struct bpf_map *map, int index, int sock_fd)
0494 {
0495     int err, map_fd;
0496     uint64_t value;
0497 
0498     map_fd = bpf_map__fd(map);
0499     if (CHECK(map_fd < 0, "bpf_map__fd", "failed\n")) {
0500         errno = -map_fd;
0501         log_err("failed to get map FD");
0502         return -1;
0503     }
0504 
0505     value = (uint64_t)sock_fd;
0506     err = bpf_map_update_elem(map_fd, &index, &value, BPF_NOEXIST);
0507     if (CHECK(err, "bpf_map_update_elem", "failed\n")) {
0508         log_err("failed to update redir_map @ %d", index);
0509         return -1;
0510     }
0511 
0512     return 0;
0513 }
0514 
0515 static void query_lookup_prog(struct test_sk_lookup *skel)
0516 {
0517     struct bpf_link *link[3] = {};
0518     struct bpf_link_info info;
0519     __u32 attach_flags = 0;
0520     __u32 prog_ids[3] = {};
0521     __u32 prog_cnt = 3;
0522     __u32 prog_id;
0523     int net_fd;
0524     int err;
0525 
0526     net_fd = open("/proc/self/ns/net", O_RDONLY);
0527     if (CHECK(net_fd < 0, "open", "failed\n")) {
0528         log_err("failed to open /proc/self/ns/net");
0529         return;
0530     }
0531 
0532     link[0] = attach_lookup_prog(skel->progs.lookup_pass);
0533     if (!link[0])
0534         goto close;
0535     link[1] = attach_lookup_prog(skel->progs.lookup_pass);
0536     if (!link[1])
0537         goto detach;
0538     link[2] = attach_lookup_prog(skel->progs.lookup_drop);
0539     if (!link[2])
0540         goto detach;
0541 
0542     err = bpf_prog_query(net_fd, BPF_SK_LOOKUP, 0 /* query flags */,
0543                  &attach_flags, prog_ids, &prog_cnt);
0544     if (CHECK(err, "bpf_prog_query", "failed\n")) {
0545         log_err("failed to query lookup prog");
0546         goto detach;
0547     }
0548 
0549     errno = 0;
0550     if (CHECK(attach_flags != 0, "bpf_prog_query",
0551           "wrong attach_flags on query: %u", attach_flags))
0552         goto detach;
0553     if (CHECK(prog_cnt != 3, "bpf_prog_query",
0554           "wrong program count on query: %u", prog_cnt))
0555         goto detach;
0556     prog_id = link_info_prog_id(link[0], &info);
0557     CHECK(prog_ids[0] != prog_id, "bpf_prog_query",
0558           "invalid program #0 id on query: %u != %u\n",
0559           prog_ids[0], prog_id);
0560     CHECK(info.netns.netns_ino == 0, "netns_ino",
0561           "unexpected netns_ino: %u\n", info.netns.netns_ino);
0562     prog_id = link_info_prog_id(link[1], &info);
0563     CHECK(prog_ids[1] != prog_id, "bpf_prog_query",
0564           "invalid program #1 id on query: %u != %u\n",
0565           prog_ids[1], prog_id);
0566     CHECK(info.netns.netns_ino == 0, "netns_ino",
0567           "unexpected netns_ino: %u\n", info.netns.netns_ino);
0568     prog_id = link_info_prog_id(link[2], &info);
0569     CHECK(prog_ids[2] != prog_id, "bpf_prog_query",
0570           "invalid program #2 id on query: %u != %u\n",
0571           prog_ids[2], prog_id);
0572     CHECK(info.netns.netns_ino == 0, "netns_ino",
0573           "unexpected netns_ino: %u\n", info.netns.netns_ino);
0574 
0575     err = bpf_link__detach(link[0]);
0576     if (CHECK(err, "link_detach", "failed %d\n", err))
0577         goto detach;
0578 
0579     /* prog id is still there, but netns_ino is zeroed out */
0580     prog_id = link_info_prog_id(link[0], &info);
0581     CHECK(prog_ids[0] != prog_id, "bpf_prog_query",
0582           "invalid program #0 id on query: %u != %u\n",
0583           prog_ids[0], prog_id);
0584     CHECK(info.netns.netns_ino != 0, "netns_ino",
0585           "unexpected netns_ino: %u\n", info.netns.netns_ino);
0586 
0587 detach:
0588     if (link[2])
0589         bpf_link__destroy(link[2]);
0590     if (link[1])
0591         bpf_link__destroy(link[1]);
0592     if (link[0])
0593         bpf_link__destroy(link[0]);
0594 close:
0595     close(net_fd);
0596 }
0597 
0598 static void run_lookup_prog(const struct test *t)
0599 {
0600     int server_fds[] = { [0 ... MAX_SERVERS - 1] = -1 };
0601     int client_fd, reuse_conn_fd = -1;
0602     struct bpf_link *lookup_link;
0603     int i, err;
0604 
0605     lookup_link = attach_lookup_prog(t->lookup_prog);
0606     if (!lookup_link)
0607         return;
0608 
0609     for (i = 0; i < ARRAY_SIZE(server_fds); i++) {
0610         server_fds[i] = make_server(t->sotype, t->listen_at.ip,
0611                         t->listen_at.port,
0612                         t->reuseport_prog);
0613         if (server_fds[i] < 0)
0614             goto close;
0615 
0616         err = update_lookup_map(t->sock_map, i, server_fds[i]);
0617         if (err)
0618             goto close;
0619 
0620         /* want just one server for non-reuseport test */
0621         if (!t->reuseport_prog)
0622             break;
0623     }
0624 
0625     /* Regular UDP socket lookup with reuseport behaves
0626      * differently when reuseport group contains connected
0627      * sockets. Check that adding a connected UDP socket to the
0628      * reuseport group does not affect how reuseport works with
0629      * BPF socket lookup.
0630      */
0631     if (t->reuseport_has_conns) {
0632         struct sockaddr_storage addr = {};
0633         socklen_t len = sizeof(addr);
0634 
0635         /* Add an extra socket to reuseport group */
0636         reuse_conn_fd = make_server(t->sotype, t->listen_at.ip,
0637                         t->listen_at.port,
0638                         t->reuseport_prog);
0639         if (reuse_conn_fd < 0)
0640             goto close;
0641 
0642         /* Connect the extra socket to itself */
0643         err = getsockname(reuse_conn_fd, (void *)&addr, &len);
0644         if (CHECK(err, "getsockname", "errno %d\n", errno))
0645             goto close;
0646         err = connect(reuse_conn_fd, (void *)&addr, len);
0647         if (CHECK(err, "connect", "errno %d\n", errno))
0648             goto close;
0649     }
0650 
0651     client_fd = make_client(t->sotype, t->connect_to.ip, t->connect_to.port);
0652     if (client_fd < 0)
0653         goto close;
0654 
0655     if (t->sotype == SOCK_STREAM)
0656         tcp_echo_test(client_fd, server_fds[t->accept_on]);
0657     else
0658         udp_echo_test(client_fd, server_fds[t->accept_on]);
0659 
0660     close(client_fd);
0661 close:
0662     if (reuse_conn_fd != -1)
0663         close(reuse_conn_fd);
0664     for (i = 0; i < ARRAY_SIZE(server_fds); i++) {
0665         if (server_fds[i] != -1)
0666             close(server_fds[i]);
0667     }
0668     bpf_link__destroy(lookup_link);
0669 }
0670 
0671 static void test_redirect_lookup(struct test_sk_lookup *skel)
0672 {
0673     const struct test tests[] = {
0674         {
0675             .desc       = "TCP IPv4 redir port",
0676             .lookup_prog    = skel->progs.redir_port,
0677             .sock_map   = skel->maps.redir_map,
0678             .sotype     = SOCK_STREAM,
0679             .connect_to = { EXT_IP4, EXT_PORT },
0680             .listen_at  = { EXT_IP4, INT_PORT },
0681         },
0682         {
0683             .desc       = "TCP IPv4 redir addr",
0684             .lookup_prog    = skel->progs.redir_ip4,
0685             .sock_map   = skel->maps.redir_map,
0686             .sotype     = SOCK_STREAM,
0687             .connect_to = { EXT_IP4, EXT_PORT },
0688             .listen_at  = { INT_IP4, EXT_PORT },
0689         },
0690         {
0691             .desc       = "TCP IPv4 redir with reuseport",
0692             .lookup_prog    = skel->progs.select_sock_a,
0693             .reuseport_prog = skel->progs.select_sock_b,
0694             .sock_map   = skel->maps.redir_map,
0695             .sotype     = SOCK_STREAM,
0696             .connect_to = { EXT_IP4, EXT_PORT },
0697             .listen_at  = { INT_IP4, INT_PORT },
0698             .accept_on  = SERVER_B,
0699         },
0700         {
0701             .desc       = "TCP IPv4 redir skip reuseport",
0702             .lookup_prog    = skel->progs.select_sock_a_no_reuseport,
0703             .reuseport_prog = skel->progs.select_sock_b,
0704             .sock_map   = skel->maps.redir_map,
0705             .sotype     = SOCK_STREAM,
0706             .connect_to = { EXT_IP4, EXT_PORT },
0707             .listen_at  = { INT_IP4, INT_PORT },
0708             .accept_on  = SERVER_A,
0709         },
0710         {
0711             .desc       = "TCP IPv6 redir port",
0712             .lookup_prog    = skel->progs.redir_port,
0713             .sock_map   = skel->maps.redir_map,
0714             .sotype     = SOCK_STREAM,
0715             .connect_to = { EXT_IP6, EXT_PORT },
0716             .listen_at  = { EXT_IP6, INT_PORT },
0717         },
0718         {
0719             .desc       = "TCP IPv6 redir addr",
0720             .lookup_prog    = skel->progs.redir_ip6,
0721             .sock_map   = skel->maps.redir_map,
0722             .sotype     = SOCK_STREAM,
0723             .connect_to = { EXT_IP6, EXT_PORT },
0724             .listen_at  = { INT_IP6, EXT_PORT },
0725         },
0726         {
0727             .desc       = "TCP IPv4->IPv6 redir port",
0728             .lookup_prog    = skel->progs.redir_port,
0729             .sock_map   = skel->maps.redir_map,
0730             .sotype     = SOCK_STREAM,
0731             .connect_to = { EXT_IP4, EXT_PORT },
0732             .listen_at  = { INT_IP4_V6, INT_PORT },
0733         },
0734         {
0735             .desc       = "TCP IPv6 redir with reuseport",
0736             .lookup_prog    = skel->progs.select_sock_a,
0737             .reuseport_prog = skel->progs.select_sock_b,
0738             .sock_map   = skel->maps.redir_map,
0739             .sotype     = SOCK_STREAM,
0740             .connect_to = { EXT_IP6, EXT_PORT },
0741             .listen_at  = { INT_IP6, INT_PORT },
0742             .accept_on  = SERVER_B,
0743         },
0744         {
0745             .desc       = "TCP IPv6 redir skip reuseport",
0746             .lookup_prog    = skel->progs.select_sock_a_no_reuseport,
0747             .reuseport_prog = skel->progs.select_sock_b,
0748             .sock_map   = skel->maps.redir_map,
0749             .sotype     = SOCK_STREAM,
0750             .connect_to = { EXT_IP6, EXT_PORT },
0751             .listen_at  = { INT_IP6, INT_PORT },
0752             .accept_on  = SERVER_A,
0753         },
0754         {
0755             .desc       = "UDP IPv4 redir port",
0756             .lookup_prog    = skel->progs.redir_port,
0757             .sock_map   = skel->maps.redir_map,
0758             .sotype     = SOCK_DGRAM,
0759             .connect_to = { EXT_IP4, EXT_PORT },
0760             .listen_at  = { EXT_IP4, INT_PORT },
0761         },
0762         {
0763             .desc       = "UDP IPv4 redir addr",
0764             .lookup_prog    = skel->progs.redir_ip4,
0765             .sock_map   = skel->maps.redir_map,
0766             .sotype     = SOCK_DGRAM,
0767             .connect_to = { EXT_IP4, EXT_PORT },
0768             .listen_at  = { INT_IP4, EXT_PORT },
0769         },
0770         {
0771             .desc       = "UDP IPv4 redir with reuseport",
0772             .lookup_prog    = skel->progs.select_sock_a,
0773             .reuseport_prog = skel->progs.select_sock_b,
0774             .sock_map   = skel->maps.redir_map,
0775             .sotype     = SOCK_DGRAM,
0776             .connect_to = { EXT_IP4, EXT_PORT },
0777             .listen_at  = { INT_IP4, INT_PORT },
0778             .accept_on  = SERVER_B,
0779         },
0780         {
0781             .desc       = "UDP IPv4 redir and reuseport with conns",
0782             .lookup_prog    = skel->progs.select_sock_a,
0783             .reuseport_prog = skel->progs.select_sock_b,
0784             .sock_map   = skel->maps.redir_map,
0785             .sotype     = SOCK_DGRAM,
0786             .connect_to = { EXT_IP4, EXT_PORT },
0787             .listen_at  = { INT_IP4, INT_PORT },
0788             .accept_on  = SERVER_B,
0789             .reuseport_has_conns = true,
0790         },
0791         {
0792             .desc       = "UDP IPv4 redir skip reuseport",
0793             .lookup_prog    = skel->progs.select_sock_a_no_reuseport,
0794             .reuseport_prog = skel->progs.select_sock_b,
0795             .sock_map   = skel->maps.redir_map,
0796             .sotype     = SOCK_DGRAM,
0797             .connect_to = { EXT_IP4, EXT_PORT },
0798             .listen_at  = { INT_IP4, INT_PORT },
0799             .accept_on  = SERVER_A,
0800         },
0801         {
0802             .desc       = "UDP IPv6 redir port",
0803             .lookup_prog    = skel->progs.redir_port,
0804             .sock_map   = skel->maps.redir_map,
0805             .sotype     = SOCK_DGRAM,
0806             .connect_to = { EXT_IP6, EXT_PORT },
0807             .listen_at  = { EXT_IP6, INT_PORT },
0808         },
0809         {
0810             .desc       = "UDP IPv6 redir addr",
0811             .lookup_prog    = skel->progs.redir_ip6,
0812             .sock_map   = skel->maps.redir_map,
0813             .sotype     = SOCK_DGRAM,
0814             .connect_to = { EXT_IP6, EXT_PORT },
0815             .listen_at  = { INT_IP6, EXT_PORT },
0816         },
0817         {
0818             .desc       = "UDP IPv4->IPv6 redir port",
0819             .lookup_prog    = skel->progs.redir_port,
0820             .sock_map   = skel->maps.redir_map,
0821             .sotype     = SOCK_DGRAM,
0822             .listen_at  = { INT_IP4_V6, INT_PORT },
0823             .connect_to = { EXT_IP4, EXT_PORT },
0824         },
0825         {
0826             .desc       = "UDP IPv6 redir and reuseport",
0827             .lookup_prog    = skel->progs.select_sock_a,
0828             .reuseport_prog = skel->progs.select_sock_b,
0829             .sock_map   = skel->maps.redir_map,
0830             .sotype     = SOCK_DGRAM,
0831             .connect_to = { EXT_IP6, EXT_PORT },
0832             .listen_at  = { INT_IP6, INT_PORT },
0833             .accept_on  = SERVER_B,
0834         },
0835         {
0836             .desc       = "UDP IPv6 redir and reuseport with conns",
0837             .lookup_prog    = skel->progs.select_sock_a,
0838             .reuseport_prog = skel->progs.select_sock_b,
0839             .sock_map   = skel->maps.redir_map,
0840             .sotype     = SOCK_DGRAM,
0841             .connect_to = { EXT_IP6, EXT_PORT },
0842             .listen_at  = { INT_IP6, INT_PORT },
0843             .accept_on  = SERVER_B,
0844             .reuseport_has_conns = true,
0845         },
0846         {
0847             .desc       = "UDP IPv6 redir skip reuseport",
0848             .lookup_prog    = skel->progs.select_sock_a_no_reuseport,
0849             .reuseport_prog = skel->progs.select_sock_b,
0850             .sock_map   = skel->maps.redir_map,
0851             .sotype     = SOCK_DGRAM,
0852             .connect_to = { EXT_IP6, EXT_PORT },
0853             .listen_at  = { INT_IP6, INT_PORT },
0854             .accept_on  = SERVER_A,
0855         },
0856     };
0857     const struct test *t;
0858 
0859     for (t = tests; t < tests + ARRAY_SIZE(tests); t++) {
0860         if (test__start_subtest(t->desc))
0861             run_lookup_prog(t);
0862     }
0863 }
0864 
0865 static void drop_on_lookup(const struct test *t)
0866 {
0867     struct sockaddr_storage dst = {};
0868     int client_fd, server_fd, err;
0869     struct bpf_link *lookup_link;
0870     ssize_t n;
0871 
0872     lookup_link = attach_lookup_prog(t->lookup_prog);
0873     if (!lookup_link)
0874         return;
0875 
0876     server_fd = make_server(t->sotype, t->listen_at.ip, t->listen_at.port,
0877                 t->reuseport_prog);
0878     if (server_fd < 0)
0879         goto detach;
0880 
0881     client_fd = make_socket(t->sotype, t->connect_to.ip,
0882                 t->connect_to.port, &dst);
0883     if (client_fd < 0)
0884         goto close_srv;
0885 
0886     err = connect(client_fd, (void *)&dst, inetaddr_len(&dst));
0887     if (t->sotype == SOCK_DGRAM) {
0888         err = send_byte(client_fd);
0889         if (err)
0890             goto close_all;
0891 
0892         /* Read out asynchronous error */
0893         n = recv(client_fd, NULL, 0, 0);
0894         err = n == -1;
0895     }
0896     if (CHECK(!err || errno != ECONNREFUSED, "connect",
0897           "unexpected success or error\n"))
0898         log_err("expected ECONNREFUSED on connect");
0899 
0900 close_all:
0901     close(client_fd);
0902 close_srv:
0903     close(server_fd);
0904 detach:
0905     bpf_link__destroy(lookup_link);
0906 }
0907 
0908 static void test_drop_on_lookup(struct test_sk_lookup *skel)
0909 {
0910     const struct test tests[] = {
0911         {
0912             .desc       = "TCP IPv4 drop on lookup",
0913             .lookup_prog    = skel->progs.lookup_drop,
0914             .sotype     = SOCK_STREAM,
0915             .connect_to = { EXT_IP4, EXT_PORT },
0916             .listen_at  = { EXT_IP4, EXT_PORT },
0917         },
0918         {
0919             .desc       = "TCP IPv6 drop on lookup",
0920             .lookup_prog    = skel->progs.lookup_drop,
0921             .sotype     = SOCK_STREAM,
0922             .connect_to = { EXT_IP6, EXT_PORT },
0923             .listen_at  = { EXT_IP6, EXT_PORT },
0924         },
0925         {
0926             .desc       = "UDP IPv4 drop on lookup",
0927             .lookup_prog    = skel->progs.lookup_drop,
0928             .sotype     = SOCK_DGRAM,
0929             .connect_to = { EXT_IP4, EXT_PORT },
0930             .listen_at  = { EXT_IP4, EXT_PORT },
0931         },
0932         {
0933             .desc       = "UDP IPv6 drop on lookup",
0934             .lookup_prog    = skel->progs.lookup_drop,
0935             .sotype     = SOCK_DGRAM,
0936             .connect_to = { EXT_IP6, EXT_PORT },
0937             .listen_at  = { EXT_IP6, INT_PORT },
0938         },
0939         /* The program will drop on success, meaning that the ifindex
0940          * was 1.
0941          */
0942         {
0943             .desc       = "TCP IPv4 drop on valid ifindex",
0944             .lookup_prog    = skel->progs.check_ifindex,
0945             .sotype     = SOCK_STREAM,
0946             .connect_to = { EXT_IP4, EXT_PORT },
0947             .listen_at  = { EXT_IP4, EXT_PORT },
0948         },
0949         {
0950             .desc       = "TCP IPv6 drop on valid ifindex",
0951             .lookup_prog    = skel->progs.check_ifindex,
0952             .sotype     = SOCK_STREAM,
0953             .connect_to = { EXT_IP6, EXT_PORT },
0954             .listen_at  = { EXT_IP6, EXT_PORT },
0955         },
0956         {
0957             .desc       = "UDP IPv4 drop on valid ifindex",
0958             .lookup_prog    = skel->progs.check_ifindex,
0959             .sotype     = SOCK_DGRAM,
0960             .connect_to = { EXT_IP4, EXT_PORT },
0961             .listen_at  = { EXT_IP4, EXT_PORT },
0962         },
0963         {
0964             .desc       = "UDP IPv6 drop on valid ifindex",
0965             .lookup_prog    = skel->progs.check_ifindex,
0966             .sotype     = SOCK_DGRAM,
0967             .connect_to = { EXT_IP6, EXT_PORT },
0968             .listen_at  = { EXT_IP6, EXT_PORT },
0969         },
0970     };
0971     const struct test *t;
0972 
0973     for (t = tests; t < tests + ARRAY_SIZE(tests); t++) {
0974         if (test__start_subtest(t->desc))
0975             drop_on_lookup(t);
0976     }
0977 }
0978 
0979 static void drop_on_reuseport(const struct test *t)
0980 {
0981     struct sockaddr_storage dst = { 0 };
0982     int client, server1, server2, err;
0983     struct bpf_link *lookup_link;
0984     ssize_t n;
0985 
0986     lookup_link = attach_lookup_prog(t->lookup_prog);
0987     if (!lookup_link)
0988         return;
0989 
0990     server1 = make_server(t->sotype, t->listen_at.ip, t->listen_at.port,
0991                   t->reuseport_prog);
0992     if (server1 < 0)
0993         goto detach;
0994 
0995     err = update_lookup_map(t->sock_map, SERVER_A, server1);
0996     if (err)
0997         goto detach;
0998 
0999     /* second server on destination address we should never reach */
1000     server2 = make_server(t->sotype, t->connect_to.ip, t->connect_to.port,
1001                   NULL /* reuseport prog */);
1002     if (server2 < 0)
1003         goto close_srv1;
1004 
1005     client = make_socket(t->sotype, t->connect_to.ip,
1006                  t->connect_to.port, &dst);
1007     if (client < 0)
1008         goto close_srv2;
1009 
1010     err = connect(client, (void *)&dst, inetaddr_len(&dst));
1011     if (t->sotype == SOCK_DGRAM) {
1012         err = send_byte(client);
1013         if (err)
1014             goto close_all;
1015 
1016         /* Read out asynchronous error */
1017         n = recv(client, NULL, 0, 0);
1018         err = n == -1;
1019     }
1020     if (CHECK(!err || errno != ECONNREFUSED, "connect",
1021           "unexpected success or error\n"))
1022         log_err("expected ECONNREFUSED on connect");
1023 
1024 close_all:
1025     close(client);
1026 close_srv2:
1027     close(server2);
1028 close_srv1:
1029     close(server1);
1030 detach:
1031     bpf_link__destroy(lookup_link);
1032 }
1033 
1034 static void test_drop_on_reuseport(struct test_sk_lookup *skel)
1035 {
1036     const struct test tests[] = {
1037         {
1038             .desc       = "TCP IPv4 drop on reuseport",
1039             .lookup_prog    = skel->progs.select_sock_a,
1040             .reuseport_prog = skel->progs.reuseport_drop,
1041             .sock_map   = skel->maps.redir_map,
1042             .sotype     = SOCK_STREAM,
1043             .connect_to = { EXT_IP4, EXT_PORT },
1044             .listen_at  = { INT_IP4, INT_PORT },
1045         },
1046         {
1047             .desc       = "TCP IPv6 drop on reuseport",
1048             .lookup_prog    = skel->progs.select_sock_a,
1049             .reuseport_prog = skel->progs.reuseport_drop,
1050             .sock_map   = skel->maps.redir_map,
1051             .sotype     = SOCK_STREAM,
1052             .connect_to = { EXT_IP6, EXT_PORT },
1053             .listen_at  = { INT_IP6, INT_PORT },
1054         },
1055         {
1056             .desc       = "UDP IPv4 drop on reuseport",
1057             .lookup_prog    = skel->progs.select_sock_a,
1058             .reuseport_prog = skel->progs.reuseport_drop,
1059             .sock_map   = skel->maps.redir_map,
1060             .sotype     = SOCK_DGRAM,
1061             .connect_to = { EXT_IP4, EXT_PORT },
1062             .listen_at  = { INT_IP4, INT_PORT },
1063         },
1064         {
1065             .desc       = "TCP IPv6 drop on reuseport",
1066             .lookup_prog    = skel->progs.select_sock_a,
1067             .reuseport_prog = skel->progs.reuseport_drop,
1068             .sock_map   = skel->maps.redir_map,
1069             .sotype     = SOCK_STREAM,
1070             .connect_to = { EXT_IP6, EXT_PORT },
1071             .listen_at  = { INT_IP6, INT_PORT },
1072         },
1073     };
1074     const struct test *t;
1075 
1076     for (t = tests; t < tests + ARRAY_SIZE(tests); t++) {
1077         if (test__start_subtest(t->desc))
1078             drop_on_reuseport(t);
1079     }
1080 }
1081 
1082 static void run_sk_assign(struct test_sk_lookup *skel,
1083               struct bpf_program *lookup_prog,
1084               const char *remote_ip, const char *local_ip)
1085 {
1086     int server_fds[] = { [0 ... MAX_SERVERS - 1] = -1 };
1087     struct bpf_sk_lookup ctx;
1088     __u64 server_cookie;
1089     int i, err;
1090 
1091     DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,
1092         .ctx_in = &ctx,
1093         .ctx_size_in = sizeof(ctx),
1094         .ctx_out = &ctx,
1095         .ctx_size_out = sizeof(ctx),
1096     );
1097 
1098     if (fill_sk_lookup_ctx(&ctx, local_ip, EXT_PORT, remote_ip, INT_PORT))
1099         return;
1100 
1101     ctx.protocol = IPPROTO_TCP;
1102 
1103     for (i = 0; i < ARRAY_SIZE(server_fds); i++) {
1104         server_fds[i] = make_server(SOCK_STREAM, local_ip, 0, NULL);
1105         if (server_fds[i] < 0)
1106             goto close_servers;
1107 
1108         err = update_lookup_map(skel->maps.redir_map, i,
1109                     server_fds[i]);
1110         if (err)
1111             goto close_servers;
1112     }
1113 
1114     server_cookie = socket_cookie(server_fds[SERVER_B]);
1115     if (!server_cookie)
1116         return;
1117 
1118     err = bpf_prog_test_run_opts(bpf_program__fd(lookup_prog), &opts);
1119     if (CHECK(err, "test_run", "failed with error %d\n", errno))
1120         goto close_servers;
1121 
1122     if (CHECK(ctx.cookie == 0, "ctx.cookie", "no socket selected\n"))
1123         goto close_servers;
1124 
1125     CHECK(ctx.cookie != server_cookie, "ctx.cookie",
1126           "selected sk %llu instead of %llu\n", ctx.cookie, server_cookie);
1127 
1128 close_servers:
1129     for (i = 0; i < ARRAY_SIZE(server_fds); i++) {
1130         if (server_fds[i] != -1)
1131             close(server_fds[i]);
1132     }
1133 }
1134 
1135 static void run_sk_assign_v4(struct test_sk_lookup *skel,
1136                  struct bpf_program *lookup_prog)
1137 {
1138     run_sk_assign(skel, lookup_prog, INT_IP4, EXT_IP4);
1139 }
1140 
1141 static void run_sk_assign_v6(struct test_sk_lookup *skel,
1142                  struct bpf_program *lookup_prog)
1143 {
1144     run_sk_assign(skel, lookup_prog, INT_IP6, EXT_IP6);
1145 }
1146 
1147 static void run_sk_assign_connected(struct test_sk_lookup *skel,
1148                     int sotype)
1149 {
1150     int err, client_fd, connected_fd, server_fd;
1151     struct bpf_link *lookup_link;
1152 
1153     server_fd = make_server(sotype, EXT_IP4, EXT_PORT, NULL);
1154     if (server_fd < 0)
1155         return;
1156 
1157     connected_fd = make_client(sotype, EXT_IP4, EXT_PORT);
1158     if (connected_fd < 0)
1159         goto out_close_server;
1160 
1161     /* Put a connected socket in redirect map */
1162     err = update_lookup_map(skel->maps.redir_map, SERVER_A, connected_fd);
1163     if (err)
1164         goto out_close_connected;
1165 
1166     lookup_link = attach_lookup_prog(skel->progs.sk_assign_esocknosupport);
1167     if (!lookup_link)
1168         goto out_close_connected;
1169 
1170     /* Try to redirect TCP SYN / UDP packet to a connected socket */
1171     client_fd = make_client(sotype, EXT_IP4, EXT_PORT);
1172     if (client_fd < 0)
1173         goto out_unlink_prog;
1174     if (sotype == SOCK_DGRAM) {
1175         send_byte(client_fd);
1176         recv_byte(server_fd);
1177     }
1178 
1179     close(client_fd);
1180 out_unlink_prog:
1181     bpf_link__destroy(lookup_link);
1182 out_close_connected:
1183     close(connected_fd);
1184 out_close_server:
1185     close(server_fd);
1186 }
1187 
1188 static void test_sk_assign_helper(struct test_sk_lookup *skel)
1189 {
1190     if (test__start_subtest("sk_assign returns EEXIST"))
1191         run_sk_assign_v4(skel, skel->progs.sk_assign_eexist);
1192     if (test__start_subtest("sk_assign honors F_REPLACE"))
1193         run_sk_assign_v4(skel, skel->progs.sk_assign_replace_flag);
1194     if (test__start_subtest("sk_assign accepts NULL socket"))
1195         run_sk_assign_v4(skel, skel->progs.sk_assign_null);
1196     if (test__start_subtest("access ctx->sk"))
1197         run_sk_assign_v4(skel, skel->progs.access_ctx_sk);
1198     if (test__start_subtest("narrow access to ctx v4"))
1199         run_sk_assign_v4(skel, skel->progs.ctx_narrow_access);
1200     if (test__start_subtest("narrow access to ctx v6"))
1201         run_sk_assign_v6(skel, skel->progs.ctx_narrow_access);
1202     if (test__start_subtest("sk_assign rejects TCP established"))
1203         run_sk_assign_connected(skel, SOCK_STREAM);
1204     if (test__start_subtest("sk_assign rejects UDP connected"))
1205         run_sk_assign_connected(skel, SOCK_DGRAM);
1206 }
1207 
1208 struct test_multi_prog {
1209     const char *desc;
1210     struct bpf_program *prog1;
1211     struct bpf_program *prog2;
1212     struct bpf_map *redir_map;
1213     struct bpf_map *run_map;
1214     int expect_errno;
1215     struct inet_addr listen_at;
1216 };
1217 
1218 static void run_multi_prog_lookup(const struct test_multi_prog *t)
1219 {
1220     struct sockaddr_storage dst = {};
1221     int map_fd, server_fd, client_fd;
1222     struct bpf_link *link1, *link2;
1223     int prog_idx, done, err;
1224 
1225     map_fd = bpf_map__fd(t->run_map);
1226 
1227     done = 0;
1228     prog_idx = PROG1;
1229     err = bpf_map_update_elem(map_fd, &prog_idx, &done, BPF_ANY);
1230     if (CHECK(err, "bpf_map_update_elem", "failed\n"))
1231         return;
1232     prog_idx = PROG2;
1233     err = bpf_map_update_elem(map_fd, &prog_idx, &done, BPF_ANY);
1234     if (CHECK(err, "bpf_map_update_elem", "failed\n"))
1235         return;
1236 
1237     link1 = attach_lookup_prog(t->prog1);
1238     if (!link1)
1239         return;
1240     link2 = attach_lookup_prog(t->prog2);
1241     if (!link2)
1242         goto out_unlink1;
1243 
1244     server_fd = make_server(SOCK_STREAM, t->listen_at.ip,
1245                 t->listen_at.port, NULL);
1246     if (server_fd < 0)
1247         goto out_unlink2;
1248 
1249     err = update_lookup_map(t->redir_map, SERVER_A, server_fd);
1250     if (err)
1251         goto out_close_server;
1252 
1253     client_fd = make_socket(SOCK_STREAM, EXT_IP4, EXT_PORT, &dst);
1254     if (client_fd < 0)
1255         goto out_close_server;
1256 
1257     err = connect(client_fd, (void *)&dst, inetaddr_len(&dst));
1258     if (CHECK(err && !t->expect_errno, "connect",
1259           "unexpected error %d\n", errno))
1260         goto out_close_client;
1261     if (CHECK(err && t->expect_errno && errno != t->expect_errno,
1262           "connect", "unexpected error %d\n", errno))
1263         goto out_close_client;
1264 
1265     done = 0;
1266     prog_idx = PROG1;
1267     err = bpf_map_lookup_elem(map_fd, &prog_idx, &done);
1268     CHECK(err, "bpf_map_lookup_elem", "failed\n");
1269     CHECK(!done, "bpf_map_lookup_elem", "PROG1 !done\n");
1270 
1271     done = 0;
1272     prog_idx = PROG2;
1273     err = bpf_map_lookup_elem(map_fd, &prog_idx, &done);
1274     CHECK(err, "bpf_map_lookup_elem", "failed\n");
1275     CHECK(!done, "bpf_map_lookup_elem", "PROG2 !done\n");
1276 
1277 out_close_client:
1278     close(client_fd);
1279 out_close_server:
1280     close(server_fd);
1281 out_unlink2:
1282     bpf_link__destroy(link2);
1283 out_unlink1:
1284     bpf_link__destroy(link1);
1285 }
1286 
1287 static void test_multi_prog_lookup(struct test_sk_lookup *skel)
1288 {
1289     struct test_multi_prog tests[] = {
1290         {
1291             .desc       = "multi prog - pass, pass",
1292             .prog1      = skel->progs.multi_prog_pass1,
1293             .prog2      = skel->progs.multi_prog_pass2,
1294             .listen_at  = { EXT_IP4, EXT_PORT },
1295         },
1296         {
1297             .desc       = "multi prog - drop, drop",
1298             .prog1      = skel->progs.multi_prog_drop1,
1299             .prog2      = skel->progs.multi_prog_drop2,
1300             .listen_at  = { EXT_IP4, EXT_PORT },
1301             .expect_errno   = ECONNREFUSED,
1302         },
1303         {
1304             .desc       = "multi prog - pass, drop",
1305             .prog1      = skel->progs.multi_prog_pass1,
1306             .prog2      = skel->progs.multi_prog_drop2,
1307             .listen_at  = { EXT_IP4, EXT_PORT },
1308             .expect_errno   = ECONNREFUSED,
1309         },
1310         {
1311             .desc       = "multi prog - drop, pass",
1312             .prog1      = skel->progs.multi_prog_drop1,
1313             .prog2      = skel->progs.multi_prog_pass2,
1314             .listen_at  = { EXT_IP4, EXT_PORT },
1315             .expect_errno   = ECONNREFUSED,
1316         },
1317         {
1318             .desc       = "multi prog - pass, redir",
1319             .prog1      = skel->progs.multi_prog_pass1,
1320             .prog2      = skel->progs.multi_prog_redir2,
1321             .listen_at  = { INT_IP4, INT_PORT },
1322         },
1323         {
1324             .desc       = "multi prog - redir, pass",
1325             .prog1      = skel->progs.multi_prog_redir1,
1326             .prog2      = skel->progs.multi_prog_pass2,
1327             .listen_at  = { INT_IP4, INT_PORT },
1328         },
1329         {
1330             .desc       = "multi prog - drop, redir",
1331             .prog1      = skel->progs.multi_prog_drop1,
1332             .prog2      = skel->progs.multi_prog_redir2,
1333             .listen_at  = { INT_IP4, INT_PORT },
1334         },
1335         {
1336             .desc       = "multi prog - redir, drop",
1337             .prog1      = skel->progs.multi_prog_redir1,
1338             .prog2      = skel->progs.multi_prog_drop2,
1339             .listen_at  = { INT_IP4, INT_PORT },
1340         },
1341         {
1342             .desc       = "multi prog - redir, redir",
1343             .prog1      = skel->progs.multi_prog_redir1,
1344             .prog2      = skel->progs.multi_prog_redir2,
1345             .listen_at  = { INT_IP4, INT_PORT },
1346         },
1347     };
1348     struct test_multi_prog *t;
1349 
1350     for (t = tests; t < tests + ARRAY_SIZE(tests); t++) {
1351         t->redir_map = skel->maps.redir_map;
1352         t->run_map = skel->maps.run_map;
1353         if (test__start_subtest(t->desc))
1354             run_multi_prog_lookup(t);
1355     }
1356 }
1357 
1358 static void run_tests(struct test_sk_lookup *skel)
1359 {
1360     if (test__start_subtest("query lookup prog"))
1361         query_lookup_prog(skel);
1362     test_redirect_lookup(skel);
1363     test_drop_on_lookup(skel);
1364     test_drop_on_reuseport(skel);
1365     test_sk_assign_helper(skel);
1366     test_multi_prog_lookup(skel);
1367 }
1368 
1369 static int switch_netns(void)
1370 {
1371     static const char * const setup_script[] = {
1372         "ip -6 addr add dev lo " EXT_IP6 "/128",
1373         "ip -6 addr add dev lo " INT_IP6 "/128",
1374         "ip link set dev lo up",
1375         NULL,
1376     };
1377     const char * const *cmd;
1378     int err;
1379 
1380     err = unshare(CLONE_NEWNET);
1381     if (CHECK(err, "unshare", "failed\n")) {
1382         log_err("unshare(CLONE_NEWNET)");
1383         return -1;
1384     }
1385 
1386     for (cmd = setup_script; *cmd; cmd++) {
1387         err = system(*cmd);
1388         if (CHECK(err, "system", "failed\n")) {
1389             log_err("system(%s)", *cmd);
1390             return -1;
1391         }
1392     }
1393 
1394     return 0;
1395 }
1396 
1397 void test_sk_lookup(void)
1398 {
1399     struct test_sk_lookup *skel;
1400     int err;
1401 
1402     err = switch_netns();
1403     if (err)
1404         return;
1405 
1406     skel = test_sk_lookup__open_and_load();
1407     if (CHECK(!skel, "skel open_and_load", "failed\n"))
1408         return;
1409 
1410     run_tests(skel);
1411 
1412     test_sk_lookup__destroy(skel);
1413 }