Linux Audio

Check our new training course

Loading...
v6.13.7
  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}
v5.14.15
  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}