1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 #include <test_progs.h> 4 #include <network_helpers.h> 5 #include <linux/if_tun.h> 6 #include <sys/uio.h> 7 8 #include "bpf_flow.skel.h" 9 10 #define TEST_NS "flow_dissector_ns" 11 #define FLOW_CONTINUE_SADDR 0x7f00007f /* 127.0.0.127 */ 12 #define TEST_NAME_MAX_LEN 64 13 14 #ifndef IP_MF 15 #define IP_MF 0x2000 16 #endif 17 18 struct ipv4_pkt { 19 struct ethhdr eth; 20 struct iphdr iph; 21 struct tcphdr tcp; 22 } __packed; 23 24 struct ipip_pkt { 25 struct ethhdr eth; 26 struct iphdr iph; 27 struct iphdr iph_inner; 28 struct tcphdr tcp; 29 } __packed; 30 31 struct svlan_ipv4_pkt { 32 struct ethhdr eth; 33 __u16 vlan_tci; 34 __u16 vlan_proto; 35 struct iphdr iph; 36 struct tcphdr tcp; 37 } __packed; 38 39 struct ipv6_pkt { 40 struct ethhdr eth; 41 struct ipv6hdr iph; 42 struct tcphdr tcp; 43 } __packed; 44 45 struct ipv6_frag_pkt { 46 struct ethhdr eth; 47 struct ipv6hdr iph; 48 struct frag_hdr { 49 __u8 nexthdr; 50 __u8 reserved; 51 __be16 frag_off; 52 __be32 identification; 53 } ipf; 54 struct tcphdr tcp; 55 } __packed; 56 57 struct dvlan_ipv6_pkt { 58 struct ethhdr eth; 59 __u16 vlan_tci; 60 __u16 vlan_proto; 61 __u16 vlan_tci2; 62 __u16 vlan_proto2; 63 struct ipv6hdr iph; 64 struct tcphdr tcp; 65 } __packed; 66 67 struct gre_base_hdr { 68 __be16 flags; 69 __be16 protocol; 70 } gre_base_hdr; 71 72 struct gre_minimal_pkt { 73 struct ethhdr eth; 74 struct iphdr iph; 75 struct gre_base_hdr gre_hdr; 76 struct iphdr iph_inner; 77 struct tcphdr tcp; 78 } __packed; 79 80 struct test { 81 const char *name; 82 union { 83 struct ipv4_pkt ipv4; 84 struct svlan_ipv4_pkt svlan_ipv4; 85 struct ipip_pkt ipip; 86 struct ipv6_pkt ipv6; 87 struct ipv6_frag_pkt ipv6_frag; 88 struct dvlan_ipv6_pkt dvlan_ipv6; 89 struct gre_minimal_pkt gre_minimal; 90 } pkt; 91 struct bpf_flow_keys keys; 92 __u32 flags; 93 __u32 retval; 94 }; 95 96 #define VLAN_HLEN 4 97 98 struct test tests[] = { 99 { 100 .name = "ipv4", 101 .pkt.ipv4 = { 102 .eth.h_proto = __bpf_constant_htons(ETH_P_IP), 103 .iph.ihl = 5, 104 .iph.protocol = IPPROTO_TCP, 105 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 106 .tcp.doff = 5, 107 .tcp.source = 80, 108 .tcp.dest = 8080, 109 }, 110 .keys = { 111 .nhoff = ETH_HLEN, 112 .thoff = ETH_HLEN + sizeof(struct iphdr), 113 .addr_proto = ETH_P_IP, 114 .ip_proto = IPPROTO_TCP, 115 .n_proto = __bpf_constant_htons(ETH_P_IP), 116 .sport = 80, 117 .dport = 8080, 118 }, 119 .retval = BPF_OK, 120 }, 121 { 122 .name = "ipv6", 123 .pkt.ipv6 = { 124 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), 125 .iph.nexthdr = IPPROTO_TCP, 126 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 127 .tcp.doff = 5, 128 .tcp.source = 80, 129 .tcp.dest = 8080, 130 }, 131 .keys = { 132 .nhoff = ETH_HLEN, 133 .thoff = ETH_HLEN + sizeof(struct ipv6hdr), 134 .addr_proto = ETH_P_IPV6, 135 .ip_proto = IPPROTO_TCP, 136 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 137 .sport = 80, 138 .dport = 8080, 139 }, 140 .retval = BPF_OK, 141 }, 142 { 143 .name = "802.1q-ipv4", 144 .pkt.svlan_ipv4 = { 145 .eth.h_proto = __bpf_constant_htons(ETH_P_8021Q), 146 .vlan_proto = __bpf_constant_htons(ETH_P_IP), 147 .iph.ihl = 5, 148 .iph.protocol = IPPROTO_TCP, 149 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 150 .tcp.doff = 5, 151 .tcp.source = 80, 152 .tcp.dest = 8080, 153 }, 154 .keys = { 155 .nhoff = ETH_HLEN + VLAN_HLEN, 156 .thoff = ETH_HLEN + VLAN_HLEN + sizeof(struct iphdr), 157 .addr_proto = ETH_P_IP, 158 .ip_proto = IPPROTO_TCP, 159 .n_proto = __bpf_constant_htons(ETH_P_IP), 160 .sport = 80, 161 .dport = 8080, 162 }, 163 .retval = BPF_OK, 164 }, 165 { 166 .name = "802.1ad-ipv6", 167 .pkt.dvlan_ipv6 = { 168 .eth.h_proto = __bpf_constant_htons(ETH_P_8021AD), 169 .vlan_proto = __bpf_constant_htons(ETH_P_8021Q), 170 .vlan_proto2 = __bpf_constant_htons(ETH_P_IPV6), 171 .iph.nexthdr = IPPROTO_TCP, 172 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 173 .tcp.doff = 5, 174 .tcp.source = 80, 175 .tcp.dest = 8080, 176 }, 177 .keys = { 178 .nhoff = ETH_HLEN + VLAN_HLEN * 2, 179 .thoff = ETH_HLEN + VLAN_HLEN * 2 + 180 sizeof(struct ipv6hdr), 181 .addr_proto = ETH_P_IPV6, 182 .ip_proto = IPPROTO_TCP, 183 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 184 .sport = 80, 185 .dport = 8080, 186 }, 187 .retval = BPF_OK, 188 }, 189 { 190 .name = "ipv4-frag", 191 .pkt.ipv4 = { 192 .eth.h_proto = __bpf_constant_htons(ETH_P_IP), 193 .iph.ihl = 5, 194 .iph.protocol = IPPROTO_TCP, 195 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 196 .iph.frag_off = __bpf_constant_htons(IP_MF), 197 .tcp.doff = 5, 198 .tcp.source = 80, 199 .tcp.dest = 8080, 200 }, 201 .keys = { 202 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG, 203 .nhoff = ETH_HLEN, 204 .thoff = ETH_HLEN + sizeof(struct iphdr), 205 .addr_proto = ETH_P_IP, 206 .ip_proto = IPPROTO_TCP, 207 .n_proto = __bpf_constant_htons(ETH_P_IP), 208 .is_frag = true, 209 .is_first_frag = true, 210 .sport = 80, 211 .dport = 8080, 212 }, 213 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG, 214 .retval = BPF_OK, 215 }, 216 { 217 .name = "ipv4-no-frag", 218 .pkt.ipv4 = { 219 .eth.h_proto = __bpf_constant_htons(ETH_P_IP), 220 .iph.ihl = 5, 221 .iph.protocol = IPPROTO_TCP, 222 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 223 .iph.frag_off = __bpf_constant_htons(IP_MF), 224 .tcp.doff = 5, 225 .tcp.source = 80, 226 .tcp.dest = 8080, 227 }, 228 .keys = { 229 .nhoff = ETH_HLEN, 230 .thoff = ETH_HLEN + sizeof(struct iphdr), 231 .addr_proto = ETH_P_IP, 232 .ip_proto = IPPROTO_TCP, 233 .n_proto = __bpf_constant_htons(ETH_P_IP), 234 .is_frag = true, 235 .is_first_frag = true, 236 }, 237 .retval = BPF_OK, 238 }, 239 { 240 .name = "ipv6-frag", 241 .pkt.ipv6_frag = { 242 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), 243 .iph.nexthdr = IPPROTO_FRAGMENT, 244 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 245 .ipf.nexthdr = IPPROTO_TCP, 246 .tcp.doff = 5, 247 .tcp.source = 80, 248 .tcp.dest = 8080, 249 }, 250 .keys = { 251 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG, 252 .nhoff = ETH_HLEN, 253 .thoff = ETH_HLEN + sizeof(struct ipv6hdr) + 254 sizeof(struct frag_hdr), 255 .addr_proto = ETH_P_IPV6, 256 .ip_proto = IPPROTO_TCP, 257 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 258 .is_frag = true, 259 .is_first_frag = true, 260 .sport = 80, 261 .dport = 8080, 262 }, 263 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG, 264 .retval = BPF_OK, 265 }, 266 { 267 .name = "ipv6-no-frag", 268 .pkt.ipv6_frag = { 269 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), 270 .iph.nexthdr = IPPROTO_FRAGMENT, 271 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 272 .ipf.nexthdr = IPPROTO_TCP, 273 .tcp.doff = 5, 274 .tcp.source = 80, 275 .tcp.dest = 8080, 276 }, 277 .keys = { 278 .nhoff = ETH_HLEN, 279 .thoff = ETH_HLEN + sizeof(struct ipv6hdr) + 280 sizeof(struct frag_hdr), 281 .addr_proto = ETH_P_IPV6, 282 .ip_proto = IPPROTO_TCP, 283 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 284 .is_frag = true, 285 .is_first_frag = true, 286 }, 287 .retval = BPF_OK, 288 }, 289 { 290 .name = "ipv6-flow-label", 291 .pkt.ipv6 = { 292 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), 293 .iph.nexthdr = IPPROTO_TCP, 294 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 295 .iph.flow_lbl = { 0xb, 0xee, 0xef }, 296 .tcp.doff = 5, 297 .tcp.source = 80, 298 .tcp.dest = 8080, 299 }, 300 .keys = { 301 .nhoff = ETH_HLEN, 302 .thoff = ETH_HLEN + sizeof(struct ipv6hdr), 303 .addr_proto = ETH_P_IPV6, 304 .ip_proto = IPPROTO_TCP, 305 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 306 .sport = 80, 307 .dport = 8080, 308 .flow_label = __bpf_constant_htonl(0xbeeef), 309 }, 310 .retval = BPF_OK, 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 .retval = BPF_OK, 334 }, 335 { 336 .name = "ipv6-empty-flow-label", 337 .pkt.ipv6 = { 338 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), 339 .iph.nexthdr = IPPROTO_TCP, 340 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 341 .iph.flow_lbl = { 0x00, 0x00, 0x00 }, 342 .tcp.doff = 5, 343 .tcp.source = 80, 344 .tcp.dest = 8080, 345 }, 346 .keys = { 347 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL, 348 .nhoff = ETH_HLEN, 349 .thoff = ETH_HLEN + sizeof(struct ipv6hdr), 350 .addr_proto = ETH_P_IPV6, 351 .ip_proto = IPPROTO_TCP, 352 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 353 .sport = 80, 354 .dport = 8080, 355 }, 356 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL, 357 .retval = BPF_OK, 358 }, 359 { 360 .name = "ipip-encap", 361 .pkt.ipip = { 362 .eth.h_proto = __bpf_constant_htons(ETH_P_IP), 363 .iph.ihl = 5, 364 .iph.protocol = IPPROTO_IPIP, 365 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 366 .iph_inner.ihl = 5, 367 .iph_inner.protocol = IPPROTO_TCP, 368 .iph_inner.tot_len = 369 __bpf_constant_htons(MAGIC_BYTES - 370 sizeof(struct iphdr)), 371 .tcp.doff = 5, 372 .tcp.source = 80, 373 .tcp.dest = 8080, 374 }, 375 .keys = { 376 .nhoff = ETH_HLEN, 377 .thoff = ETH_HLEN + sizeof(struct iphdr) + 378 sizeof(struct iphdr), 379 .addr_proto = ETH_P_IP, 380 .ip_proto = IPPROTO_TCP, 381 .n_proto = __bpf_constant_htons(ETH_P_IP), 382 .is_encap = true, 383 .sport = 80, 384 .dport = 8080, 385 }, 386 .retval = BPF_OK, 387 }, 388 { 389 .name = "ipip-no-encap", 390 .pkt.ipip = { 391 .eth.h_proto = __bpf_constant_htons(ETH_P_IP), 392 .iph.ihl = 5, 393 .iph.protocol = IPPROTO_IPIP, 394 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 395 .iph_inner.ihl = 5, 396 .iph_inner.protocol = IPPROTO_TCP, 397 .iph_inner.tot_len = 398 __bpf_constant_htons(MAGIC_BYTES - 399 sizeof(struct iphdr)), 400 .tcp.doff = 5, 401 .tcp.source = 80, 402 .tcp.dest = 8080, 403 }, 404 .keys = { 405 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP, 406 .nhoff = ETH_HLEN, 407 .thoff = ETH_HLEN + sizeof(struct iphdr), 408 .addr_proto = ETH_P_IP, 409 .ip_proto = IPPROTO_IPIP, 410 .n_proto = __bpf_constant_htons(ETH_P_IP), 411 .is_encap = true, 412 }, 413 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP, 414 .retval = BPF_OK, 415 }, 416 { 417 .name = "ipip-encap-dissector-continue", 418 .pkt.ipip = { 419 .eth.h_proto = __bpf_constant_htons(ETH_P_IP), 420 .iph.ihl = 5, 421 .iph.protocol = IPPROTO_IPIP, 422 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 423 .iph.saddr = __bpf_constant_htonl(FLOW_CONTINUE_SADDR), 424 .iph_inner.ihl = 5, 425 .iph_inner.protocol = IPPROTO_TCP, 426 .iph_inner.tot_len = 427 __bpf_constant_htons(MAGIC_BYTES - 428 sizeof(struct iphdr)), 429 .tcp.doff = 5, 430 .tcp.source = 99, 431 .tcp.dest = 9090, 432 }, 433 .retval = BPF_FLOW_DISSECTOR_CONTINUE, 434 }, 435 { 436 .name = "ip-gre", 437 .pkt.gre_minimal = { 438 .eth.h_proto = __bpf_constant_htons(ETH_P_IP), 439 .iph.ihl = 5, 440 .iph.protocol = IPPROTO_GRE, 441 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 442 .gre_hdr = { 443 .flags = 0, 444 .protocol = __bpf_constant_htons(ETH_P_IP), 445 }, 446 .iph_inner.ihl = 5, 447 .iph_inner.protocol = IPPROTO_TCP, 448 .iph_inner.tot_len = 449 __bpf_constant_htons(MAGIC_BYTES - 450 sizeof(struct iphdr)), 451 .tcp.doff = 5, 452 .tcp.source = 80, 453 .tcp.dest = 8080, 454 }, 455 .keys = { 456 .nhoff = ETH_HLEN, 457 .thoff = ETH_HLEN + sizeof(struct iphdr) * 2 + 458 sizeof(struct gre_base_hdr), 459 .addr_proto = ETH_P_IP, 460 .ip_proto = IPPROTO_TCP, 461 .n_proto = __bpf_constant_htons(ETH_P_IP), 462 .is_encap = true, 463 .sport = 80, 464 .dport = 8080, 465 }, 466 .retval = BPF_OK, 467 }, 468 { 469 .name = "ip-gre-no-encap", 470 .pkt.ipip = { 471 .eth.h_proto = __bpf_constant_htons(ETH_P_IP), 472 .iph.ihl = 5, 473 .iph.protocol = IPPROTO_GRE, 474 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 475 .iph_inner.ihl = 5, 476 .iph_inner.protocol = IPPROTO_TCP, 477 .iph_inner.tot_len = 478 __bpf_constant_htons(MAGIC_BYTES - 479 sizeof(struct iphdr)), 480 .tcp.doff = 5, 481 .tcp.source = 80, 482 .tcp.dest = 8080, 483 }, 484 .keys = { 485 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP, 486 .nhoff = ETH_HLEN, 487 .thoff = ETH_HLEN + sizeof(struct iphdr) 488 + sizeof(struct gre_base_hdr), 489 .addr_proto = ETH_P_IP, 490 .ip_proto = IPPROTO_GRE, 491 .n_proto = __bpf_constant_htons(ETH_P_IP), 492 .is_encap = true, 493 }, 494 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP, 495 .retval = BPF_OK, 496 }, 497 }; 498 499 void serial_test_flow_dissector_namespace(void) 500 { 501 struct bpf_flow *skel; 502 struct nstoken *ns; 503 int err, prog_fd; 504 505 skel = bpf_flow__open_and_load(); 506 if (!ASSERT_OK_PTR(skel, "open/load skeleton")) 507 return; 508 509 prog_fd = bpf_program__fd(skel->progs._dissect); 510 if (!ASSERT_OK_FD(prog_fd, "get dissector fd")) 511 goto out_destroy_skel; 512 513 /* We must be able to attach a flow dissector to root namespace */ 514 err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0); 515 if (!ASSERT_OK(err, "attach on root namespace ok")) 516 goto out_destroy_skel; 517 518 err = make_netns(TEST_NS); 519 if (!ASSERT_OK(err, "create non-root net namespace")) 520 goto out_destroy_skel; 521 522 /* We must not be able to additionally attach a flow dissector to a 523 * non-root net namespace 524 */ 525 ns = open_netns(TEST_NS); 526 if (!ASSERT_OK_PTR(ns, "enter non-root net namespace")) 527 goto out_clean_ns; 528 529 err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0); 530 close_netns(ns); 531 ASSERT_ERR(err, "refuse new flow dissector in non-root net namespace"); 532 ASSERT_EQ(errno, EEXIST, "refused because of already attached prog"); 533 534 /* If no flow dissector is attached to the root namespace, we must 535 * be able to attach one to a non-root net namespace 536 */ 537 bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR); 538 ns = open_netns(TEST_NS); 539 ASSERT_OK_PTR(ns, "enter non-root net namespace"); 540 err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0); 541 close_netns(ns); 542 ASSERT_OK(err, "accept new flow dissector in non-root net namespace"); 543 544 /* If a flow dissector is attached to non-root net namespace, attaching 545 * a flow dissector to root namespace must fail 546 */ 547 err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0); 548 ASSERT_ERR(err, "refuse new flow dissector on root namespace"); 549 ASSERT_EQ(errno, EEXIST, "refused because of already attached prog"); 550 551 ns = open_netns(TEST_NS); 552 bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR); 553 close_netns(ns); 554 out_clean_ns: 555 remove_netns(TEST_NS); 556 out_destroy_skel: 557 bpf_flow__destroy(skel); 558 } 559 560 static int create_tap(const char *ifname) 561 { 562 struct ifreq ifr = { 563 .ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS, 564 }; 565 int fd, ret; 566 567 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 568 569 fd = open("/dev/net/tun", O_RDWR); 570 if (fd < 0) 571 return -1; 572 573 ret = ioctl(fd, TUNSETIFF, &ifr); 574 if (ret) 575 return -1; 576 577 return fd; 578 } 579 580 static int tx_tap(int fd, void *pkt, size_t len) 581 { 582 struct iovec iov[] = { 583 { 584 .iov_len = len, 585 .iov_base = pkt, 586 }, 587 }; 588 return writev(fd, iov, ARRAY_SIZE(iov)); 589 } 590 591 static int ifup(const char *ifname) 592 { 593 struct ifreq ifr = {}; 594 int sk, ret; 595 596 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 597 598 sk = socket(PF_INET, SOCK_DGRAM, 0); 599 if (sk < 0) 600 return -1; 601 602 ret = ioctl(sk, SIOCGIFFLAGS, &ifr); 603 if (ret) { 604 close(sk); 605 return -1; 606 } 607 608 ifr.ifr_flags |= IFF_UP; 609 ret = ioctl(sk, SIOCSIFFLAGS, &ifr); 610 if (ret) { 611 close(sk); 612 return -1; 613 } 614 615 close(sk); 616 return 0; 617 } 618 619 static int init_prog_array(struct bpf_object *obj, struct bpf_map *prog_array) 620 { 621 int i, err, map_fd, prog_fd; 622 struct bpf_program *prog; 623 char prog_name[32]; 624 625 map_fd = bpf_map__fd(prog_array); 626 if (map_fd < 0) 627 return -1; 628 629 for (i = 0; i < bpf_map__max_entries(prog_array); i++) { 630 snprintf(prog_name, sizeof(prog_name), "flow_dissector_%d", i); 631 632 prog = bpf_object__find_program_by_name(obj, prog_name); 633 if (!prog) 634 return -1; 635 636 prog_fd = bpf_program__fd(prog); 637 if (prog_fd < 0) 638 return -1; 639 640 err = bpf_map_update_elem(map_fd, &i, &prog_fd, BPF_ANY); 641 if (err) 642 return -1; 643 } 644 return 0; 645 } 646 647 static void run_tests_skb_less(int tap_fd, struct bpf_map *keys, 648 char *test_suffix) 649 { 650 char test_name[TEST_NAME_MAX_LEN]; 651 int i, err, keys_fd; 652 653 keys_fd = bpf_map__fd(keys); 654 if (!ASSERT_OK_FD(keys_fd, "bpf_map__fd")) 655 return; 656 657 for (i = 0; i < ARRAY_SIZE(tests); i++) { 658 /* Keep in sync with 'flags' from eth_get_headlen. */ 659 __u32 eth_get_headlen_flags = 660 BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG; 661 struct bpf_flow_keys flow_keys = {}; 662 __u32 key = (__u32)(tests[i].keys.sport) << 16 | 663 tests[i].keys.dport; 664 snprintf(test_name, TEST_NAME_MAX_LEN, "%s-%s", tests[i].name, 665 test_suffix); 666 if (!test__start_subtest(test_name)) 667 continue; 668 669 /* For skb-less case we can't pass input flags; run 670 * only the tests that have a matching set of flags. 671 */ 672 673 if (tests[i].flags != eth_get_headlen_flags) 674 continue; 675 676 err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt)); 677 if (!ASSERT_EQ(err, sizeof(tests[i].pkt), "tx_tap")) 678 continue; 679 680 /* check the stored flow_keys only if BPF_OK expected */ 681 if (tests[i].retval != BPF_OK) 682 continue; 683 684 err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys); 685 if (!ASSERT_OK(err, "bpf_map_lookup_elem")) 686 continue; 687 688 ASSERT_MEMEQ(&flow_keys, &tests[i].keys, 689 sizeof(struct bpf_flow_keys), 690 "returned flow keys"); 691 692 err = bpf_map_delete_elem(keys_fd, &key); 693 ASSERT_OK(err, "bpf_map_delete_elem"); 694 } 695 } 696 697 void test_flow_dissector_skb_less_direct_attach(void) 698 { 699 int err, prog_fd, tap_fd; 700 struct bpf_flow *skel; 701 struct netns_obj *ns; 702 703 ns = netns_new("flow_dissector_skb_less_indirect_attach_ns", true); 704 if (!ASSERT_OK_PTR(ns, "create and open netns")) 705 return; 706 707 skel = bpf_flow__open_and_load(); 708 if (!ASSERT_OK_PTR(skel, "open/load skeleton")) 709 goto out_clean_ns; 710 711 err = init_prog_array(skel->obj, skel->maps.jmp_table); 712 if (!ASSERT_OK(err, "init_prog_array")) 713 goto out_destroy_skel; 714 715 prog_fd = bpf_program__fd(skel->progs._dissect); 716 if (!ASSERT_OK_FD(prog_fd, "bpf_program__fd")) 717 goto out_destroy_skel; 718 719 err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0); 720 if (!ASSERT_OK(err, "bpf_prog_attach")) 721 goto out_destroy_skel; 722 723 tap_fd = create_tap("tap0"); 724 if (!ASSERT_OK_FD(tap_fd, "create_tap")) 725 goto out_destroy_skel; 726 err = ifup("tap0"); 727 if (!ASSERT_OK(err, "ifup")) 728 goto out_close_tap; 729 730 run_tests_skb_less(tap_fd, skel->maps.last_dissection, 731 "non-skb-direct-attach"); 732 733 err = bpf_prog_detach2(prog_fd, 0, BPF_FLOW_DISSECTOR); 734 ASSERT_OK(err, "bpf_prog_detach2"); 735 736 out_close_tap: 737 close(tap_fd); 738 out_destroy_skel: 739 bpf_flow__destroy(skel); 740 out_clean_ns: 741 netns_free(ns); 742 } 743 744 void test_flow_dissector_skb_less_indirect_attach(void) 745 { 746 int err, net_fd, tap_fd; 747 struct bpf_flow *skel; 748 struct bpf_link *link; 749 struct netns_obj *ns; 750 751 ns = netns_new("flow_dissector_skb_less_indirect_attach_ns", true); 752 if (!ASSERT_OK_PTR(ns, "create and open netns")) 753 return; 754 755 skel = bpf_flow__open_and_load(); 756 if (!ASSERT_OK_PTR(skel, "open/load skeleton")) 757 goto out_clean_ns; 758 759 net_fd = open("/proc/self/ns/net", O_RDONLY); 760 if (!ASSERT_OK_FD(net_fd, "open(/proc/self/ns/net")) 761 goto out_destroy_skel; 762 763 err = init_prog_array(skel->obj, skel->maps.jmp_table); 764 if (!ASSERT_OK(err, "init_prog_array")) 765 goto out_destroy_skel; 766 767 tap_fd = create_tap("tap0"); 768 if (!ASSERT_OK_FD(tap_fd, "create_tap")) 769 goto out_close_ns; 770 err = ifup("tap0"); 771 if (!ASSERT_OK(err, "ifup")) 772 goto out_close_tap; 773 774 link = bpf_program__attach_netns(skel->progs._dissect, net_fd); 775 if (!ASSERT_OK_PTR(link, "attach_netns")) 776 goto out_close_tap; 777 778 run_tests_skb_less(tap_fd, skel->maps.last_dissection, 779 "non-skb-indirect-attach"); 780 781 err = bpf_link__destroy(link); 782 ASSERT_OK(err, "bpf_link__destroy"); 783 784 out_close_tap: 785 close(tap_fd); 786 out_close_ns: 787 close(net_fd); 788 out_destroy_skel: 789 bpf_flow__destroy(skel); 790 out_clean_ns: 791 netns_free(ns); 792 } 793 794 void test_flow_dissector_skb(void) 795 { 796 char test_name[TEST_NAME_MAX_LEN]; 797 struct bpf_flow *skel; 798 int i, err, prog_fd; 799 800 skel = bpf_flow__open_and_load(); 801 if (!ASSERT_OK_PTR(skel, "open/load skeleton")) 802 return; 803 804 err = init_prog_array(skel->obj, skel->maps.jmp_table); 805 if (!ASSERT_OK(err, "init_prog_array")) 806 goto out_destroy_skel; 807 808 prog_fd = bpf_program__fd(skel->progs._dissect); 809 if (!ASSERT_OK_FD(prog_fd, "bpf_program__fd")) 810 goto out_destroy_skel; 811 812 for (i = 0; i < ARRAY_SIZE(tests); i++) { 813 struct bpf_flow_keys flow_keys; 814 LIBBPF_OPTS(bpf_test_run_opts, topts, 815 .data_in = &tests[i].pkt, 816 .data_size_in = sizeof(tests[i].pkt), 817 .data_out = &flow_keys, 818 ); 819 static struct bpf_flow_keys ctx = {}; 820 821 snprintf(test_name, TEST_NAME_MAX_LEN, "%s-skb", tests[i].name); 822 if (!test__start_subtest(test_name)) 823 continue; 824 825 if (tests[i].flags) { 826 topts.ctx_in = &ctx; 827 topts.ctx_size_in = sizeof(ctx); 828 ctx.flags = tests[i].flags; 829 } 830 831 err = bpf_prog_test_run_opts(prog_fd, &topts); 832 ASSERT_OK(err, "test_run"); 833 ASSERT_EQ(topts.retval, tests[i].retval, "test_run retval"); 834 835 /* check the resulting flow_keys only if BPF_OK returned */ 836 if (topts.retval != BPF_OK) 837 continue; 838 ASSERT_EQ(topts.data_size_out, sizeof(flow_keys), 839 "test_run data_size_out"); 840 ASSERT_MEMEQ(&flow_keys, &tests[i].keys, 841 sizeof(struct bpf_flow_keys), 842 "returned flow keys"); 843 } 844 845 out_destroy_skel: 846 bpf_flow__destroy(skel); 847 } 848 849