xref: /linux/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c (revision 92d33063c081a82d25dd08a9cce03947c8ed9164)
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 (!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  
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  
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  
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  
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  
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  
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_OK(memcmp(exp, act, sizeof(*exp)), hdr_desc)) {
155  		print_option(exp, "expected: ");
156  		print_option(act, "  actual: ");
157  		return -1;
158  	}
159  
160  	return 0;
161  }
162  
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_OK(memcmp(exp, &act, sizeof(*exp)), stg_desc)) {
173  		print_hdr_stg(exp, "expected: ");
174  		print_hdr_stg(&act, "  actual: ");
175  		return -1;
176  	}
177  
178  	return 0;
179  }
180  
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  
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  
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  
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  
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  
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  
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  
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  
447  static void no_exprm_estab(void)
448  {
449  	__simple_estab(false);
450  }
451  
452  static void simple_estab(void)
453  {
454  	__simple_estab(true);
455  }
456  
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  
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