xref: /linux/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c (revision 4fd18fc38757217c746aa063ba9e4729814dc737)
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 
43 static 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 
57 static 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 
77 static 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 
84 static 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 
90 static 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 
97 static 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 
118 static 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 
173 error_close:
174 	close(sk_fds->active_fd);
175 	close(sk_fds->srv_fd);
176 
177 error:
178 	memset(sk_fds, -1, sizeof(*sk_fds));
179 	return -1;
180 }
181 
182 static 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 
196 static 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 
216 static 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 
241 static 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 
279 check_linum:
280 	CHECK_FAIL(check_error_linum(sk_fds));
281 	sk_fds_close(sk_fds);
282 }
283 
284 static 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 
293 static 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 
331 static 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 
369 static 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 
414 static 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 
447 static 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 
487 static void no_exprm_estab(void)
488 {
489 	__simple_estab(false);
490 }
491 
492 static void simple_estab(void)
493 {
494 	__simple_estab(true);
495 }
496 
497 static 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 
557 check_linum:
558 	CHECK_FAIL(check_error_linum(&sk_fds));
559 	sk_fds_close(&sk_fds);
560 	bpf_link__destroy(link);
561 }
562 
563 struct test {
564 	const char *desc;
565 	void (*run)(void);
566 };
567 
568 #define DEF_TEST(name) { #name, name }
569 static 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 
578 void 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);
607 skel_destroy:
608 	test_misc_tcp_hdr_options__destroy(misc_skel);
609 	test_tcp_hdr_options__destroy(skel);
610 }
611