linux/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Copyright (c) 2020 Facebook */
   3
   4#define _GNU_SOURCE
   5#include <sched.h>
   6#include <stdio.h>
   7#include <stdlib.h>
   8#include <sys/socket.h>
   9#include <linux/compiler.h>
  10
  11#include "test_progs.h"
  12#include "cgroup_helpers.h"
  13#include "network_helpers.h"
  14#include "test_tcp_hdr_options.h"
  15#include "test_tcp_hdr_options.skel.h"
  16#include "test_misc_tcp_hdr_options.skel.h"
  17
  18#define LO_ADDR6 "::1"
  19#define CG_NAME "/tcpbpf-hdr-opt-test"
  20
  21static struct bpf_test_option exp_passive_estab_in;
  22static struct bpf_test_option exp_active_estab_in;
  23static struct bpf_test_option exp_passive_fin_in;
  24static struct bpf_test_option exp_active_fin_in;
  25static struct hdr_stg exp_passive_hdr_stg;
  26static struct hdr_stg exp_active_hdr_stg = { .active = true, };
  27
  28static struct test_misc_tcp_hdr_options *misc_skel;
  29static struct test_tcp_hdr_options *skel;
  30static int lport_linum_map_fd;
  31static int hdr_stg_map_fd;
  32static __u32 duration;
  33static int cg_fd;
  34
  35struct sk_fds {
  36        int srv_fd;
  37        int passive_fd;
  38        int active_fd;
  39        int passive_lport;
  40        int active_lport;
  41};
  42
  43static int create_netns(void)
  44{
  45        if (CHECK(unshare(CLONE_NEWNET), "create netns",
  46                  "unshare(CLONE_NEWNET): %s (%d)",
  47                  strerror(errno), errno))
  48                return -1;
  49
  50        if (CHECK(system("ip link set dev lo up"), "run ip cmd",
  51                  "failed to bring lo link up\n"))
  52                return -1;
  53
  54        return 0;
  55}
  56
  57static int write_sysctl(const char *sysctl, const char *value)
  58{
  59        int fd, err, len;
  60
  61        fd = open(sysctl, O_WRONLY);
  62        if (CHECK(fd == -1, "open sysctl", "open(%s): %s (%d)\n",
  63                  sysctl, strerror(errno), errno))
  64                return -1;
  65
  66        len = strlen(value);
  67        err = write(fd, value, len);
  68        close(fd);
  69        if (CHECK(err != len, "write sysctl",
  70                  "write(%s, %s): err:%d %s (%d)\n",
  71                  sysctl, value, err, strerror(errno), errno))
  72                return -1;
  73
  74        return 0;
  75}
  76
  77static void print_hdr_stg(const struct hdr_stg *hdr_stg, const char *prefix)
  78{
  79        fprintf(stderr, "%s{active:%u, resend_syn:%u, syncookie:%u, fastopen:%u}\n",
  80                prefix ? : "", hdr_stg->active, hdr_stg->resend_syn,
  81                hdr_stg->syncookie, hdr_stg->fastopen);
  82}
  83
  84static void print_option(const struct bpf_test_option *opt, const char *prefix)
  85{
  86        fprintf(stderr, "%s{flags:0x%x, max_delack_ms:%u, rand:0x%x}\n",
  87                prefix ? : "", opt->flags, opt->max_delack_ms, opt->rand);
  88}
  89
  90static void sk_fds_close(struct sk_fds *sk_fds)
  91{
  92        close(sk_fds->srv_fd);
  93        close(sk_fds->passive_fd);
  94        close(sk_fds->active_fd);
  95}
  96
  97static int sk_fds_shutdown(struct sk_fds *sk_fds)
  98{
  99        int ret, abyte;
 100
 101        shutdown(sk_fds->active_fd, SHUT_WR);
 102        ret = read(sk_fds->passive_fd, &abyte, sizeof(abyte));
 103        if (CHECK(ret != 0, "read-after-shutdown(passive_fd):",
 104                  "ret:%d %s (%d)\n",
 105                  ret, strerror(errno), errno))
 106                return -1;
 107
 108        shutdown(sk_fds->passive_fd, SHUT_WR);
 109        ret = read(sk_fds->active_fd, &abyte, sizeof(abyte));
 110        if (CHECK(ret != 0, "read-after-shutdown(active_fd):",
 111                  "ret:%d %s (%d)\n",
 112                  ret, strerror(errno), errno))
 113                return -1;
 114
 115        return 0;
 116}
 117
 118static int sk_fds_connect(struct sk_fds *sk_fds, bool fast_open)
 119{
 120        const char fast[] = "FAST!!!";
 121        struct sockaddr_in6 addr6;
 122        socklen_t len;
 123
 124        sk_fds->srv_fd = start_server(AF_INET6, SOCK_STREAM, LO_ADDR6, 0, 0);
 125        if (CHECK(sk_fds->srv_fd == -1, "start_server", "%s (%d)\n",
 126                  strerror(errno), errno))
 127                goto error;
 128
 129        if (fast_open)
 130                sk_fds->active_fd = fastopen_connect(sk_fds->srv_fd, fast,
 131                                                     sizeof(fast), 0);
 132        else
 133                sk_fds->active_fd = connect_to_fd(sk_fds->srv_fd, 0);
 134
 135        if (CHECK_FAIL(sk_fds->active_fd == -1)) {
 136                close(sk_fds->srv_fd);
 137                goto error;
 138        }
 139
 140        len = sizeof(addr6);
 141        if (CHECK(getsockname(sk_fds->srv_fd, (struct sockaddr *)&addr6,
 142                              &len), "getsockname(srv_fd)", "%s (%d)\n",
 143                  strerror(errno), errno))
 144                goto error_close;
 145        sk_fds->passive_lport = ntohs(addr6.sin6_port);
 146
 147        len = sizeof(addr6);
 148        if (CHECK(getsockname(sk_fds->active_fd, (struct sockaddr *)&addr6,
 149                              &len), "getsockname(active_fd)", "%s (%d)\n",
 150                  strerror(errno), errno))
 151                goto error_close;
 152        sk_fds->active_lport = ntohs(addr6.sin6_port);
 153
 154        sk_fds->passive_fd = accept(sk_fds->srv_fd, NULL, 0);
 155        if (CHECK(sk_fds->passive_fd == -1, "accept(srv_fd)", "%s (%d)\n",
 156                  strerror(errno), errno))
 157                goto error_close;
 158
 159        if (fast_open) {
 160                char bytes_in[sizeof(fast)];
 161                int ret;
 162
 163                ret = read(sk_fds->passive_fd, bytes_in, sizeof(bytes_in));
 164                if (CHECK(ret != sizeof(fast), "read fastopen syn data",
 165                          "expected=%lu actual=%d\n", sizeof(fast), ret)) {
 166                        close(sk_fds->passive_fd);
 167                        goto error_close;
 168                }
 169        }
 170
 171        return 0;
 172
 173error_close:
 174        close(sk_fds->active_fd);
 175        close(sk_fds->srv_fd);
 176
 177error:
 178        memset(sk_fds, -1, sizeof(*sk_fds));
 179        return -1;
 180}
 181
 182static int check_hdr_opt(const struct bpf_test_option *exp,
 183                         const struct bpf_test_option *act,
 184                         const char *hdr_desc)
 185{
 186        if (CHECK(memcmp(exp, act, sizeof(*exp)),
 187                  "expected-vs-actual", "unexpected %s\n", hdr_desc)) {
 188                print_option(exp, "expected: ");
 189                print_option(act, "  actual: ");
 190                return -1;
 191        }
 192
 193        return 0;
 194}
 195
 196static int check_hdr_stg(const struct hdr_stg *exp, int fd,
 197                         const char *stg_desc)
 198{
 199        struct hdr_stg act;
 200
 201        if (CHECK(bpf_map_lookup_elem(hdr_stg_map_fd, &fd, &act),
 202                  "map_lookup(hdr_stg_map_fd)", "%s %s (%d)\n",
 203                  stg_desc, strerror(errno), errno))
 204                return -1;
 205
 206        if (CHECK(memcmp(exp, &act, sizeof(*exp)),
 207                  "expected-vs-actual", "unexpected %s\n", stg_desc)) {
 208                print_hdr_stg(exp, "expected: ");
 209                print_hdr_stg(&act, "  actual: ");
 210                return -1;
 211        }
 212
 213        return 0;
 214}
 215
 216static int check_error_linum(const struct sk_fds *sk_fds)
 217{
 218        unsigned int nr_errors = 0;
 219        struct linum_err linum_err;
 220        int lport;
 221
 222        lport = sk_fds->passive_lport;
 223        if (!bpf_map_lookup_elem(lport_linum_map_fd, &lport, &linum_err)) {
 224                fprintf(stderr,
 225                        "bpf prog error out at lport:passive(%d), linum:%u err:%d\n",
 226                        lport, linum_err.linum, linum_err.err);
 227                nr_errors++;
 228        }
 229
 230        lport = sk_fds->active_lport;
 231        if (!bpf_map_lookup_elem(lport_linum_map_fd, &lport, &linum_err)) {
 232                fprintf(stderr,
 233                        "bpf prog error out at lport:active(%d), linum:%u err:%d\n",
 234                        lport, linum_err.linum, linum_err.err);
 235                nr_errors++;
 236        }
 237
 238        return nr_errors;
 239}
 240
 241static void check_hdr_and_close_fds(struct sk_fds *sk_fds)
 242{
 243        const __u32 expected_inherit_cb_flags =
 244                BPF_SOCK_OPS_PARSE_UNKNOWN_HDR_OPT_CB_FLAG |
 245                BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG |
 246                BPF_SOCK_OPS_STATE_CB_FLAG;
 247
 248        if (sk_fds_shutdown(sk_fds))
 249                goto check_linum;
 250
 251        if (CHECK(expected_inherit_cb_flags != skel->bss->inherit_cb_flags,
 252                  "Unexpected inherit_cb_flags", "0x%x != 0x%x\n",
 253                  skel->bss->inherit_cb_flags, expected_inherit_cb_flags))
 254                goto check_linum;
 255
 256        if (check_hdr_stg(&exp_passive_hdr_stg, sk_fds->passive_fd,
 257                          "passive_hdr_stg"))
 258                goto check_linum;
 259
 260        if (check_hdr_stg(&exp_active_hdr_stg, sk_fds->active_fd,
 261                          "active_hdr_stg"))
 262                goto check_linum;
 263
 264        if (check_hdr_opt(&exp_passive_estab_in, &skel->bss->passive_estab_in,
 265                          "passive_estab_in"))
 266                goto check_linum;
 267
 268        if (check_hdr_opt(&exp_active_estab_in, &skel->bss->active_estab_in,
 269                          "active_estab_in"))
 270                goto check_linum;
 271
 272        if (check_hdr_opt(&exp_passive_fin_in, &skel->bss->passive_fin_in,
 273                          "passive_fin_in"))
 274                goto check_linum;
 275
 276        check_hdr_opt(&exp_active_fin_in, &skel->bss->active_fin_in,
 277                      "active_fin_in");
 278
 279check_linum:
 280        CHECK_FAIL(check_error_linum(sk_fds));
 281        sk_fds_close(sk_fds);
 282}
 283
 284static void prepare_out(void)
 285{
 286        skel->bss->active_syn_out = exp_passive_estab_in;
 287        skel->bss->passive_synack_out = exp_active_estab_in;
 288
 289        skel->bss->active_fin_out = exp_passive_fin_in;
 290        skel->bss->passive_fin_out = exp_active_fin_in;
 291}
 292
 293static void reset_test(void)
 294{
 295        size_t optsize = sizeof(struct bpf_test_option);
 296        int lport, err;
 297
 298        memset(&skel->bss->passive_synack_out, 0, optsize);
 299        memset(&skel->bss->passive_fin_out, 0, optsize);
 300
 301        memset(&skel->bss->passive_estab_in, 0, optsize);
 302        memset(&skel->bss->passive_fin_in, 0, optsize);
 303
 304        memset(&skel->bss->active_syn_out, 0, optsize);
 305        memset(&skel->bss->active_fin_out, 0, optsize);
 306
 307        memset(&skel->bss->active_estab_in, 0, optsize);
 308        memset(&skel->bss->active_fin_in, 0, optsize);
 309
 310        skel->bss->inherit_cb_flags = 0;
 311
 312        skel->data->test_kind = TCPOPT_EXP;
 313        skel->data->test_magic = 0xeB9F;
 314
 315        memset(&exp_passive_estab_in, 0, optsize);
 316        memset(&exp_active_estab_in, 0, optsize);
 317        memset(&exp_passive_fin_in, 0, optsize);
 318        memset(&exp_active_fin_in, 0, optsize);
 319
 320        memset(&exp_passive_hdr_stg, 0, sizeof(exp_passive_hdr_stg));
 321        memset(&exp_active_hdr_stg, 0, sizeof(exp_active_hdr_stg));
 322        exp_active_hdr_stg.active = true;
 323
 324        err = bpf_map_get_next_key(lport_linum_map_fd, NULL, &lport);
 325        while (!err) {
 326                bpf_map_delete_elem(lport_linum_map_fd, &lport);
 327                err = bpf_map_get_next_key(lport_linum_map_fd, &lport, &lport);
 328        }
 329}
 330
 331static void fastopen_estab(void)
 332{
 333        struct bpf_link *link;
 334        struct sk_fds sk_fds;
 335
 336        hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map);
 337        lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map);
 338
 339        exp_passive_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
 340        exp_passive_estab_in.rand = 0xfa;
 341        exp_passive_estab_in.max_delack_ms = 11;
 342
 343        exp_active_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
 344        exp_active_estab_in.rand = 0xce;
 345        exp_active_estab_in.max_delack_ms = 22;
 346
 347        exp_passive_hdr_stg.fastopen = true;
 348
 349        prepare_out();
 350
 351        /* Allow fastopen without fastopen cookie */
 352        if (write_sysctl("/proc/sys/net/ipv4/tcp_fastopen", "1543"))
 353                return;
 354
 355        link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd);
 356        if (!ASSERT_OK_PTR(link, "attach_cgroup(estab)"))
 357                return;
 358
 359        if (sk_fds_connect(&sk_fds, true)) {
 360                bpf_link__destroy(link);
 361                return;
 362        }
 363
 364        check_hdr_and_close_fds(&sk_fds);
 365        bpf_link__destroy(link);
 366}
 367
 368static void syncookie_estab(void)
 369{
 370        struct bpf_link *link;
 371        struct sk_fds sk_fds;
 372
 373        hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map);
 374        lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map);
 375
 376        exp_passive_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
 377        exp_passive_estab_in.rand = 0xfa;
 378        exp_passive_estab_in.max_delack_ms = 11;
 379
 380        exp_active_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS |
 381                                        OPTION_F_RESEND;
 382        exp_active_estab_in.rand = 0xce;
 383        exp_active_estab_in.max_delack_ms = 22;
 384
 385        exp_passive_hdr_stg.syncookie = true;
 386        exp_active_hdr_stg.resend_syn = true,
 387
 388        prepare_out();
 389
 390        /* Clear the RESEND to ensure the bpf prog can learn
 391         * want_cookie and set the RESEND by itself.
 392         */
 393        skel->bss->passive_synack_out.flags &= ~OPTION_F_RESEND;
 394
 395        /* Enforce syncookie mode */
 396        if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "2"))
 397                return;
 398
 399        link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd);
 400        if (!ASSERT_OK_PTR(link, "attach_cgroup(estab)"))
 401                return;
 402
 403        if (sk_fds_connect(&sk_fds, false)) {
 404                bpf_link__destroy(link);
 405                return;
 406        }
 407
 408        check_hdr_and_close_fds(&sk_fds);
 409        bpf_link__destroy(link);
 410}
 411
 412static void fin(void)
 413{
 414        struct bpf_link *link;
 415        struct sk_fds sk_fds;
 416
 417        hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map);
 418        lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map);
 419
 420        exp_passive_fin_in.flags = OPTION_F_RAND;
 421        exp_passive_fin_in.rand = 0xfa;
 422
 423        exp_active_fin_in.flags = OPTION_F_RAND;
 424        exp_active_fin_in.rand = 0xce;
 425
 426        prepare_out();
 427
 428        if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1"))
 429                return;
 430
 431        link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd);
 432        if (!ASSERT_OK_PTR(link, "attach_cgroup(estab)"))
 433                return;
 434
 435        if (sk_fds_connect(&sk_fds, false)) {
 436                bpf_link__destroy(link);
 437                return;
 438        }
 439
 440        check_hdr_and_close_fds(&sk_fds);
 441        bpf_link__destroy(link);
 442}
 443
 444static void __simple_estab(bool exprm)
 445{
 446        struct bpf_link *link;
 447        struct sk_fds sk_fds;
 448
 449        hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map);
 450        lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map);
 451
 452        exp_passive_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
 453        exp_passive_estab_in.rand = 0xfa;
 454        exp_passive_estab_in.max_delack_ms = 11;
 455
 456        exp_active_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
 457        exp_active_estab_in.rand = 0xce;
 458        exp_active_estab_in.max_delack_ms = 22;
 459
 460        prepare_out();
 461
 462        if (!exprm) {
 463                skel->data->test_kind = 0xB9;
 464                skel->data->test_magic = 0;
 465        }
 466
 467        if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1"))
 468                return;
 469
 470        link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd);
 471        if (!ASSERT_OK_PTR(link, "attach_cgroup(estab)"))
 472                return;
 473
 474        if (sk_fds_connect(&sk_fds, false)) {
 475                bpf_link__destroy(link);
 476                return;
 477        }
 478
 479        check_hdr_and_close_fds(&sk_fds);
 480        bpf_link__destroy(link);
 481}
 482
 483static void no_exprm_estab(void)
 484{
 485        __simple_estab(false);
 486}
 487
 488static void simple_estab(void)
 489{
 490        __simple_estab(true);
 491}
 492
 493static void misc(void)
 494{
 495        const char send_msg[] = "MISC!!!";
 496        char recv_msg[sizeof(send_msg)];
 497        const unsigned int nr_data = 2;
 498        struct bpf_link *link;
 499        struct sk_fds sk_fds;
 500        int i, ret;
 501
 502        lport_linum_map_fd = bpf_map__fd(misc_skel->maps.lport_linum_map);
 503
 504        if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1"))
 505                return;
 506
 507        link = bpf_program__attach_cgroup(misc_skel->progs.misc_estab, cg_fd);
 508        if (!ASSERT_OK_PTR(link, "attach_cgroup(misc_estab)"))
 509                return;
 510
 511        if (sk_fds_connect(&sk_fds, false)) {
 512                bpf_link__destroy(link);
 513                return;
 514        }
 515
 516        for (i = 0; i < nr_data; i++) {
 517                /* MSG_EOR to ensure skb will not be combined */
 518                ret = send(sk_fds.active_fd, send_msg, sizeof(send_msg),
 519                           MSG_EOR);
 520                if (CHECK(ret != sizeof(send_msg), "send(msg)", "ret:%d\n",
 521                          ret))
 522                        goto check_linum;
 523
 524                ret = read(sk_fds.passive_fd, recv_msg, sizeof(recv_msg));
 525                if (CHECK(ret != sizeof(send_msg), "read(msg)", "ret:%d\n",
 526                          ret))
 527                        goto check_linum;
 528        }
 529
 530        if (sk_fds_shutdown(&sk_fds))
 531                goto check_linum;
 532
 533        CHECK(misc_skel->bss->nr_syn != 1, "unexpected nr_syn",
 534              "expected (1) != actual (%u)\n",
 535                misc_skel->bss->nr_syn);
 536
 537        CHECK(misc_skel->bss->nr_data != nr_data, "unexpected nr_data",
 538              "expected (%u) != actual (%u)\n",
 539              nr_data, misc_skel->bss->nr_data);
 540
 541        /* The last ACK may have been delayed, so it is either 1 or 2. */
 542        CHECK(misc_skel->bss->nr_pure_ack != 1 &&
 543              misc_skel->bss->nr_pure_ack != 2,
 544              "unexpected nr_pure_ack",
 545              "expected (1 or 2) != actual (%u)\n",
 546                misc_skel->bss->nr_pure_ack);
 547
 548        CHECK(misc_skel->bss->nr_fin != 1, "unexpected nr_fin",
 549              "expected (1) != actual (%u)\n",
 550              misc_skel->bss->nr_fin);
 551
 552check_linum:
 553        CHECK_FAIL(check_error_linum(&sk_fds));
 554        sk_fds_close(&sk_fds);
 555        bpf_link__destroy(link);
 556}
 557
 558struct test {
 559        const char *desc;
 560        void (*run)(void);
 561};
 562
 563#define DEF_TEST(name) { #name, name }
 564static struct test tests[] = {
 565        DEF_TEST(simple_estab),
 566        DEF_TEST(no_exprm_estab),
 567        DEF_TEST(syncookie_estab),
 568        DEF_TEST(fastopen_estab),
 569        DEF_TEST(fin),
 570        DEF_TEST(misc),
 571};
 572
 573void test_tcp_hdr_options(void)
 574{
 575        int i;
 576
 577        skel = test_tcp_hdr_options__open_and_load();
 578        if (CHECK(!skel, "open and load skel", "failed"))
 579                return;
 580
 581        misc_skel = test_misc_tcp_hdr_options__open_and_load();
 582        if (CHECK(!misc_skel, "open and load misc test skel", "failed"))
 583                goto skel_destroy;
 584
 585        cg_fd = test__join_cgroup(CG_NAME);
 586        if (CHECK_FAIL(cg_fd < 0))
 587                goto skel_destroy;
 588
 589        for (i = 0; i < ARRAY_SIZE(tests); i++) {
 590                if (!test__start_subtest(tests[i].desc))
 591                        continue;
 592
 593                if (create_netns())
 594                        break;
 595
 596                tests[i].run();
 597
 598                reset_test();
 599        }
 600
 601        close(cg_fd);
 602skel_destroy:
 603        test_misc_tcp_hdr_options__destroy(misc_skel);
 604        test_tcp_hdr_options__destroy(skel);
 605}
 606