xref: /linux/tools/testing/selftests/bpf/prog_tests/flow_dissector.c (revision 8c994eff8fcfe8ecb1f1dbebed25b4d7bb75be12)
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 
44 struct ipv4_pkt {
45 	struct ethhdr eth;
46 	struct iphdr iph;
47 	struct tcphdr tcp;
48 } __packed;
49 
50 struct ipip_pkt {
51 	struct ethhdr eth;
52 	struct iphdr iph;
53 	struct iphdr iph_inner;
54 	struct tcphdr tcp;
55 } __packed;
56 
57 struct 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 
65 struct ipv6_pkt {
66 	struct ethhdr eth;
67 	struct ipv6hdr iph;
68 	struct tcphdr tcp;
69 } __packed;
70 
71 struct 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 
83 struct 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 
93 struct 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 
110 static __u32 duration;
111 struct 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 
450 static 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 
470 static 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 
481 static 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 
509 static 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 
537 static 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 
578 static 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 
596 static 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);
613 out_close:
614 	close(net_fd);
615 }
616 
617 void 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);
680 out_destroy_skel:
681 	bpf_flow__destroy(skel);
682 }
683