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