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 (CHECK(IS_ERR(link), "attach_cgroup(estab)", "err: %ld\n",
 357                  PTR_ERR(link)))
 358                return;
 359
 360        if (sk_fds_connect(&sk_fds, true)) {
 361                bpf_link__destroy(link);
 362                return;
 363        }
 364
 365        check_hdr_and_close_fds(&sk_fds);
 366        bpf_link__destroy(link);
 367}
 368
 369static void syncookie_estab(void)
 370{
 371        struct bpf_link *link;
 372        struct sk_fds sk_fds;
 373
 374        hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map);
 375        lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map);
 376
 377        exp_passive_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
 378        exp_passive_estab_in.rand = 0xfa;
 379        exp_passive_estab_in.max_delack_ms = 11;
 380
 381        exp_active_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS |
 382                                        OPTION_F_RESEND;
 383        exp_active_estab_in.rand = 0xce;
 384        exp_active_estab_in.max_delack_ms = 22;
 385
 386        exp_passive_hdr_stg.syncookie = true;
 387        exp_active_hdr_stg.resend_syn = true,
 388
 389        prepare_out();
 390
 391        /* Clear the RESEND to ensure the bpf prog can learn
 392         * want_cookie and set the RESEND by itself.
 393         */
 394        skel->bss->passive_synack_out.flags &= ~OPTION_F_RESEND;
 395
 396        /* Enforce syncookie mode */
 397        if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "2"))
 398                return;
 399
 400        link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd);
 401        if (CHECK(IS_ERR(link), "attach_cgroup(estab)", "err: %ld\n",
 402                  PTR_ERR(link)))
 403                return;
 404
 405        if (sk_fds_connect(&sk_fds, false)) {
 406                bpf_link__destroy(link);
 407                return;
 408        }
 409
 410        check_hdr_and_close_fds(&sk_fds);
 411        bpf_link__destroy(link);
 412}
 413
 414static void fin(void)
 415{
 416        struct bpf_link *link;
 417        struct sk_fds sk_fds;
 418
 419        hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map);
 420        lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map);
 421
 422        exp_passive_fin_in.flags = OPTION_F_RAND;
 423        exp_passive_fin_in.rand = 0xfa;
 424
 425        exp_active_fin_in.flags = OPTION_F_RAND;
 426        exp_active_fin_in.rand = 0xce;
 427
 428        prepare_out();
 429
 430        if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1"))
 431                return;
 432
 433        link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd);
 434        if (CHECK(IS_ERR(link), "attach_cgroup(estab)", "err: %ld\n",
 435                  PTR_ERR(link)))
 436                return;
 437
 438        if (sk_fds_connect(&sk_fds, false)) {
 439                bpf_link__destroy(link);
 440                return;
 441        }
 442
 443        check_hdr_and_close_fds(&sk_fds);
 444        bpf_link__destroy(link);
 445}
 446
 447static void __simple_estab(bool exprm)
 448{
 449        struct bpf_link *link;
 450        struct sk_fds sk_fds;
 451
 452        hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map);
 453        lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map);
 454
 455        exp_passive_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
 456        exp_passive_estab_in.rand = 0xfa;
 457        exp_passive_estab_in.max_delack_ms = 11;
 458
 459        exp_active_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
 460        exp_active_estab_in.rand = 0xce;
 461        exp_active_estab_in.max_delack_ms = 22;
 462
 463        prepare_out();
 464
 465        if (!exprm) {
 466                skel->data->test_kind = 0xB9;
 467                skel->data->test_magic = 0;
 468        }
 469
 470        if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1"))
 471                return;
 472
 473        link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd);
 474        if (CHECK(IS_ERR(link), "attach_cgroup(estab)", "err: %ld\n",
 475                  PTR_ERR(link)))
 476                return;
 477
 478        if (sk_fds_connect(&sk_fds, false)) {
 479                bpf_link__destroy(link);
 480                return;
 481        }
 482
 483        check_hdr_and_close_fds(&sk_fds);
 484        bpf_link__destroy(link);
 485}
 486
 487static void no_exprm_estab(void)
 488{
 489        __simple_estab(false);
 490}
 491
 492static void simple_estab(void)
 493{
 494        __simple_estab(true);
 495}
 496
 497static void misc(void)
 498{
 499        const char send_msg[] = "MISC!!!";
 500        char recv_msg[sizeof(send_msg)];
 501        const unsigned int nr_data = 2;
 502        struct bpf_link *link;
 503        struct sk_fds sk_fds;
 504        int i, ret;
 505
 506        lport_linum_map_fd = bpf_map__fd(misc_skel->maps.lport_linum_map);
 507
 508        if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1"))
 509                return;
 510
 511        link = bpf_program__attach_cgroup(misc_skel->progs.misc_estab, cg_fd);
 512        if (CHECK(IS_ERR(link), "attach_cgroup(misc_estab)", "err: %ld\n",
 513                  PTR_ERR(link)))
 514                return;
 515
 516        if (sk_fds_connect(&sk_fds, false)) {
 517                bpf_link__destroy(link);
 518                return;
 519        }
 520
 521        for (i = 0; i < nr_data; i++) {
 522                /* MSG_EOR to ensure skb will not be combined */
 523                ret = send(sk_fds.active_fd, send_msg, sizeof(send_msg),
 524                           MSG_EOR);
 525                if (CHECK(ret != sizeof(send_msg), "send(msg)", "ret:%d\n",
 526                          ret))
 527                        goto check_linum;
 528
 529                ret = read(sk_fds.passive_fd, recv_msg, sizeof(recv_msg));
 530                if (CHECK(ret != sizeof(send_msg), "read(msg)", "ret:%d\n",
 531                          ret))
 532                        goto check_linum;
 533        }
 534
 535        if (sk_fds_shutdown(&sk_fds))
 536                goto check_linum;
 537
 538        CHECK(misc_skel->bss->nr_syn != 1, "unexpected nr_syn",
 539              "expected (1) != actual (%u)\n",
 540                misc_skel->bss->nr_syn);
 541
 542        CHECK(misc_skel->bss->nr_data != nr_data, "unexpected nr_data",
 543              "expected (%u) != actual (%u)\n",
 544              nr_data, misc_skel->bss->nr_data);
 545
 546        /* The last ACK may have been delayed, so it is either 1 or 2. */
 547        CHECK(misc_skel->bss->nr_pure_ack != 1 &&
 548              misc_skel->bss->nr_pure_ack != 2,
 549              "unexpected nr_pure_ack",
 550              "expected (1 or 2) != actual (%u)\n",
 551                misc_skel->bss->nr_pure_ack);
 552
 553        CHECK(misc_skel->bss->nr_fin != 1, "unexpected nr_fin",
 554              "expected (1) != actual (%u)\n",
 555              misc_skel->bss->nr_fin);
 556
 557check_linum:
 558        CHECK_FAIL(check_error_linum(&sk_fds));
 559        sk_fds_close(&sk_fds);
 560        bpf_link__destroy(link);
 561}
 562
 563struct test {
 564        const char *desc;
 565        void (*run)(void);
 566};
 567
 568#define DEF_TEST(name) { #name, name }
 569static struct test tests[] = {
 570        DEF_TEST(simple_estab),
 571        DEF_TEST(no_exprm_estab),
 572        DEF_TEST(syncookie_estab),
 573        DEF_TEST(fastopen_estab),
 574        DEF_TEST(fin),
 575        DEF_TEST(misc),
 576};
 577
 578void test_tcp_hdr_options(void)
 579{
 580        int i;
 581
 582        skel = test_tcp_hdr_options__open_and_load();
 583        if (CHECK(!skel, "open and load skel", "failed"))
 584                return;
 585
 586        misc_skel = test_misc_tcp_hdr_options__open_and_load();
 587        if (CHECK(!misc_skel, "open and load misc test skel", "failed"))
 588                goto skel_destroy;
 589
 590        cg_fd = test__join_cgroup(CG_NAME);
 591        if (CHECK_FAIL(cg_fd < 0))
 592                goto skel_destroy;
 593
 594        for (i = 0; i < ARRAY_SIZE(tests); i++) {
 595                if (!test__start_subtest(tests[i].desc))
 596                        continue;
 597
 598                if (create_netns())
 599                        break;
 600
 601                tests[i].run();
 602
 603                reset_test();
 604        }
 605
 606        close(cg_fd);
 607skel_destroy:
 608        test_misc_tcp_hdr_options__destroy(misc_skel);
 609        test_tcp_hdr_options__destroy(skel);
 610}
 611