xref: /linux/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c (revision a1c613ae4c322ddd58d5a8539dbfba2a0380a8c0)
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 
21 static struct bpf_test_option exp_passive_estab_in;
22 static struct bpf_test_option exp_active_estab_in;
23 static struct bpf_test_option exp_passive_fin_in;
24 static struct bpf_test_option exp_active_fin_in;
25 static struct hdr_stg exp_passive_hdr_stg;
26 static struct hdr_stg exp_active_hdr_stg = { .active = true, };
27 
28 static struct test_misc_tcp_hdr_options *misc_skel;
29 static struct test_tcp_hdr_options *skel;
30 static int lport_linum_map_fd;
31 static int hdr_stg_map_fd;
32 static __u32 duration;
33 static int cg_fd;
34 
35 struct sk_fds {
36 	int srv_fd;
37 	int passive_fd;
38 	int active_fd;
39 	int passive_lport;
40 	int active_lport;
41 };
42 
create_netns(void)43 static 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 
print_hdr_stg(const struct hdr_stg * hdr_stg,const char * prefix)54 static 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 
print_option(const struct bpf_test_option * opt,const char * prefix)61 static 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 
sk_fds_close(struct sk_fds * sk_fds)67 static 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 
sk_fds_shutdown(struct sk_fds * sk_fds)74 static 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 
sk_fds_connect(struct sk_fds * sk_fds,bool fast_open)91 static 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 
141 error_close:
142 	close(sk_fds->active_fd);
143 	close(sk_fds->srv_fd);
144 
145 error:
146 	memset(sk_fds, -1, sizeof(*sk_fds));
147 	return -1;
148 }
149 
check_hdr_opt(const struct bpf_test_option * exp,const struct bpf_test_option * act,const char * hdr_desc)150 static 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 
check_hdr_stg(const struct hdr_stg * exp,int fd,const char * stg_desc)163 static 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 
check_error_linum(const struct sk_fds * sk_fds)181 static 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 
check_hdr_and_close_fds(struct sk_fds * sk_fds)206 static 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 
243 check_linum:
244 	ASSERT_FALSE(check_error_linum(sk_fds), "check_error_linum");
245 	sk_fds_close(sk_fds);
246 }
247 
prepare_out(void)248 static 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 
reset_test(void)257 static 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 
fastopen_estab(void)295 static 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 
syncookie_estab(void)332 static 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 
fin(void)376 static 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 
__simple_estab(bool exprm)408 static 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 
no_exprm_estab(void)447 static void no_exprm_estab(void)
448 {
449 	__simple_estab(false);
450 }
451 
simple_estab(void)452 static void simple_estab(void)
453 {
454 	__simple_estab(true);
455 }
456 
misc(void)457 static 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 
510 check_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 
516 struct test {
517 	const char *desc;
518 	void (*run)(void);
519 };
520 
521 #define DEF_TEST(name) { #name, name }
522 static 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 
test_tcp_hdr_options(void)531 void 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);
560 skel_destroy:
561 	test_misc_tcp_hdr_options__destroy(misc_skel);
562 	test_tcp_hdr_options__destroy(skel);
563 }
564