0001
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
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
0497
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
0598
0599
0600
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
0609 test_skb_less_prog_attach(skel, tap_fd);
0610
0611 test_skb_less_link_create(skel, tap_fd);
0612
0613 close(tap_fd);
0614 out_destroy_skel:
0615 bpf_flow__destroy(skel);
0616 }