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