linux/tools/testing/selftests/bpf/prog_tests/cls_redirect.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2// Copyright (c) 2020 Cloudflare
   3
   4#define _GNU_SOURCE
   5
   6#include <arpa/inet.h>
   7#include <string.h>
   8
   9#include <linux/pkt_cls.h>
  10
  11#include <test_progs.h>
  12
  13#include "progs/test_cls_redirect.h"
  14#include "test_cls_redirect.skel.h"
  15
  16#define ENCAP_IP INADDR_LOOPBACK
  17#define ENCAP_PORT (1234)
  18
  19struct addr_port {
  20        in_port_t port;
  21        union {
  22                struct in_addr in_addr;
  23                struct in6_addr in6_addr;
  24        };
  25};
  26
  27struct tuple {
  28        int family;
  29        struct addr_port src;
  30        struct addr_port dst;
  31};
  32
  33static int start_server(const struct sockaddr *addr, socklen_t len, int type)
  34{
  35        int fd = socket(addr->sa_family, type, 0);
  36        if (CHECK_FAIL(fd == -1))
  37                return -1;
  38        if (CHECK_FAIL(bind(fd, addr, len) == -1))
  39                goto err;
  40        if (type == SOCK_STREAM && CHECK_FAIL(listen(fd, 128) == -1))
  41                goto err;
  42
  43        return fd;
  44
  45err:
  46        close(fd);
  47        return -1;
  48}
  49
  50static int connect_to_server(const struct sockaddr *addr, socklen_t len,
  51                             int type)
  52{
  53        int fd = socket(addr->sa_family, type, 0);
  54        if (CHECK_FAIL(fd == -1))
  55                return -1;
  56        if (CHECK_FAIL(connect(fd, addr, len)))
  57                goto err;
  58
  59        return fd;
  60
  61err:
  62        close(fd);
  63        return -1;
  64}
  65
  66static bool fill_addr_port(const struct sockaddr *sa, struct addr_port *ap)
  67{
  68        const struct sockaddr_in6 *in6;
  69        const struct sockaddr_in *in;
  70
  71        switch (sa->sa_family) {
  72        case AF_INET:
  73                in = (const struct sockaddr_in *)sa;
  74                ap->in_addr = in->sin_addr;
  75                ap->port = in->sin_port;
  76                return true;
  77
  78        case AF_INET6:
  79                in6 = (const struct sockaddr_in6 *)sa;
  80                ap->in6_addr = in6->sin6_addr;
  81                ap->port = in6->sin6_port;
  82                return true;
  83
  84        default:
  85                return false;
  86        }
  87}
  88
  89static bool set_up_conn(const struct sockaddr *addr, socklen_t len, int type,
  90                        int *server, int *conn, struct tuple *tuple)
  91{
  92        struct sockaddr_storage ss;
  93        socklen_t slen = sizeof(ss);
  94        struct sockaddr *sa = (struct sockaddr *)&ss;
  95
  96        *server = start_server(addr, len, type);
  97        if (*server < 0)
  98                return false;
  99
 100        if (CHECK_FAIL(getsockname(*server, sa, &slen)))
 101                goto close_server;
 102
 103        *conn = connect_to_server(sa, slen, type);
 104        if (*conn < 0)
 105                goto close_server;
 106
 107        /* We want to simulate packets arriving at conn, so we have to
 108         * swap src and dst.
 109         */
 110        slen = sizeof(ss);
 111        if (CHECK_FAIL(getsockname(*conn, sa, &slen)))
 112                goto close_conn;
 113
 114        if (CHECK_FAIL(!fill_addr_port(sa, &tuple->dst)))
 115                goto close_conn;
 116
 117        slen = sizeof(ss);
 118        if (CHECK_FAIL(getpeername(*conn, sa, &slen)))
 119                goto close_conn;
 120
 121        if (CHECK_FAIL(!fill_addr_port(sa, &tuple->src)))
 122                goto close_conn;
 123
 124        tuple->family = ss.ss_family;
 125        return true;
 126
 127close_conn:
 128        close(*conn);
 129        *conn = -1;
 130close_server:
 131        close(*server);
 132        *server = -1;
 133        return false;
 134}
 135
 136static socklen_t prepare_addr(struct sockaddr_storage *addr, int family)
 137{
 138        struct sockaddr_in *addr4;
 139        struct sockaddr_in6 *addr6;
 140
 141        switch (family) {
 142        case AF_INET:
 143                addr4 = (struct sockaddr_in *)addr;
 144                memset(addr4, 0, sizeof(*addr4));
 145                addr4->sin_family = family;
 146                addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
 147                return sizeof(*addr4);
 148        case AF_INET6:
 149                addr6 = (struct sockaddr_in6 *)addr;
 150                memset(addr6, 0, sizeof(*addr6));
 151                addr6->sin6_family = family;
 152                addr6->sin6_addr = in6addr_loopback;
 153                return sizeof(*addr6);
 154        default:
 155                fprintf(stderr, "Invalid family %d", family);
 156                return 0;
 157        }
 158}
 159
 160static bool was_decapsulated(struct bpf_prog_test_run_attr *tattr)
 161{
 162        return tattr->data_size_out < tattr->data_size_in;
 163}
 164
 165enum type {
 166        UDP,
 167        TCP,
 168        __NR_KIND,
 169};
 170
 171enum hops {
 172        NO_HOPS,
 173        ONE_HOP,
 174};
 175
 176enum flags {
 177        NONE,
 178        SYN,
 179        ACK,
 180};
 181
 182enum conn {
 183        KNOWN_CONN,
 184        UNKNOWN_CONN,
 185};
 186
 187enum result {
 188        ACCEPT,
 189        FORWARD,
 190};
 191
 192struct test_cfg {
 193        enum type type;
 194        enum result result;
 195        enum conn conn;
 196        enum hops hops;
 197        enum flags flags;
 198};
 199
 200static int test_str(void *buf, size_t len, const struct test_cfg *test,
 201                    int family)
 202{
 203        const char *family_str, *type, *conn, *hops, *result, *flags;
 204
 205        family_str = "IPv4";
 206        if (family == AF_INET6)
 207                family_str = "IPv6";
 208
 209        type = "TCP";
 210        if (test->type == UDP)
 211                type = "UDP";
 212
 213        conn = "known";
 214        if (test->conn == UNKNOWN_CONN)
 215                conn = "unknown";
 216
 217        hops = "no hops";
 218        if (test->hops == ONE_HOP)
 219                hops = "one hop";
 220
 221        result = "accept";
 222        if (test->result == FORWARD)
 223                result = "forward";
 224
 225        flags = "none";
 226        if (test->flags == SYN)
 227                flags = "SYN";
 228        else if (test->flags == ACK)
 229                flags = "ACK";
 230
 231        return snprintf(buf, len, "%s %s %s %s (%s, flags: %s)", family_str,
 232                        type, result, conn, hops, flags);
 233}
 234
 235static struct test_cfg tests[] = {
 236        { TCP, ACCEPT, UNKNOWN_CONN, NO_HOPS, SYN },
 237        { TCP, ACCEPT, UNKNOWN_CONN, NO_HOPS, ACK },
 238        { TCP, FORWARD, UNKNOWN_CONN, ONE_HOP, ACK },
 239        { TCP, ACCEPT, KNOWN_CONN, ONE_HOP, ACK },
 240        { UDP, ACCEPT, UNKNOWN_CONN, NO_HOPS, NONE },
 241        { UDP, FORWARD, UNKNOWN_CONN, ONE_HOP, NONE },
 242        { UDP, ACCEPT, KNOWN_CONN, ONE_HOP, NONE },
 243};
 244
 245static void encap_init(encap_headers_t *encap, uint8_t hop_count, uint8_t proto)
 246{
 247        const uint8_t hlen =
 248                (sizeof(struct guehdr) / sizeof(uint32_t)) + hop_count;
 249        *encap = (encap_headers_t){
 250                .eth = { .h_proto = htons(ETH_P_IP) },
 251                .ip = {
 252                        .ihl = 5,
 253                        .version = 4,
 254                        .ttl = IPDEFTTL,
 255                        .protocol = IPPROTO_UDP,
 256                        .daddr = htonl(ENCAP_IP)
 257                },
 258                .udp = {
 259                        .dest = htons(ENCAP_PORT),
 260                },
 261                .gue = {
 262                        .hlen = hlen,
 263                        .proto_ctype = proto
 264                },
 265                .unigue = {
 266                        .hop_count = hop_count
 267                },
 268        };
 269}
 270
 271static size_t build_input(const struct test_cfg *test, void *const buf,
 272                          const struct tuple *tuple)
 273{
 274        in_port_t sport = tuple->src.port;
 275        encap_headers_t encap;
 276        struct iphdr ip;
 277        struct ipv6hdr ipv6;
 278        struct tcphdr tcp;
 279        struct udphdr udp;
 280        struct in_addr next_hop;
 281        uint8_t *p = buf;
 282        int proto;
 283
 284        proto = IPPROTO_IPIP;
 285        if (tuple->family == AF_INET6)
 286                proto = IPPROTO_IPV6;
 287
 288        encap_init(&encap, test->hops == ONE_HOP ? 1 : 0, proto);
 289        p = mempcpy(p, &encap, sizeof(encap));
 290
 291        if (test->hops == ONE_HOP) {
 292                next_hop = (struct in_addr){ .s_addr = htonl(0x7f000002) };
 293                p = mempcpy(p, &next_hop, sizeof(next_hop));
 294        }
 295
 296        proto = IPPROTO_TCP;
 297        if (test->type == UDP)
 298                proto = IPPROTO_UDP;
 299
 300        switch (tuple->family) {
 301        case AF_INET:
 302                ip = (struct iphdr){
 303                        .ihl = 5,
 304                        .version = 4,
 305                        .ttl = IPDEFTTL,
 306                        .protocol = proto,
 307                        .saddr = tuple->src.in_addr.s_addr,
 308                        .daddr = tuple->dst.in_addr.s_addr,
 309                };
 310                p = mempcpy(p, &ip, sizeof(ip));
 311                break;
 312        case AF_INET6:
 313                ipv6 = (struct ipv6hdr){
 314                        .version = 6,
 315                        .hop_limit = IPDEFTTL,
 316                        .nexthdr = proto,
 317                        .saddr = tuple->src.in6_addr,
 318                        .daddr = tuple->dst.in6_addr,
 319                };
 320                p = mempcpy(p, &ipv6, sizeof(ipv6));
 321                break;
 322        default:
 323                return 0;
 324        }
 325
 326        if (test->conn == UNKNOWN_CONN)
 327                sport--;
 328
 329        switch (test->type) {
 330        case TCP:
 331                tcp = (struct tcphdr){
 332                        .source = sport,
 333                        .dest = tuple->dst.port,
 334                };
 335                if (test->flags == SYN)
 336                        tcp.syn = true;
 337                if (test->flags == ACK)
 338                        tcp.ack = true;
 339                p = mempcpy(p, &tcp, sizeof(tcp));
 340                break;
 341        case UDP:
 342                udp = (struct udphdr){
 343                        .source = sport,
 344                        .dest = tuple->dst.port,
 345                };
 346                p = mempcpy(p, &udp, sizeof(udp));
 347                break;
 348        default:
 349                return 0;
 350        }
 351
 352        return (void *)p - buf;
 353}
 354
 355static void close_fds(int *fds, int n)
 356{
 357        int i;
 358
 359        for (i = 0; i < n; i++)
 360                if (fds[i] > 0)
 361                        close(fds[i]);
 362}
 363
 364void test_cls_redirect(void)
 365{
 366        struct test_cls_redirect *skel = NULL;
 367        struct bpf_prog_test_run_attr tattr = {};
 368        int families[] = { AF_INET, AF_INET6 };
 369        struct sockaddr_storage ss;
 370        struct sockaddr *addr;
 371        socklen_t slen;
 372        int i, j, err;
 373
 374        int servers[__NR_KIND][ARRAY_SIZE(families)] = {};
 375        int conns[__NR_KIND][ARRAY_SIZE(families)] = {};
 376        struct tuple tuples[__NR_KIND][ARRAY_SIZE(families)];
 377
 378        skel = test_cls_redirect__open();
 379        if (CHECK_FAIL(!skel))
 380                return;
 381
 382        skel->rodata->ENCAPSULATION_IP = htonl(ENCAP_IP);
 383        skel->rodata->ENCAPSULATION_PORT = htons(ENCAP_PORT);
 384
 385        if (CHECK_FAIL(test_cls_redirect__load(skel)))
 386                goto cleanup;
 387
 388        addr = (struct sockaddr *)&ss;
 389        for (i = 0; i < ARRAY_SIZE(families); i++) {
 390                slen = prepare_addr(&ss, families[i]);
 391                if (CHECK_FAIL(!slen))
 392                        goto cleanup;
 393
 394                if (CHECK_FAIL(!set_up_conn(addr, slen, SOCK_DGRAM,
 395                                            &servers[UDP][i], &conns[UDP][i],
 396                                            &tuples[UDP][i])))
 397                        goto cleanup;
 398
 399                if (CHECK_FAIL(!set_up_conn(addr, slen, SOCK_STREAM,
 400                                            &servers[TCP][i], &conns[TCP][i],
 401                                            &tuples[TCP][i])))
 402                        goto cleanup;
 403        }
 404
 405        tattr.prog_fd = bpf_program__fd(skel->progs.cls_redirect);
 406        for (i = 0; i < ARRAY_SIZE(tests); i++) {
 407                struct test_cfg *test = &tests[i];
 408
 409                for (j = 0; j < ARRAY_SIZE(families); j++) {
 410                        struct tuple *tuple = &tuples[test->type][j];
 411                        char input[256];
 412                        char tmp[256];
 413
 414                        test_str(tmp, sizeof(tmp), test, tuple->family);
 415                        if (!test__start_subtest(tmp))
 416                                continue;
 417
 418                        tattr.data_out = tmp;
 419                        tattr.data_size_out = sizeof(tmp);
 420
 421                        tattr.data_in = input;
 422                        tattr.data_size_in = build_input(test, input, tuple);
 423                        if (CHECK_FAIL(!tattr.data_size_in))
 424                                continue;
 425
 426                        err = bpf_prog_test_run_xattr(&tattr);
 427                        if (CHECK_FAIL(err))
 428                                continue;
 429
 430                        if (tattr.retval != TC_ACT_REDIRECT) {
 431                                PRINT_FAIL("expected TC_ACT_REDIRECT, got %d\n",
 432                                           tattr.retval);
 433                                continue;
 434                        }
 435
 436                        switch (test->result) {
 437                        case ACCEPT:
 438                                if (CHECK_FAIL(!was_decapsulated(&tattr)))
 439                                        continue;
 440                                break;
 441                        case FORWARD:
 442                                if (CHECK_FAIL(was_decapsulated(&tattr)))
 443                                        continue;
 444                                break;
 445                        default:
 446                                PRINT_FAIL("unknown result %d\n", test->result);
 447                                continue;
 448                        }
 449                }
 450        }
 451
 452cleanup:
 453        test_cls_redirect__destroy(skel);
 454        close_fds((int *)servers, sizeof(servers) / sizeof(servers[0][0]));
 455        close_fds((int *)conns, sizeof(conns) / sizeof(conns[0][0]));
 456}
 457