1 // SPDX-License-Identifier: GPL-2.0 2 #include <test_progs.h> 3 #include <error.h> 4 #include <linux/if.h> 5 #include <linux/if_tun.h> 6 #include <sys/uio.h> 7 8 #ifndef IP_MF 9 #define IP_MF 0x2000 10 #endif 11 12 #define CHECK_FLOW_KEYS(desc, got, expected) \ 13 CHECK_ATTR(memcmp(&got, &expected, sizeof(got)) != 0, \ 14 desc, \ 15 "nhoff=%u/%u " \ 16 "thoff=%u/%u " \ 17 "addr_proto=0x%x/0x%x " \ 18 "is_frag=%u/%u " \ 19 "is_first_frag=%u/%u " \ 20 "is_encap=%u/%u " \ 21 "ip_proto=0x%x/0x%x " \ 22 "n_proto=0x%x/0x%x " \ 23 "flow_label=0x%x/0x%x " \ 24 "sport=%u/%u " \ 25 "dport=%u/%u\n", \ 26 got.nhoff, expected.nhoff, \ 27 got.thoff, expected.thoff, \ 28 got.addr_proto, expected.addr_proto, \ 29 got.is_frag, expected.is_frag, \ 30 got.is_first_frag, expected.is_first_frag, \ 31 got.is_encap, expected.is_encap, \ 32 got.ip_proto, expected.ip_proto, \ 33 got.n_proto, expected.n_proto, \ 34 got.flow_label, expected.flow_label, \ 35 got.sport, expected.sport, \ 36 got.dport, expected.dport) 37 38 struct ipv4_pkt { 39 struct ethhdr eth; 40 struct iphdr iph; 41 struct tcphdr tcp; 42 } __packed; 43 44 struct ipip_pkt { 45 struct ethhdr eth; 46 struct iphdr iph; 47 struct iphdr iph_inner; 48 struct tcphdr tcp; 49 } __packed; 50 51 struct svlan_ipv4_pkt { 52 struct ethhdr eth; 53 __u16 vlan_tci; 54 __u16 vlan_proto; 55 struct iphdr iph; 56 struct tcphdr tcp; 57 } __packed; 58 59 struct ipv6_pkt { 60 struct ethhdr eth; 61 struct ipv6hdr iph; 62 struct tcphdr tcp; 63 } __packed; 64 65 struct ipv6_frag_pkt { 66 struct ethhdr eth; 67 struct ipv6hdr iph; 68 struct frag_hdr { 69 __u8 nexthdr; 70 __u8 reserved; 71 __be16 frag_off; 72 __be32 identification; 73 } ipf; 74 struct tcphdr tcp; 75 } __packed; 76 77 struct dvlan_ipv6_pkt { 78 struct ethhdr eth; 79 __u16 vlan_tci; 80 __u16 vlan_proto; 81 __u16 vlan_tci2; 82 __u16 vlan_proto2; 83 struct ipv6hdr iph; 84 struct tcphdr tcp; 85 } __packed; 86 87 struct test { 88 const char *name; 89 union { 90 struct ipv4_pkt ipv4; 91 struct svlan_ipv4_pkt svlan_ipv4; 92 struct ipip_pkt ipip; 93 struct ipv6_pkt ipv6; 94 struct ipv6_frag_pkt ipv6_frag; 95 struct dvlan_ipv6_pkt dvlan_ipv6; 96 } pkt; 97 struct bpf_flow_keys keys; 98 __u32 flags; 99 }; 100 101 #define VLAN_HLEN 4 102 103 struct test tests[] = { 104 { 105 .name = "ipv4", 106 .pkt.ipv4 = { 107 .eth.h_proto = __bpf_constant_htons(ETH_P_IP), 108 .iph.ihl = 5, 109 .iph.protocol = IPPROTO_TCP, 110 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 111 .tcp.doff = 5, 112 .tcp.source = 80, 113 .tcp.dest = 8080, 114 }, 115 .keys = { 116 .nhoff = ETH_HLEN, 117 .thoff = ETH_HLEN + sizeof(struct iphdr), 118 .addr_proto = ETH_P_IP, 119 .ip_proto = IPPROTO_TCP, 120 .n_proto = __bpf_constant_htons(ETH_P_IP), 121 .sport = 80, 122 .dport = 8080, 123 }, 124 }, 125 { 126 .name = "ipv6", 127 .pkt.ipv6 = { 128 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), 129 .iph.nexthdr = IPPROTO_TCP, 130 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 131 .tcp.doff = 5, 132 .tcp.source = 80, 133 .tcp.dest = 8080, 134 }, 135 .keys = { 136 .nhoff = ETH_HLEN, 137 .thoff = ETH_HLEN + sizeof(struct ipv6hdr), 138 .addr_proto = ETH_P_IPV6, 139 .ip_proto = IPPROTO_TCP, 140 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 141 .sport = 80, 142 .dport = 8080, 143 }, 144 }, 145 { 146 .name = "802.1q-ipv4", 147 .pkt.svlan_ipv4 = { 148 .eth.h_proto = __bpf_constant_htons(ETH_P_8021Q), 149 .vlan_proto = __bpf_constant_htons(ETH_P_IP), 150 .iph.ihl = 5, 151 .iph.protocol = IPPROTO_TCP, 152 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 153 .tcp.doff = 5, 154 .tcp.source = 80, 155 .tcp.dest = 8080, 156 }, 157 .keys = { 158 .nhoff = ETH_HLEN + VLAN_HLEN, 159 .thoff = ETH_HLEN + VLAN_HLEN + sizeof(struct iphdr), 160 .addr_proto = ETH_P_IP, 161 .ip_proto = IPPROTO_TCP, 162 .n_proto = __bpf_constant_htons(ETH_P_IP), 163 .sport = 80, 164 .dport = 8080, 165 }, 166 }, 167 { 168 .name = "802.1ad-ipv6", 169 .pkt.dvlan_ipv6 = { 170 .eth.h_proto = __bpf_constant_htons(ETH_P_8021AD), 171 .vlan_proto = __bpf_constant_htons(ETH_P_8021Q), 172 .vlan_proto2 = __bpf_constant_htons(ETH_P_IPV6), 173 .iph.nexthdr = IPPROTO_TCP, 174 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 175 .tcp.doff = 5, 176 .tcp.source = 80, 177 .tcp.dest = 8080, 178 }, 179 .keys = { 180 .nhoff = ETH_HLEN + VLAN_HLEN * 2, 181 .thoff = ETH_HLEN + VLAN_HLEN * 2 + 182 sizeof(struct ipv6hdr), 183 .addr_proto = ETH_P_IPV6, 184 .ip_proto = IPPROTO_TCP, 185 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 186 .sport = 80, 187 .dport = 8080, 188 }, 189 }, 190 { 191 .name = "ipv4-frag", 192 .pkt.ipv4 = { 193 .eth.h_proto = __bpf_constant_htons(ETH_P_IP), 194 .iph.ihl = 5, 195 .iph.protocol = IPPROTO_TCP, 196 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 197 .iph.frag_off = __bpf_constant_htons(IP_MF), 198 .tcp.doff = 5, 199 .tcp.source = 80, 200 .tcp.dest = 8080, 201 }, 202 .keys = { 203 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG, 204 .nhoff = ETH_HLEN, 205 .thoff = ETH_HLEN + sizeof(struct iphdr), 206 .addr_proto = ETH_P_IP, 207 .ip_proto = IPPROTO_TCP, 208 .n_proto = __bpf_constant_htons(ETH_P_IP), 209 .is_frag = true, 210 .is_first_frag = true, 211 .sport = 80, 212 .dport = 8080, 213 }, 214 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG, 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 }, 238 { 239 .name = "ipv6-frag", 240 .pkt.ipv6_frag = { 241 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), 242 .iph.nexthdr = IPPROTO_FRAGMENT, 243 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 244 .ipf.nexthdr = IPPROTO_TCP, 245 .tcp.doff = 5, 246 .tcp.source = 80, 247 .tcp.dest = 8080, 248 }, 249 .keys = { 250 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG, 251 .nhoff = ETH_HLEN, 252 .thoff = ETH_HLEN + sizeof(struct ipv6hdr) + 253 sizeof(struct frag_hdr), 254 .addr_proto = ETH_P_IPV6, 255 .ip_proto = IPPROTO_TCP, 256 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 257 .is_frag = true, 258 .is_first_frag = true, 259 .sport = 80, 260 .dport = 8080, 261 }, 262 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG, 263 }, 264 { 265 .name = "ipv6-no-frag", 266 .pkt.ipv6_frag = { 267 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), 268 .iph.nexthdr = IPPROTO_FRAGMENT, 269 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 270 .ipf.nexthdr = IPPROTO_TCP, 271 .tcp.doff = 5, 272 .tcp.source = 80, 273 .tcp.dest = 8080, 274 }, 275 .keys = { 276 .nhoff = ETH_HLEN, 277 .thoff = ETH_HLEN + sizeof(struct ipv6hdr) + 278 sizeof(struct frag_hdr), 279 .addr_proto = ETH_P_IPV6, 280 .ip_proto = IPPROTO_TCP, 281 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 282 .is_frag = true, 283 .is_first_frag = true, 284 }, 285 }, 286 { 287 .name = "ipv6-flow-label", 288 .pkt.ipv6 = { 289 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), 290 .iph.nexthdr = IPPROTO_TCP, 291 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 292 .iph.flow_lbl = { 0xb, 0xee, 0xef }, 293 .tcp.doff = 5, 294 .tcp.source = 80, 295 .tcp.dest = 8080, 296 }, 297 .keys = { 298 .nhoff = ETH_HLEN, 299 .thoff = ETH_HLEN + sizeof(struct ipv6hdr), 300 .addr_proto = ETH_P_IPV6, 301 .ip_proto = IPPROTO_TCP, 302 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 303 .sport = 80, 304 .dport = 8080, 305 .flow_label = __bpf_constant_htonl(0xbeeef), 306 }, 307 }, 308 { 309 .name = "ipv6-no-flow-label", 310 .pkt.ipv6 = { 311 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), 312 .iph.nexthdr = IPPROTO_TCP, 313 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 314 .iph.flow_lbl = { 0xb, 0xee, 0xef }, 315 .tcp.doff = 5, 316 .tcp.source = 80, 317 .tcp.dest = 8080, 318 }, 319 .keys = { 320 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL, 321 .nhoff = ETH_HLEN, 322 .thoff = ETH_HLEN + sizeof(struct ipv6hdr), 323 .addr_proto = ETH_P_IPV6, 324 .ip_proto = IPPROTO_TCP, 325 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 326 .flow_label = __bpf_constant_htonl(0xbeeef), 327 }, 328 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL, 329 }, 330 { 331 .name = "ipip-encap", 332 .pkt.ipip = { 333 .eth.h_proto = __bpf_constant_htons(ETH_P_IP), 334 .iph.ihl = 5, 335 .iph.protocol = IPPROTO_IPIP, 336 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 337 .iph_inner.ihl = 5, 338 .iph_inner.protocol = IPPROTO_TCP, 339 .iph_inner.tot_len = 340 __bpf_constant_htons(MAGIC_BYTES) - 341 sizeof(struct iphdr), 342 .tcp.doff = 5, 343 .tcp.source = 80, 344 .tcp.dest = 8080, 345 }, 346 .keys = { 347 .nhoff = 0, 348 .nhoff = ETH_HLEN, 349 .thoff = ETH_HLEN + sizeof(struct iphdr) + 350 sizeof(struct iphdr), 351 .addr_proto = ETH_P_IP, 352 .ip_proto = IPPROTO_TCP, 353 .n_proto = __bpf_constant_htons(ETH_P_IP), 354 .is_encap = true, 355 .sport = 80, 356 .dport = 8080, 357 }, 358 }, 359 { 360 .name = "ipip-no-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 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP, 377 .nhoff = ETH_HLEN, 378 .thoff = ETH_HLEN + sizeof(struct iphdr), 379 .addr_proto = ETH_P_IP, 380 .ip_proto = IPPROTO_IPIP, 381 .n_proto = __bpf_constant_htons(ETH_P_IP), 382 .is_encap = true, 383 }, 384 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP, 385 }, 386 }; 387 388 static int create_tap(const char *ifname) 389 { 390 struct ifreq ifr = { 391 .ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS, 392 }; 393 int fd, ret; 394 395 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 396 397 fd = open("/dev/net/tun", O_RDWR); 398 if (fd < 0) 399 return -1; 400 401 ret = ioctl(fd, TUNSETIFF, &ifr); 402 if (ret) 403 return -1; 404 405 return fd; 406 } 407 408 static int tx_tap(int fd, void *pkt, size_t len) 409 { 410 struct iovec iov[] = { 411 { 412 .iov_len = len, 413 .iov_base = pkt, 414 }, 415 }; 416 return writev(fd, iov, ARRAY_SIZE(iov)); 417 } 418 419 static int ifup(const char *ifname) 420 { 421 struct ifreq ifr = {}; 422 int sk, ret; 423 424 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 425 426 sk = socket(PF_INET, SOCK_DGRAM, 0); 427 if (sk < 0) 428 return -1; 429 430 ret = ioctl(sk, SIOCGIFFLAGS, &ifr); 431 if (ret) { 432 close(sk); 433 return -1; 434 } 435 436 ifr.ifr_flags |= IFF_UP; 437 ret = ioctl(sk, SIOCSIFFLAGS, &ifr); 438 if (ret) { 439 close(sk); 440 return -1; 441 } 442 443 close(sk); 444 return 0; 445 } 446 447 void test_flow_dissector(void) 448 { 449 int i, err, prog_fd, keys_fd = -1, tap_fd; 450 struct bpf_object *obj; 451 __u32 duration = 0; 452 453 err = bpf_flow_load(&obj, "./bpf_flow.o", "flow_dissector", 454 "jmp_table", "last_dissection", &prog_fd, &keys_fd); 455 if (err) { 456 error_cnt++; 457 return; 458 } 459 460 for (i = 0; i < ARRAY_SIZE(tests); i++) { 461 struct bpf_flow_keys flow_keys; 462 struct bpf_prog_test_run_attr tattr = { 463 .prog_fd = prog_fd, 464 .data_in = &tests[i].pkt, 465 .data_size_in = sizeof(tests[i].pkt), 466 .data_out = &flow_keys, 467 }; 468 static struct bpf_flow_keys ctx = {}; 469 470 if (tests[i].flags) { 471 tattr.ctx_in = &ctx; 472 tattr.ctx_size_in = sizeof(ctx); 473 ctx.flags = tests[i].flags; 474 } 475 476 err = bpf_prog_test_run_xattr(&tattr); 477 CHECK_ATTR(tattr.data_size_out != sizeof(flow_keys) || 478 err || tattr.retval != 1, 479 tests[i].name, 480 "err %d errno %d retval %d duration %d size %u/%lu\n", 481 err, errno, tattr.retval, tattr.duration, 482 tattr.data_size_out, sizeof(flow_keys)); 483 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys); 484 } 485 486 /* Do the same tests but for skb-less flow dissector. 487 * We use a known path in the net/tun driver that calls 488 * eth_get_headlen and we manually export bpf_flow_keys 489 * via BPF map in this case. 490 */ 491 492 err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0); 493 CHECK(err, "bpf_prog_attach", "err %d errno %d\n", err, errno); 494 495 tap_fd = create_tap("tap0"); 496 CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d\n", tap_fd, errno); 497 err = ifup("tap0"); 498 CHECK(err, "ifup", "err %d errno %d\n", err, errno); 499 500 for (i = 0; i < ARRAY_SIZE(tests); i++) { 501 /* Keep in sync with 'flags' from eth_get_headlen. */ 502 __u32 eth_get_headlen_flags = 503 BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG; 504 struct bpf_prog_test_run_attr tattr = {}; 505 struct bpf_flow_keys flow_keys = {}; 506 __u32 key = (__u32)(tests[i].keys.sport) << 16 | 507 tests[i].keys.dport; 508 509 /* For skb-less case we can't pass input flags; run 510 * only the tests that have a matching set of flags. 511 */ 512 513 if (tests[i].flags != eth_get_headlen_flags) 514 continue; 515 516 err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt)); 517 CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno); 518 519 err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys); 520 CHECK_ATTR(err, tests[i].name, "bpf_map_lookup_elem %d\n", err); 521 522 CHECK_ATTR(err, tests[i].name, "skb-less err %d\n", err); 523 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys); 524 525 err = bpf_map_delete_elem(keys_fd, &key); 526 CHECK_ATTR(err, tests[i].name, "bpf_map_delete_elem %d\n", err); 527 } 528 529 bpf_prog_detach(prog_fd, BPF_FLOW_DISSECTOR); 530 bpf_object__close(obj); 531 } 532