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