1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include <network_helpers.h>
6 #include <linux/if_tun.h>
9 #include "bpf_flow.skel.h"
11 #define FLOW_CONTINUE_SADDR 0x7f00007f /* 127.0.0.127 */
17 #define CHECK_FLOW_KEYS(desc, got, expected) \
18 _CHECK(memcmp(&got, &expected, sizeof(got)) != 0, \
23 "addr_proto=0x%x/0x%x " \
25 "is_first_frag=%u/%u " \
27 "ip_proto=0x%x/0x%x " \
28 "n_proto=0x%x/0x%x " \
29 "flow_label=0x%x/0x%x " \
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)
53 struct iphdr iph_inner;
57 struct svlan_ipv4_pkt {
71 struct ipv6_frag_pkt {
78 __be32 identification;
83 struct dvlan_ipv6_pkt {
97 struct svlan_ipv4_pkt svlan_ipv4;
100 struct ipv6_frag_pkt ipv6_frag;
101 struct dvlan_ipv6_pkt dvlan_ipv6;
103 struct bpf_flow_keys keys;
110 static __u32 duration;
111 struct test tests[] = {
115 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
117 .iph.protocol = IPPROTO_TCP,
118 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
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),
137 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
138 .iph.nexthdr = IPPROTO_TCP,
139 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
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),
156 .name = "802.1q-ipv4",
158 .eth.h_proto = __bpf_constant_htons(ETH_P_8021Q),
159 .vlan_proto = __bpf_constant_htons(ETH_P_IP),
161 .iph.protocol = IPPROTO_TCP,
162 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
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),
179 .name = "802.1ad-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),
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),
205 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
207 .iph.protocol = IPPROTO_TCP,
208 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
209 .iph.frag_off = __bpf_constant_htons(IP_MF),
215 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
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),
222 .is_first_frag = true,
226 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
230 .name = "ipv4-no-frag",
232 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
234 .iph.protocol = IPPROTO_TCP,
235 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
236 .iph.frag_off = __bpf_constant_htons(IP_MF),
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),
248 .is_first_frag = true,
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,
264 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
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),
272 .is_first_frag = true,
276 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
280 .name = "ipv6-no-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,
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),
298 .is_first_frag = true,
303 .name = "ipv6-flow-label",
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 },
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),
321 .flow_label = __bpf_constant_htonl(0xbeeef),
326 .name = "ipv6-no-flow-label",
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 },
337 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
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),
345 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
349 .name = "ipip-encap",
351 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
353 .iph.protocol = IPPROTO_IPIP,
354 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
356 .iph_inner.protocol = IPPROTO_TCP,
358 __bpf_constant_htons(MAGIC_BYTES) -
359 sizeof(struct iphdr),
366 .thoff = ETH_HLEN + sizeof(struct iphdr) +
367 sizeof(struct iphdr),
368 .addr_proto = ETH_P_IP,
369 .ip_proto = IPPROTO_TCP,
370 .n_proto = __bpf_constant_htons(ETH_P_IP),
378 .name = "ipip-no-encap",
380 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
382 .iph.protocol = IPPROTO_IPIP,
383 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
385 .iph_inner.protocol = IPPROTO_TCP,
387 __bpf_constant_htons(MAGIC_BYTES) -
388 sizeof(struct iphdr),
394 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
396 .thoff = ETH_HLEN + sizeof(struct iphdr),
397 .addr_proto = ETH_P_IP,
398 .ip_proto = IPPROTO_IPIP,
399 .n_proto = __bpf_constant_htons(ETH_P_IP),
402 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
406 .name = "ipip-encap-dissector-continue",
408 .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
410 .iph.protocol = IPPROTO_IPIP,
411 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
412 .iph.saddr = __bpf_constant_htonl(FLOW_CONTINUE_SADDR),
414 .iph_inner.protocol = IPPROTO_TCP,
416 __bpf_constant_htons(MAGIC_BYTES) -
417 sizeof(struct iphdr),
422 .retval = BPF_FLOW_DISSECTOR_CONTINUE,
426 static int create_tap(const char *ifname)
429 .ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS,
433 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
435 fd = open("/dev/net/tun", O_RDWR);
439 ret = ioctl(fd, TUNSETIFF, &ifr);
446 static int tx_tap(int fd, void *pkt, size_t len)
448 struct iovec iov[] = {
454 return writev(fd, iov, ARRAY_SIZE(iov));
457 static int ifup(const char *ifname)
459 struct ifreq ifr = {};
462 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
464 sk = socket(PF_INET, SOCK_DGRAM, 0);
468 ret = ioctl(sk, SIOCGIFFLAGS, &ifr);
474 ifr.ifr_flags |= IFF_UP;
475 ret = ioctl(sk, SIOCSIFFLAGS, &ifr);
485 static int init_prog_array(struct bpf_object *obj, struct bpf_map *prog_array)
487 int i, err, map_fd, prog_fd;
488 struct bpf_program *prog;
491 map_fd = bpf_map__fd(prog_array);
495 for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
496 snprintf(prog_name, sizeof(prog_name), "flow_dissector_%d", i);
498 prog = bpf_object__find_program_by_name(obj, prog_name);
502 prog_fd = bpf_program__fd(prog);
506 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
513 static void run_tests_skb_less(int tap_fd, struct bpf_map *keys)
517 keys_fd = bpf_map__fd(keys);
518 if (CHECK(keys_fd < 0, "bpf_map__fd", "err %d\n", keys_fd))
521 for (i = 0; i < ARRAY_SIZE(tests); i++) {
522 /* Keep in sync with 'flags' from eth_get_headlen. */
523 __u32 eth_get_headlen_flags =
524 BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
525 LIBBPF_OPTS(bpf_test_run_opts, topts);
526 struct bpf_flow_keys flow_keys = {};
527 __u32 key = (__u32)(tests[i].keys.sport) << 16 |
530 /* For skb-less case we can't pass input flags; run
531 * only the tests that have a matching set of flags.
534 if (tests[i].flags != eth_get_headlen_flags)
537 err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
538 CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno);
540 /* check the stored flow_keys only if BPF_OK expected */
541 if (tests[i].retval != BPF_OK)
544 err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
545 ASSERT_OK(err, "bpf_map_lookup_elem");
547 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
549 err = bpf_map_delete_elem(keys_fd, &key);
550 ASSERT_OK(err, "bpf_map_delete_elem");
554 static void test_skb_less_prog_attach(struct bpf_flow *skel, int tap_fd)
558 prog_fd = bpf_program__fd(skel->progs._dissect);
559 if (CHECK(prog_fd < 0, "bpf_program__fd", "err %d\n", prog_fd))
562 err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
563 if (CHECK(err, "bpf_prog_attach", "err %d errno %d\n", err, errno))
566 run_tests_skb_less(tap_fd, skel->maps.last_dissection);
568 err = bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
569 CHECK(err, "bpf_prog_detach2", "err %d errno %d\n", err, errno);
572 static void test_skb_less_link_create(struct bpf_flow *skel, int tap_fd)
574 struct bpf_link *link;
577 net_fd = open("/proc/self/ns/net", O_RDONLY);
578 if (CHECK(net_fd < 0, "open(/proc/self/ns/net)", "err %d\n", errno))
581 link = bpf_program__attach_netns(skel->progs._dissect, net_fd);
582 if (!ASSERT_OK_PTR(link, "attach_netns"))
585 run_tests_skb_less(tap_fd, skel->maps.last_dissection);
587 err = bpf_link__destroy(link);
588 CHECK(err, "bpf_link__destroy", "err %d\n", err);
593 void test_flow_dissector(void)
595 int i, err, prog_fd, keys_fd = -1, tap_fd;
596 struct bpf_flow *skel;
598 skel = bpf_flow__open_and_load();
599 if (CHECK(!skel, "skel", "failed to open/load skeleton\n"))
602 prog_fd = bpf_program__fd(skel->progs._dissect);
603 if (CHECK(prog_fd < 0, "bpf_program__fd", "err %d\n", prog_fd))
604 goto out_destroy_skel;
605 keys_fd = bpf_map__fd(skel->maps.last_dissection);
606 if (CHECK(keys_fd < 0, "bpf_map__fd", "err %d\n", keys_fd))
607 goto out_destroy_skel;
608 err = init_prog_array(skel->obj, skel->maps.jmp_table);
609 if (CHECK(err, "init_prog_array", "err %d\n", err))
610 goto out_destroy_skel;
612 for (i = 0; i < ARRAY_SIZE(tests); i++) {
613 struct bpf_flow_keys flow_keys;
614 LIBBPF_OPTS(bpf_test_run_opts, topts,
615 .data_in = &tests[i].pkt,
616 .data_size_in = sizeof(tests[i].pkt),
617 .data_out = &flow_keys,
619 static struct bpf_flow_keys ctx = {};
621 if (tests[i].flags) {
623 topts.ctx_size_in = sizeof(ctx);
624 ctx.flags = tests[i].flags;
627 err = bpf_prog_test_run_opts(prog_fd, &topts);
628 ASSERT_OK(err, "test_run");
629 ASSERT_EQ(topts.retval, tests[i].retval, "test_run retval");
631 /* check the resulting flow_keys only if BPF_OK returned */
632 if (topts.retval != BPF_OK)
634 ASSERT_EQ(topts.data_size_out, sizeof(flow_keys),
635 "test_run data_size_out");
636 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
639 /* Do the same tests but for skb-less flow dissector.
640 * We use a known path in the net/tun driver that calls
641 * eth_get_headlen and we manually export bpf_flow_keys
642 * via BPF map in this case.
645 tap_fd = create_tap("tap0");
646 CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d\n", tap_fd, errno);
648 CHECK(err, "ifup", "err %d errno %d\n", err, errno);
650 /* Test direct prog attachment */
651 test_skb_less_prog_attach(skel, tap_fd);
652 /* Test indirect prog attachment via link */
653 test_skb_less_link_create(skel, tap_fd);
657 bpf_flow__destroy(skel);