xref: /linux/tools/testing/selftests/bpf/prog_tests/flow_dissector.c (revision ed3e469d021cba343a3b32f0876f8f28b1d9bfdd)
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 TEST_NS	"flow_dissector_ns"
11 #define FLOW_CONTINUE_SADDR 0x7f00007f /* 127.0.0.127 */
12 #define TEST_NAME_MAX_LEN	64
13 
14 #ifndef IP_MF
15 #define IP_MF 0x2000
16 #endif
17 
18 struct ipv4_pkt {
19 	struct ethhdr eth;
20 	struct iphdr iph;
21 	struct tcphdr tcp;
22 } __packed;
23 
24 struct ipip_pkt {
25 	struct ethhdr eth;
26 	struct iphdr iph;
27 	struct iphdr iph_inner;
28 	struct tcphdr tcp;
29 } __packed;
30 
31 struct svlan_ipv4_pkt {
32 	struct ethhdr eth;
33 	__u16 vlan_tci;
34 	__u16 vlan_proto;
35 	struct iphdr iph;
36 	struct tcphdr tcp;
37 } __packed;
38 
39 struct ipv6_pkt {
40 	struct ethhdr eth;
41 	struct ipv6hdr iph;
42 	struct tcphdr tcp;
43 } __packed;
44 
45 struct ipv6_frag_pkt {
46 	struct ethhdr eth;
47 	struct ipv6hdr iph;
48 	struct frag_hdr {
49 		__u8 nexthdr;
50 		__u8 reserved;
51 		__be16 frag_off;
52 		__be32 identification;
53 	} ipf;
54 	struct tcphdr tcp;
55 } __packed;
56 
57 struct dvlan_ipv6_pkt {
58 	struct ethhdr eth;
59 	__u16 vlan_tci;
60 	__u16 vlan_proto;
61 	__u16 vlan_tci2;
62 	__u16 vlan_proto2;
63 	struct ipv6hdr iph;
64 	struct tcphdr tcp;
65 } __packed;
66 
67 struct gre_base_hdr {
68 	__be16 flags;
69 	__be16 protocol;
70 } gre_base_hdr;
71 
72 struct gre_minimal_pkt {
73 	struct ethhdr eth;
74 	struct iphdr iph;
75 	struct gre_base_hdr gre_hdr;
76 	struct iphdr iph_inner;
77 	struct tcphdr tcp;
78 } __packed;
79 
80 struct test {
81 	const char *name;
82 	union {
83 		struct ipv4_pkt ipv4;
84 		struct svlan_ipv4_pkt svlan_ipv4;
85 		struct ipip_pkt ipip;
86 		struct ipv6_pkt ipv6;
87 		struct ipv6_frag_pkt ipv6_frag;
88 		struct dvlan_ipv6_pkt dvlan_ipv6;
89 		struct gre_minimal_pkt gre_minimal;
90 	} pkt;
91 	struct bpf_flow_keys keys;
92 	__u32 flags;
93 	__u32 retval;
94 };
95 
96 #define VLAN_HLEN	4
97 
98 struct test tests[] = {
99 	{
100 		.name = "ipv4",
101 		.pkt.ipv4 = {
102 			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
103 			.iph.ihl = 5,
104 			.iph.protocol = IPPROTO_TCP,
105 			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
106 			.tcp.doff = 5,
107 			.tcp.source = 80,
108 			.tcp.dest = 8080,
109 		},
110 		.keys = {
111 			.nhoff = ETH_HLEN,
112 			.thoff = ETH_HLEN + sizeof(struct iphdr),
113 			.addr_proto = ETH_P_IP,
114 			.ip_proto = IPPROTO_TCP,
115 			.n_proto = __bpf_constant_htons(ETH_P_IP),
116 			.sport = 80,
117 			.dport = 8080,
118 		},
119 		.retval = BPF_OK,
120 	},
121 	{
122 		.name = "ipv6",
123 		.pkt.ipv6 = {
124 			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
125 			.iph.nexthdr = IPPROTO_TCP,
126 			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
127 			.tcp.doff = 5,
128 			.tcp.source = 80,
129 			.tcp.dest = 8080,
130 		},
131 		.keys = {
132 			.nhoff = ETH_HLEN,
133 			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
134 			.addr_proto = ETH_P_IPV6,
135 			.ip_proto = IPPROTO_TCP,
136 			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
137 			.sport = 80,
138 			.dport = 8080,
139 		},
140 		.retval = BPF_OK,
141 	},
142 	{
143 		.name = "802.1q-ipv4",
144 		.pkt.svlan_ipv4 = {
145 			.eth.h_proto = __bpf_constant_htons(ETH_P_8021Q),
146 			.vlan_proto = __bpf_constant_htons(ETH_P_IP),
147 			.iph.ihl = 5,
148 			.iph.protocol = IPPROTO_TCP,
149 			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
150 			.tcp.doff = 5,
151 			.tcp.source = 80,
152 			.tcp.dest = 8080,
153 		},
154 		.keys = {
155 			.nhoff = ETH_HLEN + VLAN_HLEN,
156 			.thoff = ETH_HLEN + VLAN_HLEN + sizeof(struct iphdr),
157 			.addr_proto = ETH_P_IP,
158 			.ip_proto = IPPROTO_TCP,
159 			.n_proto = __bpf_constant_htons(ETH_P_IP),
160 			.sport = 80,
161 			.dport = 8080,
162 		},
163 		.retval = BPF_OK,
164 	},
165 	{
166 		.name = "802.1ad-ipv6",
167 		.pkt.dvlan_ipv6 = {
168 			.eth.h_proto = __bpf_constant_htons(ETH_P_8021AD),
169 			.vlan_proto = __bpf_constant_htons(ETH_P_8021Q),
170 			.vlan_proto2 = __bpf_constant_htons(ETH_P_IPV6),
171 			.iph.nexthdr = IPPROTO_TCP,
172 			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
173 			.tcp.doff = 5,
174 			.tcp.source = 80,
175 			.tcp.dest = 8080,
176 		},
177 		.keys = {
178 			.nhoff = ETH_HLEN + VLAN_HLEN * 2,
179 			.thoff = ETH_HLEN + VLAN_HLEN * 2 +
180 				sizeof(struct ipv6hdr),
181 			.addr_proto = ETH_P_IPV6,
182 			.ip_proto = IPPROTO_TCP,
183 			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
184 			.sport = 80,
185 			.dport = 8080,
186 		},
187 		.retval = BPF_OK,
188 	},
189 	{
190 		.name = "ipv4-frag",
191 		.pkt.ipv4 = {
192 			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
193 			.iph.ihl = 5,
194 			.iph.protocol = IPPROTO_TCP,
195 			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
196 			.iph.frag_off = __bpf_constant_htons(IP_MF),
197 			.tcp.doff = 5,
198 			.tcp.source = 80,
199 			.tcp.dest = 8080,
200 		},
201 		.keys = {
202 			.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
203 			.nhoff = ETH_HLEN,
204 			.thoff = ETH_HLEN + sizeof(struct iphdr),
205 			.addr_proto = ETH_P_IP,
206 			.ip_proto = IPPROTO_TCP,
207 			.n_proto = __bpf_constant_htons(ETH_P_IP),
208 			.is_frag = true,
209 			.is_first_frag = true,
210 			.sport = 80,
211 			.dport = 8080,
212 		},
213 		.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
214 		.retval = BPF_OK,
215 	},
216 	{
217 		.name = "ipv4-no-frag",
218 		.pkt.ipv4 = {
219 			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
220 			.iph.ihl = 5,
221 			.iph.protocol = IPPROTO_TCP,
222 			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
223 			.iph.frag_off = __bpf_constant_htons(IP_MF),
224 			.tcp.doff = 5,
225 			.tcp.source = 80,
226 			.tcp.dest = 8080,
227 		},
228 		.keys = {
229 			.nhoff = ETH_HLEN,
230 			.thoff = ETH_HLEN + sizeof(struct iphdr),
231 			.addr_proto = ETH_P_IP,
232 			.ip_proto = IPPROTO_TCP,
233 			.n_proto = __bpf_constant_htons(ETH_P_IP),
234 			.is_frag = true,
235 			.is_first_frag = true,
236 		},
237 		.retval = BPF_OK,
238 	},
239 	{
240 		.name = "ipv6-frag",
241 		.pkt.ipv6_frag = {
242 			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
243 			.iph.nexthdr = IPPROTO_FRAGMENT,
244 			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
245 			.ipf.nexthdr = IPPROTO_TCP,
246 			.tcp.doff = 5,
247 			.tcp.source = 80,
248 			.tcp.dest = 8080,
249 		},
250 		.keys = {
251 			.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
252 			.nhoff = ETH_HLEN,
253 			.thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
254 				sizeof(struct frag_hdr),
255 			.addr_proto = ETH_P_IPV6,
256 			.ip_proto = IPPROTO_TCP,
257 			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
258 			.is_frag = true,
259 			.is_first_frag = true,
260 			.sport = 80,
261 			.dport = 8080,
262 		},
263 		.flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
264 		.retval = BPF_OK,
265 	},
266 	{
267 		.name = "ipv6-no-frag",
268 		.pkt.ipv6_frag = {
269 			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
270 			.iph.nexthdr = IPPROTO_FRAGMENT,
271 			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
272 			.ipf.nexthdr = IPPROTO_TCP,
273 			.tcp.doff = 5,
274 			.tcp.source = 80,
275 			.tcp.dest = 8080,
276 		},
277 		.keys = {
278 			.nhoff = ETH_HLEN,
279 			.thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
280 				sizeof(struct frag_hdr),
281 			.addr_proto = ETH_P_IPV6,
282 			.ip_proto = IPPROTO_TCP,
283 			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
284 			.is_frag = true,
285 			.is_first_frag = true,
286 		},
287 		.retval = BPF_OK,
288 	},
289 	{
290 		.name = "ipv6-flow-label",
291 		.pkt.ipv6 = {
292 			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
293 			.iph.nexthdr = IPPROTO_TCP,
294 			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
295 			.iph.flow_lbl = { 0xb, 0xee, 0xef },
296 			.tcp.doff = 5,
297 			.tcp.source = 80,
298 			.tcp.dest = 8080,
299 		},
300 		.keys = {
301 			.nhoff = ETH_HLEN,
302 			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
303 			.addr_proto = ETH_P_IPV6,
304 			.ip_proto = IPPROTO_TCP,
305 			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
306 			.sport = 80,
307 			.dport = 8080,
308 			.flow_label = __bpf_constant_htonl(0xbeeef),
309 		},
310 		.retval = BPF_OK,
311 	},
312 	{
313 		.name = "ipv6-no-flow-label",
314 		.pkt.ipv6 = {
315 			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
316 			.iph.nexthdr = IPPROTO_TCP,
317 			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
318 			.iph.flow_lbl = { 0xb, 0xee, 0xef },
319 			.tcp.doff = 5,
320 			.tcp.source = 80,
321 			.tcp.dest = 8080,
322 		},
323 		.keys = {
324 			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
325 			.nhoff = ETH_HLEN,
326 			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
327 			.addr_proto = ETH_P_IPV6,
328 			.ip_proto = IPPROTO_TCP,
329 			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
330 			.flow_label = __bpf_constant_htonl(0xbeeef),
331 		},
332 		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
333 		.retval = BPF_OK,
334 	},
335 	{
336 		.name = "ipv6-empty-flow-label",
337 		.pkt.ipv6 = {
338 			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
339 			.iph.nexthdr = IPPROTO_TCP,
340 			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
341 			.iph.flow_lbl = { 0x00, 0x00, 0x00 },
342 			.tcp.doff = 5,
343 			.tcp.source = 80,
344 			.tcp.dest = 8080,
345 		},
346 		.keys = {
347 			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
348 			.nhoff = ETH_HLEN,
349 			.thoff = ETH_HLEN + sizeof(struct ipv6hdr),
350 			.addr_proto = ETH_P_IPV6,
351 			.ip_proto = IPPROTO_TCP,
352 			.n_proto = __bpf_constant_htons(ETH_P_IPV6),
353 			.sport = 80,
354 			.dport = 8080,
355 		},
356 		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
357 		.retval = BPF_OK,
358 	},
359 	{
360 		.name = "ipip-encap",
361 		.pkt.ipip = {
362 			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
363 			.iph.ihl = 5,
364 			.iph.protocol = IPPROTO_IPIP,
365 			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
366 			.iph_inner.ihl = 5,
367 			.iph_inner.protocol = IPPROTO_TCP,
368 			.iph_inner.tot_len =
369 				__bpf_constant_htons(MAGIC_BYTES -
370 				sizeof(struct iphdr)),
371 			.tcp.doff = 5,
372 			.tcp.source = 80,
373 			.tcp.dest = 8080,
374 		},
375 		.keys = {
376 			.nhoff = ETH_HLEN,
377 			.thoff = ETH_HLEN + sizeof(struct iphdr) +
378 				sizeof(struct iphdr),
379 			.addr_proto = ETH_P_IP,
380 			.ip_proto = IPPROTO_TCP,
381 			.n_proto = __bpf_constant_htons(ETH_P_IP),
382 			.is_encap = true,
383 			.sport = 80,
384 			.dport = 8080,
385 		},
386 		.retval = BPF_OK,
387 	},
388 	{
389 		.name = "ipip-no-encap",
390 		.pkt.ipip = {
391 			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
392 			.iph.ihl = 5,
393 			.iph.protocol = IPPROTO_IPIP,
394 			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
395 			.iph_inner.ihl = 5,
396 			.iph_inner.protocol = IPPROTO_TCP,
397 			.iph_inner.tot_len =
398 				__bpf_constant_htons(MAGIC_BYTES -
399 				sizeof(struct iphdr)),
400 			.tcp.doff = 5,
401 			.tcp.source = 80,
402 			.tcp.dest = 8080,
403 		},
404 		.keys = {
405 			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
406 			.nhoff = ETH_HLEN,
407 			.thoff = ETH_HLEN + sizeof(struct iphdr),
408 			.addr_proto = ETH_P_IP,
409 			.ip_proto = IPPROTO_IPIP,
410 			.n_proto = __bpf_constant_htons(ETH_P_IP),
411 			.is_encap = true,
412 		},
413 		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
414 		.retval = BPF_OK,
415 	},
416 	{
417 		.name = "ipip-encap-dissector-continue",
418 		.pkt.ipip = {
419 			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
420 			.iph.ihl = 5,
421 			.iph.protocol = IPPROTO_IPIP,
422 			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
423 			.iph.saddr = __bpf_constant_htonl(FLOW_CONTINUE_SADDR),
424 			.iph_inner.ihl = 5,
425 			.iph_inner.protocol = IPPROTO_TCP,
426 			.iph_inner.tot_len =
427 				__bpf_constant_htons(MAGIC_BYTES -
428 				sizeof(struct iphdr)),
429 			.tcp.doff = 5,
430 			.tcp.source = 99,
431 			.tcp.dest = 9090,
432 		},
433 		.retval = BPF_FLOW_DISSECTOR_CONTINUE,
434 	},
435 	{
436 		.name = "ip-gre",
437 		.pkt.gre_minimal = {
438 			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
439 			.iph.ihl = 5,
440 			.iph.protocol = IPPROTO_GRE,
441 			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
442 			.gre_hdr = {
443 				.flags = 0,
444 				.protocol = __bpf_constant_htons(ETH_P_IP),
445 			},
446 			.iph_inner.ihl = 5,
447 			.iph_inner.protocol = IPPROTO_TCP,
448 			.iph_inner.tot_len =
449 				__bpf_constant_htons(MAGIC_BYTES -
450 				sizeof(struct iphdr)),
451 			.tcp.doff = 5,
452 			.tcp.source = 80,
453 			.tcp.dest = 8080,
454 		},
455 		.keys = {
456 			.nhoff = ETH_HLEN,
457 			.thoff = ETH_HLEN + sizeof(struct iphdr) * 2 +
458 				 sizeof(struct gre_base_hdr),
459 			.addr_proto = ETH_P_IP,
460 			.ip_proto = IPPROTO_TCP,
461 			.n_proto = __bpf_constant_htons(ETH_P_IP),
462 			.is_encap = true,
463 			.sport = 80,
464 			.dport = 8080,
465 		},
466 		.retval = BPF_OK,
467 	},
468 	{
469 		.name = "ip-gre-no-encap",
470 		.pkt.ipip = {
471 			.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
472 			.iph.ihl = 5,
473 			.iph.protocol = IPPROTO_GRE,
474 			.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
475 			.iph_inner.ihl = 5,
476 			.iph_inner.protocol = IPPROTO_TCP,
477 			.iph_inner.tot_len =
478 				__bpf_constant_htons(MAGIC_BYTES -
479 				sizeof(struct iphdr)),
480 			.tcp.doff = 5,
481 			.tcp.source = 80,
482 			.tcp.dest = 8080,
483 		},
484 		.keys = {
485 			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
486 			.nhoff = ETH_HLEN,
487 			.thoff = ETH_HLEN + sizeof(struct iphdr)
488 				 + sizeof(struct gre_base_hdr),
489 			.addr_proto = ETH_P_IP,
490 			.ip_proto = IPPROTO_GRE,
491 			.n_proto = __bpf_constant_htons(ETH_P_IP),
492 			.is_encap = true,
493 		},
494 		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
495 		.retval = BPF_OK,
496 	},
497 };
498 
499 void serial_test_flow_dissector_namespace(void)
500 {
501 	struct bpf_flow *skel;
502 	struct nstoken *ns;
503 	int err, prog_fd;
504 
505 	skel = bpf_flow__open_and_load();
506 	if (!ASSERT_OK_PTR(skel, "open/load skeleton"))
507 		return;
508 
509 	prog_fd = bpf_program__fd(skel->progs._dissect);
510 	if (!ASSERT_OK_FD(prog_fd, "get dissector fd"))
511 		goto out_destroy_skel;
512 
513 	/* We must be able to attach a flow dissector to root namespace */
514 	err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
515 	if (!ASSERT_OK(err, "attach on root namespace ok"))
516 		goto out_destroy_skel;
517 
518 	err = make_netns(TEST_NS);
519 	if (!ASSERT_OK(err, "create non-root net namespace"))
520 		goto out_destroy_skel;
521 
522 	/* We must not be able to additionally attach a flow dissector to a
523 	 * non-root net namespace
524 	 */
525 	ns = open_netns(TEST_NS);
526 	if (!ASSERT_OK_PTR(ns, "enter non-root net namespace"))
527 		goto out_clean_ns;
528 
529 	err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
530 	close_netns(ns);
531 	ASSERT_ERR(err, "refuse new flow dissector in non-root net namespace");
532 	ASSERT_EQ(errno, EEXIST, "refused because of already attached prog");
533 
534 	/* If no flow dissector is attached to the root namespace, we must
535 	 * be able to attach one to a non-root net namespace
536 	 */
537 	bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
538 	ns = open_netns(TEST_NS);
539 	ASSERT_OK_PTR(ns, "enter non-root net namespace");
540 	err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
541 	close_netns(ns);
542 	ASSERT_OK(err, "accept new flow dissector in non-root net namespace");
543 
544 	/* If a flow dissector is attached to non-root net namespace, attaching
545 	 * a flow dissector to root namespace must fail
546 	 */
547 	err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
548 	ASSERT_ERR(err, "refuse new flow dissector on root namespace");
549 	ASSERT_EQ(errno, EEXIST, "refused because of already attached prog");
550 
551 	ns = open_netns(TEST_NS);
552 	bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
553 	close_netns(ns);
554 out_clean_ns:
555 	remove_netns(TEST_NS);
556 out_destroy_skel:
557 	bpf_flow__destroy(skel);
558 }
559 
560 static int create_tap(const char *ifname)
561 {
562 	struct ifreq ifr = {
563 		.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS,
564 	};
565 	int fd, ret;
566 
567 	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
568 
569 	fd = open("/dev/net/tun", O_RDWR);
570 	if (fd < 0)
571 		return -1;
572 
573 	ret = ioctl(fd, TUNSETIFF, &ifr);
574 	if (ret)
575 		return -1;
576 
577 	return fd;
578 }
579 
580 static int tx_tap(int fd, void *pkt, size_t len)
581 {
582 	struct iovec iov[] = {
583 		{
584 			.iov_len = len,
585 			.iov_base = pkt,
586 		},
587 	};
588 	return writev(fd, iov, ARRAY_SIZE(iov));
589 }
590 
591 static int ifup(const char *ifname)
592 {
593 	struct ifreq ifr = {};
594 	int sk, ret;
595 
596 	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
597 
598 	sk = socket(PF_INET, SOCK_DGRAM, 0);
599 	if (sk < 0)
600 		return -1;
601 
602 	ret = ioctl(sk, SIOCGIFFLAGS, &ifr);
603 	if (ret) {
604 		close(sk);
605 		return -1;
606 	}
607 
608 	ifr.ifr_flags |= IFF_UP;
609 	ret = ioctl(sk, SIOCSIFFLAGS, &ifr);
610 	if (ret) {
611 		close(sk);
612 		return -1;
613 	}
614 
615 	close(sk);
616 	return 0;
617 }
618 
619 static int init_prog_array(struct bpf_object *obj, struct bpf_map *prog_array)
620 {
621 	int i, err, map_fd, prog_fd;
622 	struct bpf_program *prog;
623 	char prog_name[32];
624 
625 	map_fd = bpf_map__fd(prog_array);
626 	if (map_fd < 0)
627 		return -1;
628 
629 	for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
630 		snprintf(prog_name, sizeof(prog_name), "flow_dissector_%d", i);
631 
632 		prog = bpf_object__find_program_by_name(obj, prog_name);
633 		if (!prog)
634 			return -1;
635 
636 		prog_fd = bpf_program__fd(prog);
637 		if (prog_fd < 0)
638 			return -1;
639 
640 		err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
641 		if (err)
642 			return -1;
643 	}
644 	return 0;
645 }
646 
647 static void run_tests_skb_less(int tap_fd, struct bpf_map *keys,
648 			       char *test_suffix)
649 {
650 	char test_name[TEST_NAME_MAX_LEN];
651 	int i, err, keys_fd;
652 
653 	keys_fd = bpf_map__fd(keys);
654 	if (!ASSERT_OK_FD(keys_fd, "bpf_map__fd"))
655 		return;
656 
657 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
658 		/* Keep in sync with 'flags' from eth_get_headlen. */
659 		__u32 eth_get_headlen_flags =
660 			BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
661 		struct bpf_flow_keys flow_keys = {};
662 		__u32 key = (__u32)(tests[i].keys.sport) << 16 |
663 			    tests[i].keys.dport;
664 		snprintf(test_name, TEST_NAME_MAX_LEN, "%s-%s", tests[i].name,
665 			 test_suffix);
666 		if (!test__start_subtest(test_name))
667 			continue;
668 
669 		/* For skb-less case we can't pass input flags; run
670 		 * only the tests that have a matching set of flags.
671 		 */
672 
673 		if (tests[i].flags != eth_get_headlen_flags)
674 			continue;
675 
676 		err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
677 		if (!ASSERT_EQ(err, sizeof(tests[i].pkt), "tx_tap"))
678 			continue;
679 
680 		/* check the stored flow_keys only if BPF_OK expected */
681 		if (tests[i].retval != BPF_OK)
682 			continue;
683 
684 		err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
685 		if (!ASSERT_OK(err, "bpf_map_lookup_elem"))
686 			continue;
687 
688 		ASSERT_MEMEQ(&flow_keys, &tests[i].keys,
689 			     sizeof(struct bpf_flow_keys),
690 			     "returned flow keys");
691 
692 		err = bpf_map_delete_elem(keys_fd, &key);
693 		ASSERT_OK(err, "bpf_map_delete_elem");
694 	}
695 }
696 
697 void test_flow_dissector_skb_less_direct_attach(void)
698 {
699 	int err, prog_fd, tap_fd;
700 	struct bpf_flow *skel;
701 	struct netns_obj *ns;
702 
703 	ns = netns_new("flow_dissector_skb_less_indirect_attach_ns", true);
704 	if (!ASSERT_OK_PTR(ns, "create and open netns"))
705 		return;
706 
707 	skel = bpf_flow__open_and_load();
708 	if (!ASSERT_OK_PTR(skel, "open/load skeleton"))
709 		goto out_clean_ns;
710 
711 	err = init_prog_array(skel->obj, skel->maps.jmp_table);
712 	if (!ASSERT_OK(err, "init_prog_array"))
713 		goto out_destroy_skel;
714 
715 	prog_fd = bpf_program__fd(skel->progs._dissect);
716 	if (!ASSERT_OK_FD(prog_fd, "bpf_program__fd"))
717 		goto out_destroy_skel;
718 
719 	err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
720 	if (!ASSERT_OK(err, "bpf_prog_attach"))
721 		goto out_destroy_skel;
722 
723 	tap_fd = create_tap("tap0");
724 	if (!ASSERT_OK_FD(tap_fd, "create_tap"))
725 		goto out_destroy_skel;
726 	err = ifup("tap0");
727 	if (!ASSERT_OK(err, "ifup"))
728 		goto out_close_tap;
729 
730 	run_tests_skb_less(tap_fd, skel->maps.last_dissection,
731 			   "non-skb-direct-attach");
732 
733 	err = bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
734 	ASSERT_OK(err, "bpf_prog_detach2");
735 
736 out_close_tap:
737 	close(tap_fd);
738 out_destroy_skel:
739 	bpf_flow__destroy(skel);
740 out_clean_ns:
741 	netns_free(ns);
742 }
743 
744 void test_flow_dissector_skb_less_indirect_attach(void)
745 {
746 	int err, net_fd, tap_fd;
747 	struct bpf_flow *skel;
748 	struct bpf_link *link;
749 	struct netns_obj *ns;
750 
751 	ns = netns_new("flow_dissector_skb_less_indirect_attach_ns", true);
752 	if (!ASSERT_OK_PTR(ns, "create and open netns"))
753 		return;
754 
755 	skel = bpf_flow__open_and_load();
756 	if (!ASSERT_OK_PTR(skel, "open/load skeleton"))
757 		goto out_clean_ns;
758 
759 	net_fd = open("/proc/self/ns/net", O_RDONLY);
760 	if (!ASSERT_OK_FD(net_fd, "open(/proc/self/ns/net"))
761 		goto out_destroy_skel;
762 
763 	err = init_prog_array(skel->obj, skel->maps.jmp_table);
764 	if (!ASSERT_OK(err, "init_prog_array"))
765 		goto out_destroy_skel;
766 
767 	tap_fd = create_tap("tap0");
768 	if (!ASSERT_OK_FD(tap_fd, "create_tap"))
769 		goto out_close_ns;
770 	err = ifup("tap0");
771 	if (!ASSERT_OK(err, "ifup"))
772 		goto out_close_tap;
773 
774 	link = bpf_program__attach_netns(skel->progs._dissect, net_fd);
775 	if (!ASSERT_OK_PTR(link, "attach_netns"))
776 		goto out_close_tap;
777 
778 	run_tests_skb_less(tap_fd, skel->maps.last_dissection,
779 			   "non-skb-indirect-attach");
780 
781 	err = bpf_link__destroy(link);
782 	ASSERT_OK(err, "bpf_link__destroy");
783 
784 out_close_tap:
785 	close(tap_fd);
786 out_close_ns:
787 	close(net_fd);
788 out_destroy_skel:
789 	bpf_flow__destroy(skel);
790 out_clean_ns:
791 	netns_free(ns);
792 }
793 
794 void test_flow_dissector_skb(void)
795 {
796 	char test_name[TEST_NAME_MAX_LEN];
797 	struct bpf_flow *skel;
798 	int i, err, prog_fd;
799 
800 	skel = bpf_flow__open_and_load();
801 	if (!ASSERT_OK_PTR(skel, "open/load skeleton"))
802 		return;
803 
804 	err = init_prog_array(skel->obj, skel->maps.jmp_table);
805 	if (!ASSERT_OK(err, "init_prog_array"))
806 		goto out_destroy_skel;
807 
808 	prog_fd = bpf_program__fd(skel->progs._dissect);
809 	if (!ASSERT_OK_FD(prog_fd, "bpf_program__fd"))
810 		goto out_destroy_skel;
811 
812 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
813 		struct bpf_flow_keys flow_keys;
814 		LIBBPF_OPTS(bpf_test_run_opts, topts,
815 			.data_in = &tests[i].pkt,
816 			.data_size_in = sizeof(tests[i].pkt),
817 			.data_out = &flow_keys,
818 		);
819 		static struct bpf_flow_keys ctx = {};
820 
821 		snprintf(test_name, TEST_NAME_MAX_LEN, "%s-skb", tests[i].name);
822 		if (!test__start_subtest(test_name))
823 			continue;
824 
825 		if (tests[i].flags) {
826 			topts.ctx_in = &ctx;
827 			topts.ctx_size_in = sizeof(ctx);
828 			ctx.flags = tests[i].flags;
829 		}
830 
831 		err = bpf_prog_test_run_opts(prog_fd, &topts);
832 		ASSERT_OK(err, "test_run");
833 		ASSERT_EQ(topts.retval, tests[i].retval, "test_run retval");
834 
835 		/* check the resulting flow_keys only if BPF_OK returned */
836 		if (topts.retval != BPF_OK)
837 			continue;
838 		ASSERT_EQ(topts.data_size_out, sizeof(flow_keys),
839 			  "test_run data_size_out");
840 		ASSERT_MEMEQ(&flow_keys, &tests[i].keys,
841 			     sizeof(struct bpf_flow_keys),
842 			     "returned flow keys");
843 	}
844 
845 out_destroy_skel:
846 	bpf_flow__destroy(skel);
847 }
848 
849