0001
0002
0003
0004 #define _GNU_SOURCE
0005 #include <sched.h>
0006 #include <stdio.h>
0007 #include <stdlib.h>
0008 #include <sys/socket.h>
0009 #include <linux/compiler.h>
0010
0011 #include "test_progs.h"
0012 #include "cgroup_helpers.h"
0013 #include "network_helpers.h"
0014 #include "test_tcp_hdr_options.h"
0015 #include "test_tcp_hdr_options.skel.h"
0016 #include "test_misc_tcp_hdr_options.skel.h"
0017
0018 #define LO_ADDR6 "::1"
0019 #define CG_NAME "/tcpbpf-hdr-opt-test"
0020
0021 static struct bpf_test_option exp_passive_estab_in;
0022 static struct bpf_test_option exp_active_estab_in;
0023 static struct bpf_test_option exp_passive_fin_in;
0024 static struct bpf_test_option exp_active_fin_in;
0025 static struct hdr_stg exp_passive_hdr_stg;
0026 static struct hdr_stg exp_active_hdr_stg = { .active = true, };
0027
0028 static struct test_misc_tcp_hdr_options *misc_skel;
0029 static struct test_tcp_hdr_options *skel;
0030 static int lport_linum_map_fd;
0031 static int hdr_stg_map_fd;
0032 static __u32 duration;
0033 static int cg_fd;
0034
0035 struct sk_fds {
0036 int srv_fd;
0037 int passive_fd;
0038 int active_fd;
0039 int passive_lport;
0040 int active_lport;
0041 };
0042
0043 static int create_netns(void)
0044 {
0045 if (CHECK(unshare(CLONE_NEWNET), "create netns",
0046 "unshare(CLONE_NEWNET): %s (%d)",
0047 strerror(errno), errno))
0048 return -1;
0049
0050 if (CHECK(system("ip link set dev lo up"), "run ip cmd",
0051 "failed to bring lo link up\n"))
0052 return -1;
0053
0054 return 0;
0055 }
0056
0057 static int write_sysctl(const char *sysctl, const char *value)
0058 {
0059 int fd, err, len;
0060
0061 fd = open(sysctl, O_WRONLY);
0062 if (CHECK(fd == -1, "open sysctl", "open(%s): %s (%d)\n",
0063 sysctl, strerror(errno), errno))
0064 return -1;
0065
0066 len = strlen(value);
0067 err = write(fd, value, len);
0068 close(fd);
0069 if (CHECK(err != len, "write sysctl",
0070 "write(%s, %s): err:%d %s (%d)\n",
0071 sysctl, value, err, strerror(errno), errno))
0072 return -1;
0073
0074 return 0;
0075 }
0076
0077 static void print_hdr_stg(const struct hdr_stg *hdr_stg, const char *prefix)
0078 {
0079 fprintf(stderr, "%s{active:%u, resend_syn:%u, syncookie:%u, fastopen:%u}\n",
0080 prefix ? : "", hdr_stg->active, hdr_stg->resend_syn,
0081 hdr_stg->syncookie, hdr_stg->fastopen);
0082 }
0083
0084 static void print_option(const struct bpf_test_option *opt, const char *prefix)
0085 {
0086 fprintf(stderr, "%s{flags:0x%x, max_delack_ms:%u, rand:0x%x}\n",
0087 prefix ? : "", opt->flags, opt->max_delack_ms, opt->rand);
0088 }
0089
0090 static void sk_fds_close(struct sk_fds *sk_fds)
0091 {
0092 close(sk_fds->srv_fd);
0093 close(sk_fds->passive_fd);
0094 close(sk_fds->active_fd);
0095 }
0096
0097 static int sk_fds_shutdown(struct sk_fds *sk_fds)
0098 {
0099 int ret, abyte;
0100
0101 shutdown(sk_fds->active_fd, SHUT_WR);
0102 ret = read(sk_fds->passive_fd, &abyte, sizeof(abyte));
0103 if (CHECK(ret != 0, "read-after-shutdown(passive_fd):",
0104 "ret:%d %s (%d)\n",
0105 ret, strerror(errno), errno))
0106 return -1;
0107
0108 shutdown(sk_fds->passive_fd, SHUT_WR);
0109 ret = read(sk_fds->active_fd, &abyte, sizeof(abyte));
0110 if (CHECK(ret != 0, "read-after-shutdown(active_fd):",
0111 "ret:%d %s (%d)\n",
0112 ret, strerror(errno), errno))
0113 return -1;
0114
0115 return 0;
0116 }
0117
0118 static int sk_fds_connect(struct sk_fds *sk_fds, bool fast_open)
0119 {
0120 const char fast[] = "FAST!!!";
0121 struct sockaddr_in6 addr6;
0122 socklen_t len;
0123
0124 sk_fds->srv_fd = start_server(AF_INET6, SOCK_STREAM, LO_ADDR6, 0, 0);
0125 if (CHECK(sk_fds->srv_fd == -1, "start_server", "%s (%d)\n",
0126 strerror(errno), errno))
0127 goto error;
0128
0129 if (fast_open)
0130 sk_fds->active_fd = fastopen_connect(sk_fds->srv_fd, fast,
0131 sizeof(fast), 0);
0132 else
0133 sk_fds->active_fd = connect_to_fd(sk_fds->srv_fd, 0);
0134
0135 if (CHECK_FAIL(sk_fds->active_fd == -1)) {
0136 close(sk_fds->srv_fd);
0137 goto error;
0138 }
0139
0140 len = sizeof(addr6);
0141 if (CHECK(getsockname(sk_fds->srv_fd, (struct sockaddr *)&addr6,
0142 &len), "getsockname(srv_fd)", "%s (%d)\n",
0143 strerror(errno), errno))
0144 goto error_close;
0145 sk_fds->passive_lport = ntohs(addr6.sin6_port);
0146
0147 len = sizeof(addr6);
0148 if (CHECK(getsockname(sk_fds->active_fd, (struct sockaddr *)&addr6,
0149 &len), "getsockname(active_fd)", "%s (%d)\n",
0150 strerror(errno), errno))
0151 goto error_close;
0152 sk_fds->active_lport = ntohs(addr6.sin6_port);
0153
0154 sk_fds->passive_fd = accept(sk_fds->srv_fd, NULL, 0);
0155 if (CHECK(sk_fds->passive_fd == -1, "accept(srv_fd)", "%s (%d)\n",
0156 strerror(errno), errno))
0157 goto error_close;
0158
0159 if (fast_open) {
0160 char bytes_in[sizeof(fast)];
0161 int ret;
0162
0163 ret = read(sk_fds->passive_fd, bytes_in, sizeof(bytes_in));
0164 if (CHECK(ret != sizeof(fast), "read fastopen syn data",
0165 "expected=%lu actual=%d\n", sizeof(fast), ret)) {
0166 close(sk_fds->passive_fd);
0167 goto error_close;
0168 }
0169 }
0170
0171 return 0;
0172
0173 error_close:
0174 close(sk_fds->active_fd);
0175 close(sk_fds->srv_fd);
0176
0177 error:
0178 memset(sk_fds, -1, sizeof(*sk_fds));
0179 return -1;
0180 }
0181
0182 static int check_hdr_opt(const struct bpf_test_option *exp,
0183 const struct bpf_test_option *act,
0184 const char *hdr_desc)
0185 {
0186 if (CHECK(memcmp(exp, act, sizeof(*exp)),
0187 "expected-vs-actual", "unexpected %s\n", hdr_desc)) {
0188 print_option(exp, "expected: ");
0189 print_option(act, " actual: ");
0190 return -1;
0191 }
0192
0193 return 0;
0194 }
0195
0196 static int check_hdr_stg(const struct hdr_stg *exp, int fd,
0197 const char *stg_desc)
0198 {
0199 struct hdr_stg act;
0200
0201 if (CHECK(bpf_map_lookup_elem(hdr_stg_map_fd, &fd, &act),
0202 "map_lookup(hdr_stg_map_fd)", "%s %s (%d)\n",
0203 stg_desc, strerror(errno), errno))
0204 return -1;
0205
0206 if (CHECK(memcmp(exp, &act, sizeof(*exp)),
0207 "expected-vs-actual", "unexpected %s\n", stg_desc)) {
0208 print_hdr_stg(exp, "expected: ");
0209 print_hdr_stg(&act, " actual: ");
0210 return -1;
0211 }
0212
0213 return 0;
0214 }
0215
0216 static int check_error_linum(const struct sk_fds *sk_fds)
0217 {
0218 unsigned int nr_errors = 0;
0219 struct linum_err linum_err;
0220 int lport;
0221
0222 lport = sk_fds->passive_lport;
0223 if (!bpf_map_lookup_elem(lport_linum_map_fd, &lport, &linum_err)) {
0224 fprintf(stderr,
0225 "bpf prog error out at lport:passive(%d), linum:%u err:%d\n",
0226 lport, linum_err.linum, linum_err.err);
0227 nr_errors++;
0228 }
0229
0230 lport = sk_fds->active_lport;
0231 if (!bpf_map_lookup_elem(lport_linum_map_fd, &lport, &linum_err)) {
0232 fprintf(stderr,
0233 "bpf prog error out at lport:active(%d), linum:%u err:%d\n",
0234 lport, linum_err.linum, linum_err.err);
0235 nr_errors++;
0236 }
0237
0238 return nr_errors;
0239 }
0240
0241 static void check_hdr_and_close_fds(struct sk_fds *sk_fds)
0242 {
0243 const __u32 expected_inherit_cb_flags =
0244 BPF_SOCK_OPS_PARSE_UNKNOWN_HDR_OPT_CB_FLAG |
0245 BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG |
0246 BPF_SOCK_OPS_STATE_CB_FLAG;
0247
0248 if (sk_fds_shutdown(sk_fds))
0249 goto check_linum;
0250
0251 if (CHECK(expected_inherit_cb_flags != skel->bss->inherit_cb_flags,
0252 "Unexpected inherit_cb_flags", "0x%x != 0x%x\n",
0253 skel->bss->inherit_cb_flags, expected_inherit_cb_flags))
0254 goto check_linum;
0255
0256 if (check_hdr_stg(&exp_passive_hdr_stg, sk_fds->passive_fd,
0257 "passive_hdr_stg"))
0258 goto check_linum;
0259
0260 if (check_hdr_stg(&exp_active_hdr_stg, sk_fds->active_fd,
0261 "active_hdr_stg"))
0262 goto check_linum;
0263
0264 if (check_hdr_opt(&exp_passive_estab_in, &skel->bss->passive_estab_in,
0265 "passive_estab_in"))
0266 goto check_linum;
0267
0268 if (check_hdr_opt(&exp_active_estab_in, &skel->bss->active_estab_in,
0269 "active_estab_in"))
0270 goto check_linum;
0271
0272 if (check_hdr_opt(&exp_passive_fin_in, &skel->bss->passive_fin_in,
0273 "passive_fin_in"))
0274 goto check_linum;
0275
0276 check_hdr_opt(&exp_active_fin_in, &skel->bss->active_fin_in,
0277 "active_fin_in");
0278
0279 check_linum:
0280 CHECK_FAIL(check_error_linum(sk_fds));
0281 sk_fds_close(sk_fds);
0282 }
0283
0284 static void prepare_out(void)
0285 {
0286 skel->bss->active_syn_out = exp_passive_estab_in;
0287 skel->bss->passive_synack_out = exp_active_estab_in;
0288
0289 skel->bss->active_fin_out = exp_passive_fin_in;
0290 skel->bss->passive_fin_out = exp_active_fin_in;
0291 }
0292
0293 static void reset_test(void)
0294 {
0295 size_t optsize = sizeof(struct bpf_test_option);
0296 int lport, err;
0297
0298 memset(&skel->bss->passive_synack_out, 0, optsize);
0299 memset(&skel->bss->passive_fin_out, 0, optsize);
0300
0301 memset(&skel->bss->passive_estab_in, 0, optsize);
0302 memset(&skel->bss->passive_fin_in, 0, optsize);
0303
0304 memset(&skel->bss->active_syn_out, 0, optsize);
0305 memset(&skel->bss->active_fin_out, 0, optsize);
0306
0307 memset(&skel->bss->active_estab_in, 0, optsize);
0308 memset(&skel->bss->active_fin_in, 0, optsize);
0309
0310 skel->bss->inherit_cb_flags = 0;
0311
0312 skel->data->test_kind = TCPOPT_EXP;
0313 skel->data->test_magic = 0xeB9F;
0314
0315 memset(&exp_passive_estab_in, 0, optsize);
0316 memset(&exp_active_estab_in, 0, optsize);
0317 memset(&exp_passive_fin_in, 0, optsize);
0318 memset(&exp_active_fin_in, 0, optsize);
0319
0320 memset(&exp_passive_hdr_stg, 0, sizeof(exp_passive_hdr_stg));
0321 memset(&exp_active_hdr_stg, 0, sizeof(exp_active_hdr_stg));
0322 exp_active_hdr_stg.active = true;
0323
0324 err = bpf_map_get_next_key(lport_linum_map_fd, NULL, &lport);
0325 while (!err) {
0326 bpf_map_delete_elem(lport_linum_map_fd, &lport);
0327 err = bpf_map_get_next_key(lport_linum_map_fd, &lport, &lport);
0328 }
0329 }
0330
0331 static void fastopen_estab(void)
0332 {
0333 struct bpf_link *link;
0334 struct sk_fds sk_fds;
0335
0336 hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map);
0337 lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map);
0338
0339 exp_passive_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
0340 exp_passive_estab_in.rand = 0xfa;
0341 exp_passive_estab_in.max_delack_ms = 11;
0342
0343 exp_active_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
0344 exp_active_estab_in.rand = 0xce;
0345 exp_active_estab_in.max_delack_ms = 22;
0346
0347 exp_passive_hdr_stg.fastopen = true;
0348
0349 prepare_out();
0350
0351
0352 if (write_sysctl("/proc/sys/net/ipv4/tcp_fastopen", "1543"))
0353 return;
0354
0355 link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd);
0356 if (!ASSERT_OK_PTR(link, "attach_cgroup(estab)"))
0357 return;
0358
0359 if (sk_fds_connect(&sk_fds, true)) {
0360 bpf_link__destroy(link);
0361 return;
0362 }
0363
0364 check_hdr_and_close_fds(&sk_fds);
0365 bpf_link__destroy(link);
0366 }
0367
0368 static void syncookie_estab(void)
0369 {
0370 struct bpf_link *link;
0371 struct sk_fds sk_fds;
0372
0373 hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map);
0374 lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map);
0375
0376 exp_passive_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
0377 exp_passive_estab_in.rand = 0xfa;
0378 exp_passive_estab_in.max_delack_ms = 11;
0379
0380 exp_active_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS |
0381 OPTION_F_RESEND;
0382 exp_active_estab_in.rand = 0xce;
0383 exp_active_estab_in.max_delack_ms = 22;
0384
0385 exp_passive_hdr_stg.syncookie = true;
0386 exp_active_hdr_stg.resend_syn = true,
0387
0388 prepare_out();
0389
0390
0391
0392
0393 skel->bss->passive_synack_out.flags &= ~OPTION_F_RESEND;
0394
0395
0396 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "2"))
0397 return;
0398
0399 link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd);
0400 if (!ASSERT_OK_PTR(link, "attach_cgroup(estab)"))
0401 return;
0402
0403 if (sk_fds_connect(&sk_fds, false)) {
0404 bpf_link__destroy(link);
0405 return;
0406 }
0407
0408 check_hdr_and_close_fds(&sk_fds);
0409 bpf_link__destroy(link);
0410 }
0411
0412 static void fin(void)
0413 {
0414 struct bpf_link *link;
0415 struct sk_fds sk_fds;
0416
0417 hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map);
0418 lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map);
0419
0420 exp_passive_fin_in.flags = OPTION_F_RAND;
0421 exp_passive_fin_in.rand = 0xfa;
0422
0423 exp_active_fin_in.flags = OPTION_F_RAND;
0424 exp_active_fin_in.rand = 0xce;
0425
0426 prepare_out();
0427
0428 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1"))
0429 return;
0430
0431 link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd);
0432 if (!ASSERT_OK_PTR(link, "attach_cgroup(estab)"))
0433 return;
0434
0435 if (sk_fds_connect(&sk_fds, false)) {
0436 bpf_link__destroy(link);
0437 return;
0438 }
0439
0440 check_hdr_and_close_fds(&sk_fds);
0441 bpf_link__destroy(link);
0442 }
0443
0444 static void __simple_estab(bool exprm)
0445 {
0446 struct bpf_link *link;
0447 struct sk_fds sk_fds;
0448
0449 hdr_stg_map_fd = bpf_map__fd(skel->maps.hdr_stg_map);
0450 lport_linum_map_fd = bpf_map__fd(skel->maps.lport_linum_map);
0451
0452 exp_passive_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
0453 exp_passive_estab_in.rand = 0xfa;
0454 exp_passive_estab_in.max_delack_ms = 11;
0455
0456 exp_active_estab_in.flags = OPTION_F_RAND | OPTION_F_MAX_DELACK_MS;
0457 exp_active_estab_in.rand = 0xce;
0458 exp_active_estab_in.max_delack_ms = 22;
0459
0460 prepare_out();
0461
0462 if (!exprm) {
0463 skel->data->test_kind = 0xB9;
0464 skel->data->test_magic = 0;
0465 }
0466
0467 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1"))
0468 return;
0469
0470 link = bpf_program__attach_cgroup(skel->progs.estab, cg_fd);
0471 if (!ASSERT_OK_PTR(link, "attach_cgroup(estab)"))
0472 return;
0473
0474 if (sk_fds_connect(&sk_fds, false)) {
0475 bpf_link__destroy(link);
0476 return;
0477 }
0478
0479 check_hdr_and_close_fds(&sk_fds);
0480 bpf_link__destroy(link);
0481 }
0482
0483 static void no_exprm_estab(void)
0484 {
0485 __simple_estab(false);
0486 }
0487
0488 static void simple_estab(void)
0489 {
0490 __simple_estab(true);
0491 }
0492
0493 static void misc(void)
0494 {
0495 const char send_msg[] = "MISC!!!";
0496 char recv_msg[sizeof(send_msg)];
0497 const unsigned int nr_data = 2;
0498 struct bpf_link *link;
0499 struct sk_fds sk_fds;
0500 int i, ret;
0501
0502 lport_linum_map_fd = bpf_map__fd(misc_skel->maps.lport_linum_map);
0503
0504 if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies", "1"))
0505 return;
0506
0507 link = bpf_program__attach_cgroup(misc_skel->progs.misc_estab, cg_fd);
0508 if (!ASSERT_OK_PTR(link, "attach_cgroup(misc_estab)"))
0509 return;
0510
0511 if (sk_fds_connect(&sk_fds, false)) {
0512 bpf_link__destroy(link);
0513 return;
0514 }
0515
0516 for (i = 0; i < nr_data; i++) {
0517
0518 ret = send(sk_fds.active_fd, send_msg, sizeof(send_msg),
0519 MSG_EOR);
0520 if (CHECK(ret != sizeof(send_msg), "send(msg)", "ret:%d\n",
0521 ret))
0522 goto check_linum;
0523
0524 ret = read(sk_fds.passive_fd, recv_msg, sizeof(recv_msg));
0525 if (CHECK(ret != sizeof(send_msg), "read(msg)", "ret:%d\n",
0526 ret))
0527 goto check_linum;
0528 }
0529
0530 if (sk_fds_shutdown(&sk_fds))
0531 goto check_linum;
0532
0533 CHECK(misc_skel->bss->nr_syn != 1, "unexpected nr_syn",
0534 "expected (1) != actual (%u)\n",
0535 misc_skel->bss->nr_syn);
0536
0537 CHECK(misc_skel->bss->nr_data != nr_data, "unexpected nr_data",
0538 "expected (%u) != actual (%u)\n",
0539 nr_data, misc_skel->bss->nr_data);
0540
0541
0542 CHECK(misc_skel->bss->nr_pure_ack != 1 &&
0543 misc_skel->bss->nr_pure_ack != 2,
0544 "unexpected nr_pure_ack",
0545 "expected (1 or 2) != actual (%u)\n",
0546 misc_skel->bss->nr_pure_ack);
0547
0548 CHECK(misc_skel->bss->nr_fin != 1, "unexpected nr_fin",
0549 "expected (1) != actual (%u)\n",
0550 misc_skel->bss->nr_fin);
0551
0552 check_linum:
0553 CHECK_FAIL(check_error_linum(&sk_fds));
0554 sk_fds_close(&sk_fds);
0555 bpf_link__destroy(link);
0556 }
0557
0558 struct test {
0559 const char *desc;
0560 void (*run)(void);
0561 };
0562
0563 #define DEF_TEST(name) { #name, name }
0564 static struct test tests[] = {
0565 DEF_TEST(simple_estab),
0566 DEF_TEST(no_exprm_estab),
0567 DEF_TEST(syncookie_estab),
0568 DEF_TEST(fastopen_estab),
0569 DEF_TEST(fin),
0570 DEF_TEST(misc),
0571 };
0572
0573 void test_tcp_hdr_options(void)
0574 {
0575 int i;
0576
0577 skel = test_tcp_hdr_options__open_and_load();
0578 if (CHECK(!skel, "open and load skel", "failed"))
0579 return;
0580
0581 misc_skel = test_misc_tcp_hdr_options__open_and_load();
0582 if (CHECK(!misc_skel, "open and load misc test skel", "failed"))
0583 goto skel_destroy;
0584
0585 cg_fd = test__join_cgroup(CG_NAME);
0586 if (CHECK_FAIL(cg_fd < 0))
0587 goto skel_destroy;
0588
0589 for (i = 0; i < ARRAY_SIZE(tests); i++) {
0590 if (!test__start_subtest(tests[i].desc))
0591 continue;
0592
0593 if (create_netns())
0594 break;
0595
0596 tests[i].run();
0597
0598 reset_test();
0599 }
0600
0601 close(cg_fd);
0602 skel_destroy:
0603 test_misc_tcp_hdr_options__destroy(misc_skel);
0604 test_tcp_hdr_options__destroy(skel);
0605 }