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