linux/tools/testing/selftests/bpf/prog_tests/sk_assign.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (c) 2018 Facebook
   3// Copyright (c) 2019 Cloudflare
   4// Copyright (c) 2020 Isovalent, Inc.
   5/*
   6 * Test that the socket assign program is able to redirect traffic towards a
   7 * socket, regardless of whether the port or address destination of the traffic
   8 * matches the port.
   9 */
  10
  11#define _GNU_SOURCE
  12#include <fcntl.h>
  13#include <signal.h>
  14#include <stdlib.h>
  15#include <unistd.h>
  16
  17#include "test_progs.h"
  18
  19#define BIND_PORT 1234
  20#define CONNECT_PORT 4321
  21#define TEST_DADDR (0xC0A80203)
  22#define NS_SELF "/proc/self/ns/net"
  23#define SERVER_MAP_PATH "/sys/fs/bpf/tc/globals/server_map"
  24
  25static const struct timeval timeo_sec = { .tv_sec = 3 };
  26static const size_t timeo_optlen = sizeof(timeo_sec);
  27static int stop, duration;
  28
  29static bool
  30configure_stack(void)
  31{
  32        char tc_cmd[BUFSIZ];
  33
  34        /* Move to a new networking namespace */
  35        if (CHECK_FAIL(unshare(CLONE_NEWNET)))
  36                return false;
  37
  38        /* Configure necessary links, routes */
  39        if (CHECK_FAIL(system("ip link set dev lo up")))
  40                return false;
  41        if (CHECK_FAIL(system("ip route add local default dev lo")))
  42                return false;
  43        if (CHECK_FAIL(system("ip -6 route add local default dev lo")))
  44                return false;
  45
  46        /* Load qdisc, BPF program */
  47        if (CHECK_FAIL(system("tc qdisc add dev lo clsact")))
  48                return false;
  49        sprintf(tc_cmd, "%s %s %s %s", "tc filter add dev lo ingress bpf",
  50                       "direct-action object-file ./test_sk_assign.o",
  51                       "section classifier/sk_assign_test",
  52                       (env.verbosity < VERBOSE_VERY) ? " 2>/dev/null" : "");
  53        if (CHECK(system(tc_cmd), "BPF load failed;",
  54                  "run with -vv for more info\n"))
  55                return false;
  56
  57        return true;
  58}
  59
  60static int
  61start_server(const struct sockaddr *addr, socklen_t len, int type)
  62{
  63        int fd;
  64
  65        fd = socket(addr->sa_family, type, 0);
  66        if (CHECK_FAIL(fd == -1))
  67                goto out;
  68        if (CHECK_FAIL(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo_sec,
  69                                  timeo_optlen)))
  70                goto close_out;
  71        if (CHECK_FAIL(bind(fd, addr, len) == -1))
  72                goto close_out;
  73        if (type == SOCK_STREAM && CHECK_FAIL(listen(fd, 128) == -1))
  74                goto close_out;
  75
  76        goto out;
  77close_out:
  78        close(fd);
  79        fd = -1;
  80out:
  81        return fd;
  82}
  83
  84static int
  85connect_to_server(const struct sockaddr *addr, socklen_t len, int type)
  86{
  87        int fd = -1;
  88
  89        fd = socket(addr->sa_family, type, 0);
  90        if (CHECK_FAIL(fd == -1))
  91                goto out;
  92        if (CHECK_FAIL(setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo_sec,
  93                                  timeo_optlen)))
  94                goto close_out;
  95        if (CHECK_FAIL(connect(fd, addr, len)))
  96                goto close_out;
  97
  98        goto out;
  99close_out:
 100        close(fd);
 101        fd = -1;
 102out:
 103        return fd;
 104}
 105
 106static in_port_t
 107get_port(int fd)
 108{
 109        struct sockaddr_storage ss;
 110        socklen_t slen = sizeof(ss);
 111        in_port_t port = 0;
 112
 113        if (CHECK_FAIL(getsockname(fd, (struct sockaddr *)&ss, &slen)))
 114                return port;
 115
 116        switch (ss.ss_family) {
 117        case AF_INET:
 118                port = ((struct sockaddr_in *)&ss)->sin_port;
 119                break;
 120        case AF_INET6:
 121                port = ((struct sockaddr_in6 *)&ss)->sin6_port;
 122                break;
 123        default:
 124                CHECK(1, "Invalid address family", "%d\n", ss.ss_family);
 125        }
 126        return port;
 127}
 128
 129static ssize_t
 130rcv_msg(int srv_client, int type)
 131{
 132        struct sockaddr_storage ss;
 133        char buf[BUFSIZ];
 134        socklen_t slen;
 135
 136        if (type == SOCK_STREAM)
 137                return read(srv_client, &buf, sizeof(buf));
 138        else
 139                return recvfrom(srv_client, &buf, sizeof(buf), 0,
 140                                (struct sockaddr *)&ss, &slen);
 141}
 142
 143static int
 144run_test(int server_fd, const struct sockaddr *addr, socklen_t len, int type)
 145{
 146        int client = -1, srv_client = -1;
 147        char buf[] = "testing";
 148        in_port_t port;
 149        int ret = 1;
 150
 151        client = connect_to_server(addr, len, type);
 152        if (client == -1) {
 153                perror("Cannot connect to server");
 154                goto out;
 155        }
 156
 157        if (type == SOCK_STREAM) {
 158                srv_client = accept(server_fd, NULL, NULL);
 159                if (CHECK_FAIL(srv_client == -1)) {
 160                        perror("Can't accept connection");
 161                        goto out;
 162                }
 163        } else {
 164                srv_client = server_fd;
 165        }
 166        if (CHECK_FAIL(write(client, buf, sizeof(buf)) != sizeof(buf))) {
 167                perror("Can't write on client");
 168                goto out;
 169        }
 170        if (CHECK_FAIL(rcv_msg(srv_client, type) != sizeof(buf))) {
 171                perror("Can't read on server");
 172                goto out;
 173        }
 174
 175        port = get_port(srv_client);
 176        if (CHECK_FAIL(!port))
 177                goto out;
 178        /* SOCK_STREAM is connected via accept(), so the server's local address
 179         * will be the CONNECT_PORT rather than the BIND port that corresponds
 180         * to the listen socket. SOCK_DGRAM on the other hand is connectionless
 181         * so we can't really do the same check there; the server doesn't ever
 182         * create a socket with CONNECT_PORT.
 183         */
 184        if (type == SOCK_STREAM &&
 185            CHECK(port != htons(CONNECT_PORT), "Expected", "port %u but got %u",
 186                  CONNECT_PORT, ntohs(port)))
 187                goto out;
 188        else if (type == SOCK_DGRAM &&
 189                 CHECK(port != htons(BIND_PORT), "Expected",
 190                       "port %u but got %u", BIND_PORT, ntohs(port)))
 191                goto out;
 192
 193        ret = 0;
 194out:
 195        close(client);
 196        if (srv_client != server_fd)
 197                close(srv_client);
 198        if (ret)
 199                WRITE_ONCE(stop, 1);
 200        return ret;
 201}
 202
 203static void
 204prepare_addr(struct sockaddr *addr, int family, __u16 port, bool rewrite_addr)
 205{
 206        struct sockaddr_in *addr4;
 207        struct sockaddr_in6 *addr6;
 208
 209        switch (family) {
 210        case AF_INET:
 211                addr4 = (struct sockaddr_in *)addr;
 212                memset(addr4, 0, sizeof(*addr4));
 213                addr4->sin_family = family;
 214                addr4->sin_port = htons(port);
 215                if (rewrite_addr)
 216                        addr4->sin_addr.s_addr = htonl(TEST_DADDR);
 217                else
 218                        addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
 219                break;
 220        case AF_INET6:
 221                addr6 = (struct sockaddr_in6 *)addr;
 222                memset(addr6, 0, sizeof(*addr6));
 223                addr6->sin6_family = family;
 224                addr6->sin6_port = htons(port);
 225                addr6->sin6_addr = in6addr_loopback;
 226                if (rewrite_addr)
 227                        addr6->sin6_addr.s6_addr32[3] = htonl(TEST_DADDR);
 228                break;
 229        default:
 230                fprintf(stderr, "Invalid family %d", family);
 231        }
 232}
 233
 234struct test_sk_cfg {
 235        const char *name;
 236        int family;
 237        struct sockaddr *addr;
 238        socklen_t len;
 239        int type;
 240        bool rewrite_addr;
 241};
 242
 243#define TEST(NAME, FAMILY, TYPE, REWRITE)                               \
 244{                                                                       \
 245        .name = NAME,                                                   \
 246        .family = FAMILY,                                               \
 247        .addr = (FAMILY == AF_INET) ? (struct sockaddr *)&addr4         \
 248                                    : (struct sockaddr *)&addr6,        \
 249        .len = (FAMILY == AF_INET) ? sizeof(addr4) : sizeof(addr6),     \
 250        .type = TYPE,                                                   \
 251        .rewrite_addr = REWRITE,                                        \
 252}
 253
 254void test_sk_assign(void)
 255{
 256        struct sockaddr_in addr4;
 257        struct sockaddr_in6 addr6;
 258        struct test_sk_cfg tests[] = {
 259                TEST("ipv4 tcp port redir", AF_INET, SOCK_STREAM, false),
 260                TEST("ipv4 tcp addr redir", AF_INET, SOCK_STREAM, true),
 261                TEST("ipv6 tcp port redir", AF_INET6, SOCK_STREAM, false),
 262                TEST("ipv6 tcp addr redir", AF_INET6, SOCK_STREAM, true),
 263                TEST("ipv4 udp port redir", AF_INET, SOCK_DGRAM, false),
 264                TEST("ipv4 udp addr redir", AF_INET, SOCK_DGRAM, true),
 265                TEST("ipv6 udp port redir", AF_INET6, SOCK_DGRAM, false),
 266                TEST("ipv6 udp addr redir", AF_INET6, SOCK_DGRAM, true),
 267        };
 268        int server = -1;
 269        int server_map;
 270        int self_net;
 271
 272        self_net = open(NS_SELF, O_RDONLY);
 273        if (CHECK_FAIL(self_net < 0)) {
 274                perror("Unable to open "NS_SELF);
 275                return;
 276        }
 277
 278        if (!configure_stack()) {
 279                perror("configure_stack");
 280                goto cleanup;
 281        }
 282
 283        server_map = bpf_obj_get(SERVER_MAP_PATH);
 284        if (CHECK_FAIL(server_map < 0)) {
 285                perror("Unable to open " SERVER_MAP_PATH);
 286                goto cleanup;
 287        }
 288
 289        for (int i = 0; i < ARRAY_SIZE(tests) && !READ_ONCE(stop); i++) {
 290                struct test_sk_cfg *test = &tests[i];
 291                const struct sockaddr *addr;
 292                const int zero = 0;
 293                int err;
 294
 295                if (!test__start_subtest(test->name))
 296                        continue;
 297                prepare_addr(test->addr, test->family, BIND_PORT, false);
 298                addr = (const struct sockaddr *)test->addr;
 299                server = start_server(addr, test->len, test->type);
 300                if (server == -1)
 301                        goto close;
 302
 303                err = bpf_map_update_elem(server_map, &zero, &server, BPF_ANY);
 304                if (CHECK_FAIL(err)) {
 305                        perror("Unable to update server_map");
 306                        goto close;
 307                }
 308
 309                /* connect to unbound ports */
 310                prepare_addr(test->addr, test->family, CONNECT_PORT,
 311                             test->rewrite_addr);
 312                if (run_test(server, addr, test->len, test->type))
 313                        goto close;
 314
 315                close(server);
 316                server = -1;
 317        }
 318
 319close:
 320        close(server);
 321        close(server_map);
 322cleanup:
 323        if (CHECK_FAIL(unlink(SERVER_MAP_PATH)))
 324                perror("Unable to unlink " SERVER_MAP_PATH);
 325        if (CHECK_FAIL(setns(self_net, CLONE_NEWNET)))
 326                perror("Failed to setns("NS_SELF")");
 327        close(self_net);
 328}
 329