Linux Audio

Check our new training course

Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0
  2#define _GNU_SOURCE
  3#include <test_progs.h>
  4#include <network_helpers.h>
 
 
  5#include <linux/if_tun.h>
  6#include <sys/uio.h>
  7
  8#include "bpf_flow.skel.h"
  9
 10#define FLOW_CONTINUE_SADDR 0x7f00007f /* 127.0.0.127 */
 11
 12#ifndef IP_MF
 13#define IP_MF 0x2000
 14#endif
 15
 16#define CHECK_FLOW_KEYS(desc, got, expected)				\
 17	_CHECK(memcmp(&got, &expected, sizeof(got)) != 0,		\
 18	      desc,							\
 19	      topts.duration,						\
 20	      "nhoff=%u/%u "						\
 21	      "thoff=%u/%u "						\
 22	      "addr_proto=0x%x/0x%x "					\
 23	      "is_frag=%u/%u "						\
 24	      "is_first_frag=%u/%u "					\
 25	      "is_encap=%u/%u "						\
 26	      "ip_proto=0x%x/0x%x "					\
 27	      "n_proto=0x%x/0x%x "					\
 28	      "flow_label=0x%x/0x%x "					\
 29	      "sport=%u/%u "						\
 30	      "dport=%u/%u\n",						\
 31	      got.nhoff, expected.nhoff,				\
 32	      got.thoff, expected.thoff,				\
 33	      got.addr_proto, expected.addr_proto,			\
 34	      got.is_frag, expected.is_frag,				\
 35	      got.is_first_frag, expected.is_first_frag,		\
 36	      got.is_encap, expected.is_encap,				\
 37	      got.ip_proto, expected.ip_proto,				\
 38	      got.n_proto, expected.n_proto,				\
 39	      got.flow_label, expected.flow_label,			\
 40	      got.sport, expected.sport,				\
 41	      got.dport, expected.dport)
 42
 43struct ipv4_pkt {
 44	struct ethhdr eth;
 45	struct iphdr iph;
 46	struct tcphdr tcp;
 47} __packed;
 48
 49struct ipip_pkt {
 50	struct ethhdr eth;
 51	struct iphdr iph;
 52	struct iphdr iph_inner;
 53	struct tcphdr tcp;
 54} __packed;
 55
 56struct svlan_ipv4_pkt {
 57	struct ethhdr eth;
 58	__u16 vlan_tci;
 59	__u16 vlan_proto;
 60	struct iphdr iph;
 61	struct tcphdr tcp;
 62} __packed;
 63
 64struct ipv6_pkt {
 65	struct ethhdr eth;
 66	struct ipv6hdr iph;
 67	struct tcphdr tcp;
 68} __packed;
 69
 70struct ipv6_frag_pkt {
 71	struct ethhdr eth;
 72	struct ipv6hdr iph;
 73	struct frag_hdr {
 74		__u8 nexthdr;
 75		__u8 reserved;
 76		__be16 frag_off;
 77		__be32 identification;
 78	} ipf;
 79	struct tcphdr tcp;
 80} __packed;
 81
 82struct dvlan_ipv6_pkt {
 83	struct ethhdr eth;
 84	__u16 vlan_tci;
 85	__u16 vlan_proto;
 86	__u16 vlan_tci2;
 87	__u16 vlan_proto2;
 88	struct ipv6hdr iph;
 89	struct tcphdr tcp;
 90} __packed;
 91
 92struct test {
 93	const char *name;
 94	union {
 95		struct ipv4_pkt ipv4;
 96		struct svlan_ipv4_pkt svlan_ipv4;
 97		struct ipip_pkt ipip;
 98		struct ipv6_pkt ipv6;
 99		struct ipv6_frag_pkt ipv6_frag;
100		struct dvlan_ipv6_pkt dvlan_ipv6;
101	} pkt;
102	struct bpf_flow_keys keys;
103	__u32 flags;
104	__u32 retval;
105};
106
107#define VLAN_HLEN	4
108
109static __u32 duration;
110struct test tests[] = {
111	{
112		.name = "ipv4",
113		.pkt.ipv4 = {
114			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
115			.iph.ihl = 5,
116			.iph.protocol = IPPROTO_TCP,
117			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
118			.tcp.doff = 5,
119			.tcp.source = 80,
120			.tcp.dest = 8080,
121		},
122		.keys = {
123			.nhoff = ETH_HLEN,
124			.thoff = ETH_HLEN + sizeof(struct iphdr),
125			.addr_proto = ETH_P_IP,
126			.ip_proto = IPPROTO_TCP,
127			.n_proto = __bpf_constant_htons(ETH_P_IP),
128			.sport = 80,
129			.dport = 8080,
130		},
131		.retval = BPF_OK,
132	},
133	{
134		.name = "ipv6",
135		.pkt.ipv6 = {
136			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
137			.iph.nexthdr = IPPROTO_TCP,
138			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
139			.tcp.doff = 5,
140			.tcp.source = 80,
141			.tcp.dest = 8080,
142		},
143		.keys = {
144			.nhoff = ETH_HLEN,
145			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
146			.addr_proto = ETH_P_IPV6,
147			.ip_proto = IPPROTO_TCP,
148			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
149			.sport = 80,
150			.dport = 8080,
151		},
152		.retval = BPF_OK,
153	},
154	{
155		.name = "802.1q-ipv4",
156		.pkt.svlan_ipv4 = {
157			.eth.h_proto = __bpf_constant_htons(ETH_P_8021Q),
158			.vlan_proto = __bpf_constant_htons(ETH_P_IP),
159			.iph.ihl = 5,
160			.iph.protocol = IPPROTO_TCP,
161			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
162			.tcp.doff = 5,
163			.tcp.source = 80,
164			.tcp.dest = 8080,
165		},
166		.keys = {
167			.nhoff = ETH_HLEN + VLAN_HLEN,
168			.thoff = ETH_HLEN + VLAN_HLEN + sizeof(struct iphdr),
169			.addr_proto = ETH_P_IP,
170			.ip_proto = IPPROTO_TCP,
171			.n_proto = __bpf_constant_htons(ETH_P_IP),
172			.sport = 80,
173			.dport = 8080,
174		},
175		.retval = BPF_OK,
176	},
177	{
178		.name = "802.1ad-ipv6",
179		.pkt.dvlan_ipv6 = {
180			.eth.h_proto = __bpf_constant_htons(ETH_P_8021AD),
181			.vlan_proto = __bpf_constant_htons(ETH_P_8021Q),
182			.vlan_proto2 = __bpf_constant_htons(ETH_P_IPV6),
183			.iph.nexthdr = IPPROTO_TCP,
184			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
185			.tcp.doff = 5,
186			.tcp.source = 80,
187			.tcp.dest = 8080,
188		},
189		.keys = {
190			.nhoff = ETH_HLEN + VLAN_HLEN * 2,
191			.thoff = ETH_HLEN + VLAN_HLEN * 2 +
192				sizeof(struct ipv6hdr),
193			.addr_proto = ETH_P_IPV6,
194			.ip_proto = IPPROTO_TCP,
195			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
196			.sport = 80,
197			.dport = 8080,
198		},
199		.retval = BPF_OK,
200	},
201	{
202		.name = "ipv4-frag",
203		.pkt.ipv4 = {
204			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
205			.iph.ihl = 5,
206			.iph.protocol = IPPROTO_TCP,
207			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
208			.iph.frag_off = __bpf_constant_htons(IP_MF),
209			.tcp.doff = 5,
210			.tcp.source = 80,
211			.tcp.dest = 8080,
212		},
213		.keys = {
214			.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
215			.nhoff = ETH_HLEN,
216			.thoff = ETH_HLEN + sizeof(struct iphdr),
217			.addr_proto = ETH_P_IP,
218			.ip_proto = IPPROTO_TCP,
219			.n_proto = __bpf_constant_htons(ETH_P_IP),
220			.is_frag = true,
221			.is_first_frag = true,
222			.sport = 80,
223			.dport = 8080,
224		},
225		.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
226		.retval = BPF_OK,
227	},
228	{
229		.name = "ipv4-no-frag",
230		.pkt.ipv4 = {
231			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
232			.iph.ihl = 5,
233			.iph.protocol = IPPROTO_TCP,
234			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
235			.iph.frag_off = __bpf_constant_htons(IP_MF),
236			.tcp.doff = 5,
237			.tcp.source = 80,
238			.tcp.dest = 8080,
239		},
240		.keys = {
241			.nhoff = ETH_HLEN,
242			.thoff = ETH_HLEN + sizeof(struct iphdr),
243			.addr_proto = ETH_P_IP,
244			.ip_proto = IPPROTO_TCP,
245			.n_proto = __bpf_constant_htons(ETH_P_IP),
246			.is_frag = true,
247			.is_first_frag = true,
248		},
249		.retval = BPF_OK,
250	},
251	{
252		.name = "ipv6-frag",
253		.pkt.ipv6_frag = {
254			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
255			.iph.nexthdr = IPPROTO_FRAGMENT,
256			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
257			.ipf.nexthdr = IPPROTO_TCP,
258			.tcp.doff = 5,
259			.tcp.source = 80,
260			.tcp.dest = 8080,
261		},
262		.keys = {
263			.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
264			.nhoff = ETH_HLEN,
265			.thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
266				sizeof(struct frag_hdr),
267			.addr_proto = ETH_P_IPV6,
268			.ip_proto = IPPROTO_TCP,
269			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
270			.is_frag = true,
271			.is_first_frag = true,
272			.sport = 80,
273			.dport = 8080,
274		},
275		.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
276		.retval = BPF_OK,
277	},
278	{
279		.name = "ipv6-no-frag",
280		.pkt.ipv6_frag = {
281			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
282			.iph.nexthdr = IPPROTO_FRAGMENT,
283			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
284			.ipf.nexthdr = IPPROTO_TCP,
285			.tcp.doff = 5,
286			.tcp.source = 80,
287			.tcp.dest = 8080,
288		},
289		.keys = {
290			.nhoff = ETH_HLEN,
291			.thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
292				sizeof(struct frag_hdr),
293			.addr_proto = ETH_P_IPV6,
294			.ip_proto = IPPROTO_TCP,
295			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
296			.is_frag = true,
297			.is_first_frag = true,
298		},
299		.retval = BPF_OK,
300	},
301	{
302		.name = "ipv6-flow-label",
303		.pkt.ipv6 = {
304			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
305			.iph.nexthdr = IPPROTO_TCP,
306			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
307			.iph.flow_lbl = { 0xb, 0xee, 0xef },
308			.tcp.doff = 5,
309			.tcp.source = 80,
310			.tcp.dest = 8080,
311		},
312		.keys = {
313			.nhoff = ETH_HLEN,
314			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
315			.addr_proto = ETH_P_IPV6,
316			.ip_proto = IPPROTO_TCP,
317			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
318			.sport = 80,
319			.dport = 8080,
320			.flow_label = __bpf_constant_htonl(0xbeeef),
321		},
322		.retval = BPF_OK,
323	},
324	{
325		.name = "ipv6-no-flow-label",
326		.pkt.ipv6 = {
327			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
328			.iph.nexthdr = IPPROTO_TCP,
329			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
330			.iph.flow_lbl = { 0xb, 0xee, 0xef },
331			.tcp.doff = 5,
332			.tcp.source = 80,
333			.tcp.dest = 8080,
334		},
335		.keys = {
336			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
337			.nhoff = ETH_HLEN,
338			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
339			.addr_proto = ETH_P_IPV6,
340			.ip_proto = IPPROTO_TCP,
341			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
342			.flow_label = __bpf_constant_htonl(0xbeeef),
343		},
344		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
345		.retval = BPF_OK,
346	},
347	{
348		.name = "ipv6-empty-flow-label",
349		.pkt.ipv6 = {
350			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
351			.iph.nexthdr = IPPROTO_TCP,
352			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
353			.iph.flow_lbl = { 0x00, 0x00, 0x00 },
354			.tcp.doff = 5,
355			.tcp.source = 80,
356			.tcp.dest = 8080,
357		},
358		.keys = {
359			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
360			.nhoff = ETH_HLEN,
361			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
362			.addr_proto = ETH_P_IPV6,
363			.ip_proto = IPPROTO_TCP,
364			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
365			.sport = 80,
366			.dport = 8080,
367		},
368		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
369		.retval = BPF_OK,
370	},
371	{
372		.name = "ipip-encap",
373		.pkt.ipip = {
374			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
375			.iph.ihl = 5,
376			.iph.protocol = IPPROTO_IPIP,
377			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
378			.iph_inner.ihl = 5,
379			.iph_inner.protocol = IPPROTO_TCP,
380			.iph_inner.tot_len =
381				__bpf_constant_htons(MAGIC_BYTES -
382				sizeof(struct iphdr)),
383			.tcp.doff = 5,
384			.tcp.source = 80,
385			.tcp.dest = 8080,
386		},
387		.keys = {
388			.nhoff = ETH_HLEN,
389			.thoff = ETH_HLEN + sizeof(struct iphdr) +
390				sizeof(struct iphdr),
391			.addr_proto = ETH_P_IP,
392			.ip_proto = IPPROTO_TCP,
393			.n_proto = __bpf_constant_htons(ETH_P_IP),
394			.is_encap = true,
395			.sport = 80,
396			.dport = 8080,
397		},
398		.retval = BPF_OK,
399	},
400	{
401		.name = "ipip-no-encap",
402		.pkt.ipip = {
403			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
404			.iph.ihl = 5,
405			.iph.protocol = IPPROTO_IPIP,
406			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
407			.iph_inner.ihl = 5,
408			.iph_inner.protocol = IPPROTO_TCP,
409			.iph_inner.tot_len =
410				__bpf_constant_htons(MAGIC_BYTES -
411				sizeof(struct iphdr)),
412			.tcp.doff = 5,
413			.tcp.source = 80,
414			.tcp.dest = 8080,
415		},
416		.keys = {
417			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
418			.nhoff = ETH_HLEN,
419			.thoff = ETH_HLEN + sizeof(struct iphdr),
420			.addr_proto = ETH_P_IP,
421			.ip_proto = IPPROTO_IPIP,
422			.n_proto = __bpf_constant_htons(ETH_P_IP),
423			.is_encap = true,
424		},
425		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
426		.retval = BPF_OK,
427	},
428	{
429		.name = "ipip-encap-dissector-continue",
430		.pkt.ipip = {
431			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
432			.iph.ihl = 5,
433			.iph.protocol = IPPROTO_IPIP,
434			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
435			.iph.saddr = __bpf_constant_htonl(FLOW_CONTINUE_SADDR),
436			.iph_inner.ihl = 5,
437			.iph_inner.protocol = IPPROTO_TCP,
438			.iph_inner.tot_len =
439				__bpf_constant_htons(MAGIC_BYTES -
440				sizeof(struct iphdr)),
441			.tcp.doff = 5,
442			.tcp.source = 99,
443			.tcp.dest = 9090,
444		},
445		.retval = BPF_FLOW_DISSECTOR_CONTINUE,
446	},
447};
448
449static int create_tap(const char *ifname)
450{
451	struct ifreq ifr = {
452		.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS,
453	};
454	int fd, ret;
455
456	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
457
458	fd = open("/dev/net/tun", O_RDWR);
459	if (fd < 0)
460		return -1;
461
462	ret = ioctl(fd, TUNSETIFF, &ifr);
463	if (ret)
464		return -1;
465
466	return fd;
467}
468
469static int tx_tap(int fd, void *pkt, size_t len)
470{
471	struct iovec iov[] = {
472		{
473			.iov_len = len,
474			.iov_base = pkt,
475		},
476	};
477	return writev(fd, iov, ARRAY_SIZE(iov));
478}
479
480static int ifup(const char *ifname)
481{
482	struct ifreq ifr = {};
483	int sk, ret;
484
485	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
486
487	sk = socket(PF_INET, SOCK_DGRAM, 0);
488	if (sk < 0)
489		return -1;
490
491	ret = ioctl(sk, SIOCGIFFLAGS, &ifr);
492	if (ret) {
493		close(sk);
494		return -1;
495	}
496
497	ifr.ifr_flags |= IFF_UP;
498	ret = ioctl(sk, SIOCSIFFLAGS, &ifr);
499	if (ret) {
500		close(sk);
501		return -1;
502	}
503
504	close(sk);
505	return 0;
506}
507
508static int init_prog_array(struct bpf_object *obj, struct bpf_map *prog_array)
509{
510	int i, err, map_fd, prog_fd;
511	struct bpf_program *prog;
512	char prog_name[32];
513
514	map_fd = bpf_map__fd(prog_array);
515	if (map_fd < 0)
516		return -1;
517
518	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
519		snprintf(prog_name, sizeof(prog_name), "flow_dissector_%d", i);
520
521		prog = bpf_object__find_program_by_name(obj, prog_name);
522		if (!prog)
523			return -1;
524
525		prog_fd = bpf_program__fd(prog);
526		if (prog_fd < 0)
527			return -1;
528
529		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
530		if (err)
531			return -1;
532	}
533	return 0;
534}
535
536static void run_tests_skb_less(int tap_fd, struct bpf_map *keys)
537{
538	int i, err, keys_fd;
539
540	keys_fd = bpf_map__fd(keys);
541	if (CHECK(keys_fd < 0, "bpf_map__fd", "err %d\n", keys_fd))
542		return;
543
544	for (i = 0; i < ARRAY_SIZE(tests); i++) {
545		/* Keep in sync with 'flags' from eth_get_headlen. */
546		__u32 eth_get_headlen_flags =
547			BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
548		LIBBPF_OPTS(bpf_test_run_opts, topts);
549		struct bpf_flow_keys flow_keys = {};
550		__u32 key = (__u32)(tests[i].keys.sport) << 16 |
551			    tests[i].keys.dport;
552
553		/* For skb-less case we can't pass input flags; run
554		 * only the tests that have a matching set of flags.
555		 */
556
557		if (tests[i].flags != eth_get_headlen_flags)
558			continue;
559
560		err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
561		CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno);
562
563		/* check the stored flow_keys only if BPF_OK expected */
564		if (tests[i].retval != BPF_OK)
565			continue;
566
567		err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
568		ASSERT_OK(err, "bpf_map_lookup_elem");
569
570		CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
571
572		err = bpf_map_delete_elem(keys_fd, &key);
573		ASSERT_OK(err, "bpf_map_delete_elem");
574	}
575}
576
577static void test_skb_less_prog_attach(struct bpf_flow *skel, int tap_fd)
578{
579	int err, prog_fd;
580
581	prog_fd = bpf_program__fd(skel->progs._dissect);
582	if (CHECK(prog_fd < 0, "bpf_program__fd", "err %d\n", prog_fd))
583		return;
584
585	err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
586	if (CHECK(err, "bpf_prog_attach", "err %d errno %d\n", err, errno))
587		return;
588
589	run_tests_skb_less(tap_fd, skel->maps.last_dissection);
590
591	err = bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
592	CHECK(err, "bpf_prog_detach2", "err %d errno %d\n", err, errno);
593}
594
595static void test_skb_less_link_create(struct bpf_flow *skel, int tap_fd)
596{
597	struct bpf_link *link;
598	int err, net_fd;
599
600	net_fd = open("/proc/self/ns/net", O_RDONLY);
601	if (CHECK(net_fd < 0, "open(/proc/self/ns/net)", "err %d\n", errno))
602		return;
603
604	link = bpf_program__attach_netns(skel->progs._dissect, net_fd);
605	if (!ASSERT_OK_PTR(link, "attach_netns"))
606		goto out_close;
607
608	run_tests_skb_less(tap_fd, skel->maps.last_dissection);
609
610	err = bpf_link__destroy(link);
611	CHECK(err, "bpf_link__destroy", "err %d\n", err);
612out_close:
613	close(net_fd);
614}
615
616void test_flow_dissector(void)
617{
618	int i, err, prog_fd, keys_fd = -1, tap_fd;
619	struct bpf_flow *skel;
620
621	skel = bpf_flow__open_and_load();
622	if (CHECK(!skel, "skel", "failed to open/load skeleton\n"))
623		return;
624
625	prog_fd = bpf_program__fd(skel->progs._dissect);
626	if (CHECK(prog_fd < 0, "bpf_program__fd", "err %d\n", prog_fd))
627		goto out_destroy_skel;
628	keys_fd = bpf_map__fd(skel->maps.last_dissection);
629	if (CHECK(keys_fd < 0, "bpf_map__fd", "err %d\n", keys_fd))
630		goto out_destroy_skel;
631	err = init_prog_array(skel->obj, skel->maps.jmp_table);
632	if (CHECK(err, "init_prog_array", "err %d\n", err))
633		goto out_destroy_skel;
634
635	for (i = 0; i < ARRAY_SIZE(tests); i++) {
636		struct bpf_flow_keys flow_keys;
637		LIBBPF_OPTS(bpf_test_run_opts, topts,
638			.data_in = &tests[i].pkt,
639			.data_size_in = sizeof(tests[i].pkt),
640			.data_out = &flow_keys,
641		);
642		static struct bpf_flow_keys ctx = {};
643
644		if (tests[i].flags) {
645			topts.ctx_in = &ctx;
646			topts.ctx_size_in = sizeof(ctx);
647			ctx.flags = tests[i].flags;
648		}
649
650		err = bpf_prog_test_run_opts(prog_fd, &topts);
651		ASSERT_OK(err, "test_run");
652		ASSERT_EQ(topts.retval, tests[i].retval, "test_run retval");
653
654		/* check the resulting flow_keys only if BPF_OK returned */
655		if (topts.retval != BPF_OK)
656			continue;
657		ASSERT_EQ(topts.data_size_out, sizeof(flow_keys),
658			  "test_run data_size_out");
659		CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
660	}
661
662	/* Do the same tests but for skb-less flow dissector.
663	 * We use a known path in the net/tun driver that calls
664	 * eth_get_headlen and we manually export bpf_flow_keys
665	 * via BPF map in this case.
666	 */
667
668	tap_fd = create_tap("tap0");
669	CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d\n", tap_fd, errno);
670	err = ifup("tap0");
671	CHECK(err, "ifup", "err %d errno %d\n", err, errno);
672
673	/* Test direct prog attachment */
674	test_skb_less_prog_attach(skel, tap_fd);
675	/* Test indirect prog attachment via link */
676	test_skb_less_link_create(skel, tap_fd);
677
678	close(tap_fd);
679out_destroy_skel:
680	bpf_flow__destroy(skel);
681}
v6.8
  1// SPDX-License-Identifier: GPL-2.0
 
  2#include <test_progs.h>
  3#include <network_helpers.h>
  4#include <error.h>
  5#include <linux/if.h>
  6#include <linux/if_tun.h>
  7#include <sys/uio.h>
  8
  9#include "bpf_flow.skel.h"
 10
 11#define FLOW_CONTINUE_SADDR 0x7f00007f /* 127.0.0.127 */
 12
 13#ifndef IP_MF
 14#define IP_MF 0x2000
 15#endif
 16
 17#define CHECK_FLOW_KEYS(desc, got, expected)				\
 18	_CHECK(memcmp(&got, &expected, sizeof(got)) != 0,		\
 19	      desc,							\
 20	      topts.duration,						\
 21	      "nhoff=%u/%u "						\
 22	      "thoff=%u/%u "						\
 23	      "addr_proto=0x%x/0x%x "					\
 24	      "is_frag=%u/%u "						\
 25	      "is_first_frag=%u/%u "					\
 26	      "is_encap=%u/%u "						\
 27	      "ip_proto=0x%x/0x%x "					\
 28	      "n_proto=0x%x/0x%x "					\
 29	      "flow_label=0x%x/0x%x "					\
 30	      "sport=%u/%u "						\
 31	      "dport=%u/%u\n",						\
 32	      got.nhoff, expected.nhoff,				\
 33	      got.thoff, expected.thoff,				\
 34	      got.addr_proto, expected.addr_proto,			\
 35	      got.is_frag, expected.is_frag,				\
 36	      got.is_first_frag, expected.is_first_frag,		\
 37	      got.is_encap, expected.is_encap,				\
 38	      got.ip_proto, expected.ip_proto,				\
 39	      got.n_proto, expected.n_proto,				\
 40	      got.flow_label, expected.flow_label,			\
 41	      got.sport, expected.sport,				\
 42	      got.dport, expected.dport)
 43
 44struct ipv4_pkt {
 45	struct ethhdr eth;
 46	struct iphdr iph;
 47	struct tcphdr tcp;
 48} __packed;
 49
 50struct ipip_pkt {
 51	struct ethhdr eth;
 52	struct iphdr iph;
 53	struct iphdr iph_inner;
 54	struct tcphdr tcp;
 55} __packed;
 56
 57struct svlan_ipv4_pkt {
 58	struct ethhdr eth;
 59	__u16 vlan_tci;
 60	__u16 vlan_proto;
 61	struct iphdr iph;
 62	struct tcphdr tcp;
 63} __packed;
 64
 65struct ipv6_pkt {
 66	struct ethhdr eth;
 67	struct ipv6hdr iph;
 68	struct tcphdr tcp;
 69} __packed;
 70
 71struct ipv6_frag_pkt {
 72	struct ethhdr eth;
 73	struct ipv6hdr iph;
 74	struct frag_hdr {
 75		__u8 nexthdr;
 76		__u8 reserved;
 77		__be16 frag_off;
 78		__be32 identification;
 79	} ipf;
 80	struct tcphdr tcp;
 81} __packed;
 82
 83struct dvlan_ipv6_pkt {
 84	struct ethhdr eth;
 85	__u16 vlan_tci;
 86	__u16 vlan_proto;
 87	__u16 vlan_tci2;
 88	__u16 vlan_proto2;
 89	struct ipv6hdr iph;
 90	struct tcphdr tcp;
 91} __packed;
 92
 93struct test {
 94	const char *name;
 95	union {
 96		struct ipv4_pkt ipv4;
 97		struct svlan_ipv4_pkt svlan_ipv4;
 98		struct ipip_pkt ipip;
 99		struct ipv6_pkt ipv6;
100		struct ipv6_frag_pkt ipv6_frag;
101		struct dvlan_ipv6_pkt dvlan_ipv6;
102	} pkt;
103	struct bpf_flow_keys keys;
104	__u32 flags;
105	__u32 retval;
106};
107
108#define VLAN_HLEN	4
109
110static __u32 duration;
111struct test tests[] = {
112	{
113		.name = "ipv4",
114		.pkt.ipv4 = {
115			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
116			.iph.ihl = 5,
117			.iph.protocol = IPPROTO_TCP,
118			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
119			.tcp.doff = 5,
120			.tcp.source = 80,
121			.tcp.dest = 8080,
122		},
123		.keys = {
124			.nhoff = ETH_HLEN,
125			.thoff = ETH_HLEN + sizeof(struct iphdr),
126			.addr_proto = ETH_P_IP,
127			.ip_proto = IPPROTO_TCP,
128			.n_proto = __bpf_constant_htons(ETH_P_IP),
129			.sport = 80,
130			.dport = 8080,
131		},
132		.retval = BPF_OK,
133	},
134	{
135		.name = "ipv6",
136		.pkt.ipv6 = {
137			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
138			.iph.nexthdr = IPPROTO_TCP,
139			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
140			.tcp.doff = 5,
141			.tcp.source = 80,
142			.tcp.dest = 8080,
143		},
144		.keys = {
145			.nhoff = ETH_HLEN,
146			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
147			.addr_proto = ETH_P_IPV6,
148			.ip_proto = IPPROTO_TCP,
149			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
150			.sport = 80,
151			.dport = 8080,
152		},
153		.retval = BPF_OK,
154	},
155	{
156		.name = "802.1q-ipv4",
157		.pkt.svlan_ipv4 = {
158			.eth.h_proto = __bpf_constant_htons(ETH_P_8021Q),
159			.vlan_proto = __bpf_constant_htons(ETH_P_IP),
160			.iph.ihl = 5,
161			.iph.protocol = IPPROTO_TCP,
162			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
163			.tcp.doff = 5,
164			.tcp.source = 80,
165			.tcp.dest = 8080,
166		},
167		.keys = {
168			.nhoff = ETH_HLEN + VLAN_HLEN,
169			.thoff = ETH_HLEN + VLAN_HLEN + sizeof(struct iphdr),
170			.addr_proto = ETH_P_IP,
171			.ip_proto = IPPROTO_TCP,
172			.n_proto = __bpf_constant_htons(ETH_P_IP),
173			.sport = 80,
174			.dport = 8080,
175		},
176		.retval = BPF_OK,
177	},
178	{
179		.name = "802.1ad-ipv6",
180		.pkt.dvlan_ipv6 = {
181			.eth.h_proto = __bpf_constant_htons(ETH_P_8021AD),
182			.vlan_proto = __bpf_constant_htons(ETH_P_8021Q),
183			.vlan_proto2 = __bpf_constant_htons(ETH_P_IPV6),
184			.iph.nexthdr = IPPROTO_TCP,
185			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
186			.tcp.doff = 5,
187			.tcp.source = 80,
188			.tcp.dest = 8080,
189		},
190		.keys = {
191			.nhoff = ETH_HLEN + VLAN_HLEN * 2,
192			.thoff = ETH_HLEN + VLAN_HLEN * 2 +
193				sizeof(struct ipv6hdr),
194			.addr_proto = ETH_P_IPV6,
195			.ip_proto = IPPROTO_TCP,
196			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
197			.sport = 80,
198			.dport = 8080,
199		},
200		.retval = BPF_OK,
201	},
202	{
203		.name = "ipv4-frag",
204		.pkt.ipv4 = {
205			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
206			.iph.ihl = 5,
207			.iph.protocol = IPPROTO_TCP,
208			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
209			.iph.frag_off = __bpf_constant_htons(IP_MF),
210			.tcp.doff = 5,
211			.tcp.source = 80,
212			.tcp.dest = 8080,
213		},
214		.keys = {
215			.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
216			.nhoff = ETH_HLEN,
217			.thoff = ETH_HLEN + sizeof(struct iphdr),
218			.addr_proto = ETH_P_IP,
219			.ip_proto = IPPROTO_TCP,
220			.n_proto = __bpf_constant_htons(ETH_P_IP),
221			.is_frag = true,
222			.is_first_frag = true,
223			.sport = 80,
224			.dport = 8080,
225		},
226		.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
227		.retval = BPF_OK,
228	},
229	{
230		.name = "ipv4-no-frag",
231		.pkt.ipv4 = {
232			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
233			.iph.ihl = 5,
234			.iph.protocol = IPPROTO_TCP,
235			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
236			.iph.frag_off = __bpf_constant_htons(IP_MF),
237			.tcp.doff = 5,
238			.tcp.source = 80,
239			.tcp.dest = 8080,
240		},
241		.keys = {
242			.nhoff = ETH_HLEN,
243			.thoff = ETH_HLEN + sizeof(struct iphdr),
244			.addr_proto = ETH_P_IP,
245			.ip_proto = IPPROTO_TCP,
246			.n_proto = __bpf_constant_htons(ETH_P_IP),
247			.is_frag = true,
248			.is_first_frag = true,
249		},
250		.retval = BPF_OK,
251	},
252	{
253		.name = "ipv6-frag",
254		.pkt.ipv6_frag = {
255			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
256			.iph.nexthdr = IPPROTO_FRAGMENT,
257			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
258			.ipf.nexthdr = IPPROTO_TCP,
259			.tcp.doff = 5,
260			.tcp.source = 80,
261			.tcp.dest = 8080,
262		},
263		.keys = {
264			.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
265			.nhoff = ETH_HLEN,
266			.thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
267				sizeof(struct frag_hdr),
268			.addr_proto = ETH_P_IPV6,
269			.ip_proto = IPPROTO_TCP,
270			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
271			.is_frag = true,
272			.is_first_frag = true,
273			.sport = 80,
274			.dport = 8080,
275		},
276		.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
277		.retval = BPF_OK,
278	},
279	{
280		.name = "ipv6-no-frag",
281		.pkt.ipv6_frag = {
282			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
283			.iph.nexthdr = IPPROTO_FRAGMENT,
284			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
285			.ipf.nexthdr = IPPROTO_TCP,
286			.tcp.doff = 5,
287			.tcp.source = 80,
288			.tcp.dest = 8080,
289		},
290		.keys = {
291			.nhoff = ETH_HLEN,
292			.thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
293				sizeof(struct frag_hdr),
294			.addr_proto = ETH_P_IPV6,
295			.ip_proto = IPPROTO_TCP,
296			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
297			.is_frag = true,
298			.is_first_frag = true,
299		},
300		.retval = BPF_OK,
301	},
302	{
303		.name = "ipv6-flow-label",
304		.pkt.ipv6 = {
305			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
306			.iph.nexthdr = IPPROTO_TCP,
307			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
308			.iph.flow_lbl = { 0xb, 0xee, 0xef },
309			.tcp.doff = 5,
310			.tcp.source = 80,
311			.tcp.dest = 8080,
312		},
313		.keys = {
314			.nhoff = ETH_HLEN,
315			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
316			.addr_proto = ETH_P_IPV6,
317			.ip_proto = IPPROTO_TCP,
318			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
319			.sport = 80,
320			.dport = 8080,
321			.flow_label = __bpf_constant_htonl(0xbeeef),
322		},
323		.retval = BPF_OK,
324	},
325	{
326		.name = "ipv6-no-flow-label",
327		.pkt.ipv6 = {
328			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
329			.iph.nexthdr = IPPROTO_TCP,
330			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
331			.iph.flow_lbl = { 0xb, 0xee, 0xef },
332			.tcp.doff = 5,
333			.tcp.source = 80,
334			.tcp.dest = 8080,
335		},
336		.keys = {
337			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
338			.nhoff = ETH_HLEN,
339			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
340			.addr_proto = ETH_P_IPV6,
341			.ip_proto = IPPROTO_TCP,
342			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
343			.flow_label = __bpf_constant_htonl(0xbeeef),
344		},
345		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
346		.retval = BPF_OK,
347	},
348	{
349		.name = "ipv6-empty-flow-label",
350		.pkt.ipv6 = {
351			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
352			.iph.nexthdr = IPPROTO_TCP,
353			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
354			.iph.flow_lbl = { 0x00, 0x00, 0x00 },
355			.tcp.doff = 5,
356			.tcp.source = 80,
357			.tcp.dest = 8080,
358		},
359		.keys = {
360			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
361			.nhoff = ETH_HLEN,
362			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
363			.addr_proto = ETH_P_IPV6,
364			.ip_proto = IPPROTO_TCP,
365			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
366			.sport = 80,
367			.dport = 8080,
368		},
369		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
370		.retval = BPF_OK,
371	},
372	{
373		.name = "ipip-encap",
374		.pkt.ipip = {
375			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
376			.iph.ihl = 5,
377			.iph.protocol = IPPROTO_IPIP,
378			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
379			.iph_inner.ihl = 5,
380			.iph_inner.protocol = IPPROTO_TCP,
381			.iph_inner.tot_len =
382				__bpf_constant_htons(MAGIC_BYTES) -
383				sizeof(struct iphdr),
384			.tcp.doff = 5,
385			.tcp.source = 80,
386			.tcp.dest = 8080,
387		},
388		.keys = {
389			.nhoff = ETH_HLEN,
390			.thoff = ETH_HLEN + sizeof(struct iphdr) +
391				sizeof(struct iphdr),
392			.addr_proto = ETH_P_IP,
393			.ip_proto = IPPROTO_TCP,
394			.n_proto = __bpf_constant_htons(ETH_P_IP),
395			.is_encap = true,
396			.sport = 80,
397			.dport = 8080,
398		},
399		.retval = BPF_OK,
400	},
401	{
402		.name = "ipip-no-encap",
403		.pkt.ipip = {
404			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
405			.iph.ihl = 5,
406			.iph.protocol = IPPROTO_IPIP,
407			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
408			.iph_inner.ihl = 5,
409			.iph_inner.protocol = IPPROTO_TCP,
410			.iph_inner.tot_len =
411				__bpf_constant_htons(MAGIC_BYTES) -
412				sizeof(struct iphdr),
413			.tcp.doff = 5,
414			.tcp.source = 80,
415			.tcp.dest = 8080,
416		},
417		.keys = {
418			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
419			.nhoff = ETH_HLEN,
420			.thoff = ETH_HLEN + sizeof(struct iphdr),
421			.addr_proto = ETH_P_IP,
422			.ip_proto = IPPROTO_IPIP,
423			.n_proto = __bpf_constant_htons(ETH_P_IP),
424			.is_encap = true,
425		},
426		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
427		.retval = BPF_OK,
428	},
429	{
430		.name = "ipip-encap-dissector-continue",
431		.pkt.ipip = {
432			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
433			.iph.ihl = 5,
434			.iph.protocol = IPPROTO_IPIP,
435			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
436			.iph.saddr = __bpf_constant_htonl(FLOW_CONTINUE_SADDR),
437			.iph_inner.ihl = 5,
438			.iph_inner.protocol = IPPROTO_TCP,
439			.iph_inner.tot_len =
440				__bpf_constant_htons(MAGIC_BYTES) -
441				sizeof(struct iphdr),
442			.tcp.doff = 5,
443			.tcp.source = 99,
444			.tcp.dest = 9090,
445		},
446		.retval = BPF_FLOW_DISSECTOR_CONTINUE,
447	},
448};
449
450static int create_tap(const char *ifname)
451{
452	struct ifreq ifr = {
453		.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS,
454	};
455	int fd, ret;
456
457	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
458
459	fd = open("/dev/net/tun", O_RDWR);
460	if (fd < 0)
461		return -1;
462
463	ret = ioctl(fd, TUNSETIFF, &ifr);
464	if (ret)
465		return -1;
466
467	return fd;
468}
469
470static int tx_tap(int fd, void *pkt, size_t len)
471{
472	struct iovec iov[] = {
473		{
474			.iov_len = len,
475			.iov_base = pkt,
476		},
477	};
478	return writev(fd, iov, ARRAY_SIZE(iov));
479}
480
481static int ifup(const char *ifname)
482{
483	struct ifreq ifr = {};
484	int sk, ret;
485
486	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
487
488	sk = socket(PF_INET, SOCK_DGRAM, 0);
489	if (sk < 0)
490		return -1;
491
492	ret = ioctl(sk, SIOCGIFFLAGS, &ifr);
493	if (ret) {
494		close(sk);
495		return -1;
496	}
497
498	ifr.ifr_flags |= IFF_UP;
499	ret = ioctl(sk, SIOCSIFFLAGS, &ifr);
500	if (ret) {
501		close(sk);
502		return -1;
503	}
504
505	close(sk);
506	return 0;
507}
508
509static int init_prog_array(struct bpf_object *obj, struct bpf_map *prog_array)
510{
511	int i, err, map_fd, prog_fd;
512	struct bpf_program *prog;
513	char prog_name[32];
514
515	map_fd = bpf_map__fd(prog_array);
516	if (map_fd < 0)
517		return -1;
518
519	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
520		snprintf(prog_name, sizeof(prog_name), "flow_dissector_%d", i);
521
522		prog = bpf_object__find_program_by_name(obj, prog_name);
523		if (!prog)
524			return -1;
525
526		prog_fd = bpf_program__fd(prog);
527		if (prog_fd < 0)
528			return -1;
529
530		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
531		if (err)
532			return -1;
533	}
534	return 0;
535}
536
537static void run_tests_skb_less(int tap_fd, struct bpf_map *keys)
538{
539	int i, err, keys_fd;
540
541	keys_fd = bpf_map__fd(keys);
542	if (CHECK(keys_fd < 0, "bpf_map__fd", "err %d\n", keys_fd))
543		return;
544
545	for (i = 0; i < ARRAY_SIZE(tests); i++) {
546		/* Keep in sync with 'flags' from eth_get_headlen. */
547		__u32 eth_get_headlen_flags =
548			BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
549		LIBBPF_OPTS(bpf_test_run_opts, topts);
550		struct bpf_flow_keys flow_keys = {};
551		__u32 key = (__u32)(tests[i].keys.sport) << 16 |
552			    tests[i].keys.dport;
553
554		/* For skb-less case we can't pass input flags; run
555		 * only the tests that have a matching set of flags.
556		 */
557
558		if (tests[i].flags != eth_get_headlen_flags)
559			continue;
560
561		err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
562		CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno);
563
564		/* check the stored flow_keys only if BPF_OK expected */
565		if (tests[i].retval != BPF_OK)
566			continue;
567
568		err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
569		ASSERT_OK(err, "bpf_map_lookup_elem");
570
571		CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
572
573		err = bpf_map_delete_elem(keys_fd, &key);
574		ASSERT_OK(err, "bpf_map_delete_elem");
575	}
576}
577
578static void test_skb_less_prog_attach(struct bpf_flow *skel, int tap_fd)
579{
580	int err, prog_fd;
581
582	prog_fd = bpf_program__fd(skel->progs._dissect);
583	if (CHECK(prog_fd < 0, "bpf_program__fd", "err %d\n", prog_fd))
584		return;
585
586	err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
587	if (CHECK(err, "bpf_prog_attach", "err %d errno %d\n", err, errno))
588		return;
589
590	run_tests_skb_less(tap_fd, skel->maps.last_dissection);
591
592	err = bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
593	CHECK(err, "bpf_prog_detach2", "err %d errno %d\n", err, errno);
594}
595
596static void test_skb_less_link_create(struct bpf_flow *skel, int tap_fd)
597{
598	struct bpf_link *link;
599	int err, net_fd;
600
601	net_fd = open("/proc/self/ns/net", O_RDONLY);
602	if (CHECK(net_fd < 0, "open(/proc/self/ns/net)", "err %d\n", errno))
603		return;
604
605	link = bpf_program__attach_netns(skel->progs._dissect, net_fd);
606	if (!ASSERT_OK_PTR(link, "attach_netns"))
607		goto out_close;
608
609	run_tests_skb_less(tap_fd, skel->maps.last_dissection);
610
611	err = bpf_link__destroy(link);
612	CHECK(err, "bpf_link__destroy", "err %d\n", err);
613out_close:
614	close(net_fd);
615}
616
617void test_flow_dissector(void)
618{
619	int i, err, prog_fd, keys_fd = -1, tap_fd;
620	struct bpf_flow *skel;
621
622	skel = bpf_flow__open_and_load();
623	if (CHECK(!skel, "skel", "failed to open/load skeleton\n"))
624		return;
625
626	prog_fd = bpf_program__fd(skel->progs._dissect);
627	if (CHECK(prog_fd < 0, "bpf_program__fd", "err %d\n", prog_fd))
628		goto out_destroy_skel;
629	keys_fd = bpf_map__fd(skel->maps.last_dissection);
630	if (CHECK(keys_fd < 0, "bpf_map__fd", "err %d\n", keys_fd))
631		goto out_destroy_skel;
632	err = init_prog_array(skel->obj, skel->maps.jmp_table);
633	if (CHECK(err, "init_prog_array", "err %d\n", err))
634		goto out_destroy_skel;
635
636	for (i = 0; i < ARRAY_SIZE(tests); i++) {
637		struct bpf_flow_keys flow_keys;
638		LIBBPF_OPTS(bpf_test_run_opts, topts,
639			.data_in = &tests[i].pkt,
640			.data_size_in = sizeof(tests[i].pkt),
641			.data_out = &flow_keys,
642		);
643		static struct bpf_flow_keys ctx = {};
644
645		if (tests[i].flags) {
646			topts.ctx_in = &ctx;
647			topts.ctx_size_in = sizeof(ctx);
648			ctx.flags = tests[i].flags;
649		}
650
651		err = bpf_prog_test_run_opts(prog_fd, &topts);
652		ASSERT_OK(err, "test_run");
653		ASSERT_EQ(topts.retval, tests[i].retval, "test_run retval");
654
655		/* check the resulting flow_keys only if BPF_OK returned */
656		if (topts.retval != BPF_OK)
657			continue;
658		ASSERT_EQ(topts.data_size_out, sizeof(flow_keys),
659			  "test_run data_size_out");
660		CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
661	}
662
663	/* Do the same tests but for skb-less flow dissector.
664	 * We use a known path in the net/tun driver that calls
665	 * eth_get_headlen and we manually export bpf_flow_keys
666	 * via BPF map in this case.
667	 */
668
669	tap_fd = create_tap("tap0");
670	CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d\n", tap_fd, errno);
671	err = ifup("tap0");
672	CHECK(err, "ifup", "err %d errno %d\n", err, errno);
673
674	/* Test direct prog attachment */
675	test_skb_less_prog_attach(skel, tap_fd);
676	/* Test indirect prog attachment via link */
677	test_skb_less_link_create(skel, tap_fd);
678
679	close(tap_fd);
680out_destroy_skel:
681	bpf_flow__destroy(skel);
682}