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 41 struct ipv4_pkt { 42 struct ethhdr eth; 43 struct iphdr iph; 44 struct tcphdr tcp; 45 } __packed; 46 47 struct ipip_pkt { 48 struct ethhdr eth; 49 struct iphdr iph; 50 struct iphdr iph_inner; 51 struct tcphdr tcp; 52 } __packed; 53 54 struct 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 62 struct ipv6_pkt { 63 struct ethhdr eth; 64 struct ipv6hdr iph; 65 struct tcphdr tcp; 66 } __packed; 67 68 struct 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 80 struct 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 90 struct 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 106 static __u32 duration; 107 struct 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 391 static 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 411 static 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 422 static 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 450 static 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 478 static 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 516 static 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 534 static 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); 551 out_close: 552 close(net_fd); 553 } 554 555 void 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/%lu\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); 617 out_destroy_skel: 618 bpf_flow__destroy(skel); 619 } 620