1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include <network_helpers.h>
5 #include <linux/if_tun.h>
8 #include "bpf_flow.skel.h"
10 #define FLOW_CONTINUE_SADDR 0x7f00007f /* 127.0.0.127 */
16 #define CHECK_FLOW_KEYS(desc, got, expected) \
17 _CHECK(memcmp(&got, &expected, sizeof(got)) != 0, \
22 "addr_proto=0x%x/0x%x " \
24 "is_first_frag=%u/%u " \
26 "ip_proto=0x%x/0x%x " \
27 "n_proto=0x%x/0x%x " \
28 "flow_label=0x%x/0x%x " \
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)
52 struct iphdr iph_inner;
56 struct svlan_ipv4_pkt {
70 struct ipv6_frag_pkt {
77 __be32 identification;
82 struct dvlan_ipv6_pkt {
96 struct svlan_ipv4_pkt svlan_ipv4;
99 struct ipv6_frag_pkt ipv6_frag;
100 struct dvlan_ipv6_pkt dvlan_ipv6;
102 struct bpf_flow_keys keys;
109 static __u32 duration;
110 struct test tests[] = {
114 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
116 .iph.protocol = IPPROTO_TCP,
117 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
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),
136 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
137 .iph.nexthdr = IPPROTO_TCP,
138 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
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),
155 .name = "802.1q-ipv4",
157 .eth.h_proto = __bpf_constant_htons(ETH_P_8021Q),
158 .vlan_proto = __bpf_constant_htons(ETH_P_IP),
160 .iph.protocol = IPPROTO_TCP,
161 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
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),
178 .name = "802.1ad-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),
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),
204 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
206 .iph.protocol = IPPROTO_TCP,
207 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
208 .iph.frag_off = __bpf_constant_htons(IP_MF),
214 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
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),
221 .is_first_frag = true,
225 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
229 .name = "ipv4-no-frag",
231 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
233 .iph.protocol = IPPROTO_TCP,
234 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
235 .iph.frag_off = __bpf_constant_htons(IP_MF),
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),
247 .is_first_frag = true,
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,
263 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
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),
271 .is_first_frag = true,
275 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
279 .name = "ipv6-no-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,
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),
297 .is_first_frag = true,
302 .name = "ipv6-flow-label",
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 },
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),
320 .flow_label = __bpf_constant_htonl(0xbeeef),
325 .name = "ipv6-no-flow-label",
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 },
336 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
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),
344 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
348 .name = "ipv6-empty-flow-label",
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 },
359 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
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),
368 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
372 .name = "ipip-encap",
374 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
376 .iph.protocol = IPPROTO_IPIP,
377 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
379 .iph_inner.protocol = IPPROTO_TCP,
381 __bpf_constant_htons(MAGIC_BYTES -
382 sizeof(struct iphdr)),
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),
401 .name = "ipip-no-encap",
403 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
405 .iph.protocol = IPPROTO_IPIP,
406 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
408 .iph_inner.protocol = IPPROTO_TCP,
410 __bpf_constant_htons(MAGIC_BYTES -
411 sizeof(struct iphdr)),
417 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
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),
425 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
429 .name = "ipip-encap-dissector-continue",
431 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
433 .iph.protocol = IPPROTO_IPIP,
434 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
435 .iph.saddr = __bpf_constant_htonl(FLOW_CONTINUE_SADDR),
437 .iph_inner.protocol = IPPROTO_TCP,
439 __bpf_constant_htons(MAGIC_BYTES -
440 sizeof(struct iphdr)),
445 .retval = BPF_FLOW_DISSECTOR_CONTINUE,
449 static int create_tap(const char *ifname)
452 .ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS,
456 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
458 fd = open("/dev/net/tun", O_RDWR);
462 ret = ioctl(fd, TUNSETIFF, &ifr);
469 static int tx_tap(int fd, void *pkt, size_t len)
471 struct iovec iov[] = {
477 return writev(fd, iov, ARRAY_SIZE(iov));
480 static int ifup(const char *ifname)
482 struct ifreq ifr = {};
485 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
487 sk = socket(PF_INET, SOCK_DGRAM, 0);
491 ret = ioctl(sk, SIOCGIFFLAGS, &ifr);
497 ifr.ifr_flags |= IFF_UP;
498 ret = ioctl(sk, SIOCSIFFLAGS, &ifr);
508 static int init_prog_array(struct bpf_object *obj, struct bpf_map *prog_array)
510 int i, err, map_fd, prog_fd;
511 struct bpf_program *prog;
514 map_fd = bpf_map__fd(prog_array);
518 for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
519 snprintf(prog_name, sizeof(prog_name), "flow_dissector_%d", i);
521 prog = bpf_object__find_program_by_name(obj, prog_name);
525 prog_fd = bpf_program__fd(prog);
529 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
536 static void run_tests_skb_less(int tap_fd, struct bpf_map *keys)
540 keys_fd = bpf_map__fd(keys);
541 if (CHECK(keys_fd < 0, "bpf_map__fd", "err %d\n", keys_fd))
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 |
553 /* For skb-less case we can't pass input flags; run
554 * only the tests that have a matching set of flags.
557 if (tests[i].flags != eth_get_headlen_flags)
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);
563 /* check the stored flow_keys only if BPF_OK expected */
564 if (tests[i].retval != BPF_OK)
567 err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
568 ASSERT_OK(err, "bpf_map_lookup_elem");
570 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
572 err = bpf_map_delete_elem(keys_fd, &key);
573 ASSERT_OK(err, "bpf_map_delete_elem");
577 static void test_skb_less_prog_attach(struct bpf_flow *skel, int tap_fd)
581 prog_fd = bpf_program__fd(skel->progs._dissect);
582 if (CHECK(prog_fd < 0, "bpf_program__fd", "err %d\n", prog_fd))
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))
589 run_tests_skb_less(tap_fd, skel->maps.last_dissection);
591 err = bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
592 CHECK(err, "bpf_prog_detach2", "err %d errno %d\n", err, errno);
595 static void test_skb_less_link_create(struct bpf_flow *skel, int tap_fd)
597 struct bpf_link *link;
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))
604 link = bpf_program__attach_netns(skel->progs._dissect, net_fd);
605 if (!ASSERT_OK_PTR(link, "attach_netns"))
608 run_tests_skb_less(tap_fd, skel->maps.last_dissection);
610 err = bpf_link__destroy(link);
611 CHECK(err, "bpf_link__destroy", "err %d\n", err);
616 void test_flow_dissector(void)
618 int i, err, prog_fd, keys_fd = -1, tap_fd;
619 struct bpf_flow *skel;
621 skel = bpf_flow__open_and_load();
622 if (CHECK(!skel, "skel", "failed to open/load skeleton\n"))
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;
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,
642 static struct bpf_flow_keys ctx = {};
644 if (tests[i].flags) {
646 topts.ctx_size_in = sizeof(ctx);
647 ctx.flags = tests[i].flags;
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");
654 /* check the resulting flow_keys only if BPF_OK returned */
655 if (topts.retval != BPF_OK)
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);
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.
668 tap_fd = create_tap("tap0");
669 CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d\n", tap_fd, errno);
671 CHECK(err, "ifup", "err %d errno %d\n", err, errno);
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);
680 bpf_flow__destroy(skel);