Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <test_progs.h>
0003 #include <network_helpers.h>
0004 #include <error.h>
0005 #include <linux/if.h>
0006 #include <linux/if_tun.h>
0007 #include <sys/uio.h>
0008 
0009 #include "bpf_flow.skel.h"
0010 
0011 #ifndef IP_MF
0012 #define IP_MF 0x2000
0013 #endif
0014 
0015 #define CHECK_FLOW_KEYS(desc, got, expected)                \
0016     _CHECK(memcmp(&got, &expected, sizeof(got)) != 0,       \
0017           desc,                         \
0018           topts.duration,                       \
0019           "nhoff=%u/%u "                        \
0020           "thoff=%u/%u "                        \
0021           "addr_proto=0x%x/0x%x "                   \
0022           "is_frag=%u/%u "                      \
0023           "is_first_frag=%u/%u "                    \
0024           "is_encap=%u/%u "                     \
0025           "ip_proto=0x%x/0x%x "                 \
0026           "n_proto=0x%x/0x%x "                  \
0027           "flow_label=0x%x/0x%x "                   \
0028           "sport=%u/%u "                        \
0029           "dport=%u/%u\n",                      \
0030           got.nhoff, expected.nhoff,                \
0031           got.thoff, expected.thoff,                \
0032           got.addr_proto, expected.addr_proto,          \
0033           got.is_frag, expected.is_frag,                \
0034           got.is_first_frag, expected.is_first_frag,        \
0035           got.is_encap, expected.is_encap,              \
0036           got.ip_proto, expected.ip_proto,              \
0037           got.n_proto, expected.n_proto,                \
0038           got.flow_label, expected.flow_label,          \
0039           got.sport, expected.sport,                \
0040           got.dport, expected.dport)
0041 
0042 struct ipv4_pkt {
0043     struct ethhdr eth;
0044     struct iphdr iph;
0045     struct tcphdr tcp;
0046 } __packed;
0047 
0048 struct ipip_pkt {
0049     struct ethhdr eth;
0050     struct iphdr iph;
0051     struct iphdr iph_inner;
0052     struct tcphdr tcp;
0053 } __packed;
0054 
0055 struct svlan_ipv4_pkt {
0056     struct ethhdr eth;
0057     __u16 vlan_tci;
0058     __u16 vlan_proto;
0059     struct iphdr iph;
0060     struct tcphdr tcp;
0061 } __packed;
0062 
0063 struct ipv6_pkt {
0064     struct ethhdr eth;
0065     struct ipv6hdr iph;
0066     struct tcphdr tcp;
0067 } __packed;
0068 
0069 struct ipv6_frag_pkt {
0070     struct ethhdr eth;
0071     struct ipv6hdr iph;
0072     struct frag_hdr {
0073         __u8 nexthdr;
0074         __u8 reserved;
0075         __be16 frag_off;
0076         __be32 identification;
0077     } ipf;
0078     struct tcphdr tcp;
0079 } __packed;
0080 
0081 struct dvlan_ipv6_pkt {
0082     struct ethhdr eth;
0083     __u16 vlan_tci;
0084     __u16 vlan_proto;
0085     __u16 vlan_tci2;
0086     __u16 vlan_proto2;
0087     struct ipv6hdr iph;
0088     struct tcphdr tcp;
0089 } __packed;
0090 
0091 struct test {
0092     const char *name;
0093     union {
0094         struct ipv4_pkt ipv4;
0095         struct svlan_ipv4_pkt svlan_ipv4;
0096         struct ipip_pkt ipip;
0097         struct ipv6_pkt ipv6;
0098         struct ipv6_frag_pkt ipv6_frag;
0099         struct dvlan_ipv6_pkt dvlan_ipv6;
0100     } pkt;
0101     struct bpf_flow_keys keys;
0102     __u32 flags;
0103 };
0104 
0105 #define VLAN_HLEN   4
0106 
0107 static __u32 duration;
0108 struct test tests[] = {
0109     {
0110         .name = "ipv4",
0111         .pkt.ipv4 = {
0112             .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
0113             .iph.ihl = 5,
0114             .iph.protocol = IPPROTO_TCP,
0115             .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
0116             .tcp.doff = 5,
0117             .tcp.source = 80,
0118             .tcp.dest = 8080,
0119         },
0120         .keys = {
0121             .nhoff = ETH_HLEN,
0122             .thoff = ETH_HLEN + sizeof(struct iphdr),
0123             .addr_proto = ETH_P_IP,
0124             .ip_proto = IPPROTO_TCP,
0125             .n_proto = __bpf_constant_htons(ETH_P_IP),
0126             .sport = 80,
0127             .dport = 8080,
0128         },
0129     },
0130     {
0131         .name = "ipv6",
0132         .pkt.ipv6 = {
0133             .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
0134             .iph.nexthdr = IPPROTO_TCP,
0135             .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
0136             .tcp.doff = 5,
0137             .tcp.source = 80,
0138             .tcp.dest = 8080,
0139         },
0140         .keys = {
0141             .nhoff = ETH_HLEN,
0142             .thoff = ETH_HLEN + sizeof(struct ipv6hdr),
0143             .addr_proto = ETH_P_IPV6,
0144             .ip_proto = IPPROTO_TCP,
0145             .n_proto = __bpf_constant_htons(ETH_P_IPV6),
0146             .sport = 80,
0147             .dport = 8080,
0148         },
0149     },
0150     {
0151         .name = "802.1q-ipv4",
0152         .pkt.svlan_ipv4 = {
0153             .eth.h_proto = __bpf_constant_htons(ETH_P_8021Q),
0154             .vlan_proto = __bpf_constant_htons(ETH_P_IP),
0155             .iph.ihl = 5,
0156             .iph.protocol = IPPROTO_TCP,
0157             .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
0158             .tcp.doff = 5,
0159             .tcp.source = 80,
0160             .tcp.dest = 8080,
0161         },
0162         .keys = {
0163             .nhoff = ETH_HLEN + VLAN_HLEN,
0164             .thoff = ETH_HLEN + VLAN_HLEN + sizeof(struct iphdr),
0165             .addr_proto = ETH_P_IP,
0166             .ip_proto = IPPROTO_TCP,
0167             .n_proto = __bpf_constant_htons(ETH_P_IP),
0168             .sport = 80,
0169             .dport = 8080,
0170         },
0171     },
0172     {
0173         .name = "802.1ad-ipv6",
0174         .pkt.dvlan_ipv6 = {
0175             .eth.h_proto = __bpf_constant_htons(ETH_P_8021AD),
0176             .vlan_proto = __bpf_constant_htons(ETH_P_8021Q),
0177             .vlan_proto2 = __bpf_constant_htons(ETH_P_IPV6),
0178             .iph.nexthdr = IPPROTO_TCP,
0179             .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
0180             .tcp.doff = 5,
0181             .tcp.source = 80,
0182             .tcp.dest = 8080,
0183         },
0184         .keys = {
0185             .nhoff = ETH_HLEN + VLAN_HLEN * 2,
0186             .thoff = ETH_HLEN + VLAN_HLEN * 2 +
0187                 sizeof(struct ipv6hdr),
0188             .addr_proto = ETH_P_IPV6,
0189             .ip_proto = IPPROTO_TCP,
0190             .n_proto = __bpf_constant_htons(ETH_P_IPV6),
0191             .sport = 80,
0192             .dport = 8080,
0193         },
0194     },
0195     {
0196         .name = "ipv4-frag",
0197         .pkt.ipv4 = {
0198             .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
0199             .iph.ihl = 5,
0200             .iph.protocol = IPPROTO_TCP,
0201             .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
0202             .iph.frag_off = __bpf_constant_htons(IP_MF),
0203             .tcp.doff = 5,
0204             .tcp.source = 80,
0205             .tcp.dest = 8080,
0206         },
0207         .keys = {
0208             .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
0209             .nhoff = ETH_HLEN,
0210             .thoff = ETH_HLEN + sizeof(struct iphdr),
0211             .addr_proto = ETH_P_IP,
0212             .ip_proto = IPPROTO_TCP,
0213             .n_proto = __bpf_constant_htons(ETH_P_IP),
0214             .is_frag = true,
0215             .is_first_frag = true,
0216             .sport = 80,
0217             .dport = 8080,
0218         },
0219         .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
0220     },
0221     {
0222         .name = "ipv4-no-frag",
0223         .pkt.ipv4 = {
0224             .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
0225             .iph.ihl = 5,
0226             .iph.protocol = IPPROTO_TCP,
0227             .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
0228             .iph.frag_off = __bpf_constant_htons(IP_MF),
0229             .tcp.doff = 5,
0230             .tcp.source = 80,
0231             .tcp.dest = 8080,
0232         },
0233         .keys = {
0234             .nhoff = ETH_HLEN,
0235             .thoff = ETH_HLEN + sizeof(struct iphdr),
0236             .addr_proto = ETH_P_IP,
0237             .ip_proto = IPPROTO_TCP,
0238             .n_proto = __bpf_constant_htons(ETH_P_IP),
0239             .is_frag = true,
0240             .is_first_frag = true,
0241         },
0242     },
0243     {
0244         .name = "ipv6-frag",
0245         .pkt.ipv6_frag = {
0246             .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
0247             .iph.nexthdr = IPPROTO_FRAGMENT,
0248             .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
0249             .ipf.nexthdr = IPPROTO_TCP,
0250             .tcp.doff = 5,
0251             .tcp.source = 80,
0252             .tcp.dest = 8080,
0253         },
0254         .keys = {
0255             .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
0256             .nhoff = ETH_HLEN,
0257             .thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
0258                 sizeof(struct frag_hdr),
0259             .addr_proto = ETH_P_IPV6,
0260             .ip_proto = IPPROTO_TCP,
0261             .n_proto = __bpf_constant_htons(ETH_P_IPV6),
0262             .is_frag = true,
0263             .is_first_frag = true,
0264             .sport = 80,
0265             .dport = 8080,
0266         },
0267         .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
0268     },
0269     {
0270         .name = "ipv6-no-frag",
0271         .pkt.ipv6_frag = {
0272             .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
0273             .iph.nexthdr = IPPROTO_FRAGMENT,
0274             .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
0275             .ipf.nexthdr = IPPROTO_TCP,
0276             .tcp.doff = 5,
0277             .tcp.source = 80,
0278             .tcp.dest = 8080,
0279         },
0280         .keys = {
0281             .nhoff = ETH_HLEN,
0282             .thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
0283                 sizeof(struct frag_hdr),
0284             .addr_proto = ETH_P_IPV6,
0285             .ip_proto = IPPROTO_TCP,
0286             .n_proto = __bpf_constant_htons(ETH_P_IPV6),
0287             .is_frag = true,
0288             .is_first_frag = true,
0289         },
0290     },
0291     {
0292         .name = "ipv6-flow-label",
0293         .pkt.ipv6 = {
0294             .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
0295             .iph.nexthdr = IPPROTO_TCP,
0296             .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
0297             .iph.flow_lbl = { 0xb, 0xee, 0xef },
0298             .tcp.doff = 5,
0299             .tcp.source = 80,
0300             .tcp.dest = 8080,
0301         },
0302         .keys = {
0303             .nhoff = ETH_HLEN,
0304             .thoff = ETH_HLEN + sizeof(struct ipv6hdr),
0305             .addr_proto = ETH_P_IPV6,
0306             .ip_proto = IPPROTO_TCP,
0307             .n_proto = __bpf_constant_htons(ETH_P_IPV6),
0308             .sport = 80,
0309             .dport = 8080,
0310             .flow_label = __bpf_constant_htonl(0xbeeef),
0311         },
0312     },
0313     {
0314         .name = "ipv6-no-flow-label",
0315         .pkt.ipv6 = {
0316             .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
0317             .iph.nexthdr = IPPROTO_TCP,
0318             .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
0319             .iph.flow_lbl = { 0xb, 0xee, 0xef },
0320             .tcp.doff = 5,
0321             .tcp.source = 80,
0322             .tcp.dest = 8080,
0323         },
0324         .keys = {
0325             .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
0326             .nhoff = ETH_HLEN,
0327             .thoff = ETH_HLEN + sizeof(struct ipv6hdr),
0328             .addr_proto = ETH_P_IPV6,
0329             .ip_proto = IPPROTO_TCP,
0330             .n_proto = __bpf_constant_htons(ETH_P_IPV6),
0331             .flow_label = __bpf_constant_htonl(0xbeeef),
0332         },
0333         .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
0334     },
0335     {
0336         .name = "ipip-encap",
0337         .pkt.ipip = {
0338             .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
0339             .iph.ihl = 5,
0340             .iph.protocol = IPPROTO_IPIP,
0341             .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
0342             .iph_inner.ihl = 5,
0343             .iph_inner.protocol = IPPROTO_TCP,
0344             .iph_inner.tot_len =
0345                 __bpf_constant_htons(MAGIC_BYTES) -
0346                 sizeof(struct iphdr),
0347             .tcp.doff = 5,
0348             .tcp.source = 80,
0349             .tcp.dest = 8080,
0350         },
0351         .keys = {
0352             .nhoff = ETH_HLEN,
0353             .thoff = ETH_HLEN + sizeof(struct iphdr) +
0354                 sizeof(struct iphdr),
0355             .addr_proto = ETH_P_IP,
0356             .ip_proto = IPPROTO_TCP,
0357             .n_proto = __bpf_constant_htons(ETH_P_IP),
0358             .is_encap = true,
0359             .sport = 80,
0360             .dport = 8080,
0361         },
0362     },
0363     {
0364         .name = "ipip-no-encap",
0365         .pkt.ipip = {
0366             .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
0367             .iph.ihl = 5,
0368             .iph.protocol = IPPROTO_IPIP,
0369             .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
0370             .iph_inner.ihl = 5,
0371             .iph_inner.protocol = IPPROTO_TCP,
0372             .iph_inner.tot_len =
0373                 __bpf_constant_htons(MAGIC_BYTES) -
0374                 sizeof(struct iphdr),
0375             .tcp.doff = 5,
0376             .tcp.source = 80,
0377             .tcp.dest = 8080,
0378         },
0379         .keys = {
0380             .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
0381             .nhoff = ETH_HLEN,
0382             .thoff = ETH_HLEN + sizeof(struct iphdr),
0383             .addr_proto = ETH_P_IP,
0384             .ip_proto = IPPROTO_IPIP,
0385             .n_proto = __bpf_constant_htons(ETH_P_IP),
0386             .is_encap = true,
0387         },
0388         .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
0389     },
0390 };
0391 
0392 static int create_tap(const char *ifname)
0393 {
0394     struct ifreq ifr = {
0395         .ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS,
0396     };
0397     int fd, ret;
0398 
0399     strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
0400 
0401     fd = open("/dev/net/tun", O_RDWR);
0402     if (fd < 0)
0403         return -1;
0404 
0405     ret = ioctl(fd, TUNSETIFF, &ifr);
0406     if (ret)
0407         return -1;
0408 
0409     return fd;
0410 }
0411 
0412 static int tx_tap(int fd, void *pkt, size_t len)
0413 {
0414     struct iovec iov[] = {
0415         {
0416             .iov_len = len,
0417             .iov_base = pkt,
0418         },
0419     };
0420     return writev(fd, iov, ARRAY_SIZE(iov));
0421 }
0422 
0423 static int ifup(const char *ifname)
0424 {
0425     struct ifreq ifr = {};
0426     int sk, ret;
0427 
0428     strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
0429 
0430     sk = socket(PF_INET, SOCK_DGRAM, 0);
0431     if (sk < 0)
0432         return -1;
0433 
0434     ret = ioctl(sk, SIOCGIFFLAGS, &ifr);
0435     if (ret) {
0436         close(sk);
0437         return -1;
0438     }
0439 
0440     ifr.ifr_flags |= IFF_UP;
0441     ret = ioctl(sk, SIOCSIFFLAGS, &ifr);
0442     if (ret) {
0443         close(sk);
0444         return -1;
0445     }
0446 
0447     close(sk);
0448     return 0;
0449 }
0450 
0451 static int init_prog_array(struct bpf_object *obj, struct bpf_map *prog_array)
0452 {
0453     int i, err, map_fd, prog_fd;
0454     struct bpf_program *prog;
0455     char prog_name[32];
0456 
0457     map_fd = bpf_map__fd(prog_array);
0458     if (map_fd < 0)
0459         return -1;
0460 
0461     for (i = 0; i < bpf_map__max_entries(prog_array); i++) {
0462         snprintf(prog_name, sizeof(prog_name), "flow_dissector_%d", i);
0463 
0464         prog = bpf_object__find_program_by_name(obj, prog_name);
0465         if (!prog)
0466             return -1;
0467 
0468         prog_fd = bpf_program__fd(prog);
0469         if (prog_fd < 0)
0470             return -1;
0471 
0472         err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY);
0473         if (err)
0474             return -1;
0475     }
0476     return 0;
0477 }
0478 
0479 static void run_tests_skb_less(int tap_fd, struct bpf_map *keys)
0480 {
0481     int i, err, keys_fd;
0482 
0483     keys_fd = bpf_map__fd(keys);
0484     if (CHECK(keys_fd < 0, "bpf_map__fd", "err %d\n", keys_fd))
0485         return;
0486 
0487     for (i = 0; i < ARRAY_SIZE(tests); i++) {
0488         /* Keep in sync with 'flags' from eth_get_headlen. */
0489         __u32 eth_get_headlen_flags =
0490             BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
0491         LIBBPF_OPTS(bpf_test_run_opts, topts);
0492         struct bpf_flow_keys flow_keys = {};
0493         __u32 key = (__u32)(tests[i].keys.sport) << 16 |
0494                 tests[i].keys.dport;
0495 
0496         /* For skb-less case we can't pass input flags; run
0497          * only the tests that have a matching set of flags.
0498          */
0499 
0500         if (tests[i].flags != eth_get_headlen_flags)
0501             continue;
0502 
0503         err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
0504         CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno);
0505 
0506         err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
0507         ASSERT_OK(err, "bpf_map_lookup_elem");
0508 
0509         CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
0510 
0511         err = bpf_map_delete_elem(keys_fd, &key);
0512         ASSERT_OK(err, "bpf_map_delete_elem");
0513     }
0514 }
0515 
0516 static void test_skb_less_prog_attach(struct bpf_flow *skel, int tap_fd)
0517 {
0518     int err, prog_fd;
0519 
0520     prog_fd = bpf_program__fd(skel->progs._dissect);
0521     if (CHECK(prog_fd < 0, "bpf_program__fd", "err %d\n", prog_fd))
0522         return;
0523 
0524     err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
0525     if (CHECK(err, "bpf_prog_attach", "err %d errno %d\n", err, errno))
0526         return;
0527 
0528     run_tests_skb_less(tap_fd, skel->maps.last_dissection);
0529 
0530     err = bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR);
0531     CHECK(err, "bpf_prog_detach2", "err %d errno %d\n", err, errno);
0532 }
0533 
0534 static void test_skb_less_link_create(struct bpf_flow *skel, int tap_fd)
0535 {
0536     struct bpf_link *link;
0537     int err, net_fd;
0538 
0539     net_fd = open("/proc/self/ns/net", O_RDONLY);
0540     if (CHECK(net_fd < 0, "open(/proc/self/ns/net)", "err %d\n", errno))
0541         return;
0542 
0543     link = bpf_program__attach_netns(skel->progs._dissect, net_fd);
0544     if (!ASSERT_OK_PTR(link, "attach_netns"))
0545         goto out_close;
0546 
0547     run_tests_skb_less(tap_fd, skel->maps.last_dissection);
0548 
0549     err = bpf_link__destroy(link);
0550     CHECK(err, "bpf_link__destroy", "err %d\n", err);
0551 out_close:
0552     close(net_fd);
0553 }
0554 
0555 void test_flow_dissector(void)
0556 {
0557     int i, err, prog_fd, keys_fd = -1, tap_fd;
0558     struct bpf_flow *skel;
0559 
0560     skel = bpf_flow__open_and_load();
0561     if (CHECK(!skel, "skel", "failed to open/load skeleton\n"))
0562         return;
0563 
0564     prog_fd = bpf_program__fd(skel->progs._dissect);
0565     if (CHECK(prog_fd < 0, "bpf_program__fd", "err %d\n", prog_fd))
0566         goto out_destroy_skel;
0567     keys_fd = bpf_map__fd(skel->maps.last_dissection);
0568     if (CHECK(keys_fd < 0, "bpf_map__fd", "err %d\n", keys_fd))
0569         goto out_destroy_skel;
0570     err = init_prog_array(skel->obj, skel->maps.jmp_table);
0571     if (CHECK(err, "init_prog_array", "err %d\n", err))
0572         goto out_destroy_skel;
0573 
0574     for (i = 0; i < ARRAY_SIZE(tests); i++) {
0575         struct bpf_flow_keys flow_keys;
0576         LIBBPF_OPTS(bpf_test_run_opts, topts,
0577             .data_in = &tests[i].pkt,
0578             .data_size_in = sizeof(tests[i].pkt),
0579             .data_out = &flow_keys,
0580         );
0581         static struct bpf_flow_keys ctx = {};
0582 
0583         if (tests[i].flags) {
0584             topts.ctx_in = &ctx;
0585             topts.ctx_size_in = sizeof(ctx);
0586             ctx.flags = tests[i].flags;
0587         }
0588 
0589         err = bpf_prog_test_run_opts(prog_fd, &topts);
0590         ASSERT_OK(err, "test_run");
0591         ASSERT_EQ(topts.retval, 1, "test_run retval");
0592         ASSERT_EQ(topts.data_size_out, sizeof(flow_keys),
0593               "test_run data_size_out");
0594         CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
0595     }
0596 
0597     /* Do the same tests but for skb-less flow dissector.
0598      * We use a known path in the net/tun driver that calls
0599      * eth_get_headlen and we manually export bpf_flow_keys
0600      * via BPF map in this case.
0601      */
0602 
0603     tap_fd = create_tap("tap0");
0604     CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d\n", tap_fd, errno);
0605     err = ifup("tap0");
0606     CHECK(err, "ifup", "err %d errno %d\n", err, errno);
0607 
0608     /* Test direct prog attachment */
0609     test_skb_less_prog_attach(skel, tap_fd);
0610     /* Test indirect prog attachment via link */
0611     test_skb_less_link_create(skel, tap_fd);
0612 
0613     close(tap_fd);
0614 out_destroy_skel:
0615     bpf_flow__destroy(skel);
0616 }