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 #ifndef IP_MF 10 #define IP_MF 0x2000 11 #endif 12 13 #define CHECK_FLOW_KEYS(desc, got, expected) \ 14 CHECK_ATTR(memcmp(&got, &expected, sizeof(got)) != 0, \ 15 desc, \ 16 "nhoff=%u/%u " \ 17 "thoff=%u/%u " \ 18 "addr_proto=0x%x/0x%x " \ 19 "is_frag=%u/%u " \ 20 "is_first_frag=%u/%u " \ 21 "is_encap=%u/%u " \ 22 "ip_proto=0x%x/0x%x " \ 23 "n_proto=0x%x/0x%x " \ 24 "flow_label=0x%x/0x%x " \ 25 "sport=%u/%u " \ 26 "dport=%u/%u\n", \ 27 got.nhoff, expected.nhoff, \ 28 got.thoff, expected.thoff, \ 29 got.addr_proto, expected.addr_proto, \ 30 got.is_frag, expected.is_frag, \ 31 got.is_first_frag, expected.is_first_frag, \ 32 got.is_encap, expected.is_encap, \ 33 got.ip_proto, expected.ip_proto, \ 34 got.n_proto, expected.n_proto, \ 35 got.flow_label, expected.flow_label, \ 36 got.sport, expected.sport, \ 37 got.dport, expected.dport) 38 39 struct ipv4_pkt { 40 struct ethhdr eth; 41 struct iphdr iph; 42 struct tcphdr tcp; 43 } __packed; 44 45 struct ipip_pkt { 46 struct ethhdr eth; 47 struct iphdr iph; 48 struct iphdr iph_inner; 49 struct tcphdr tcp; 50 } __packed; 51 52 struct svlan_ipv4_pkt { 53 struct ethhdr eth; 54 __u16 vlan_tci; 55 __u16 vlan_proto; 56 struct iphdr iph; 57 struct tcphdr tcp; 58 } __packed; 59 60 struct ipv6_pkt { 61 struct ethhdr eth; 62 struct ipv6hdr iph; 63 struct tcphdr tcp; 64 } __packed; 65 66 struct ipv6_frag_pkt { 67 struct ethhdr eth; 68 struct ipv6hdr iph; 69 struct frag_hdr { 70 __u8 nexthdr; 71 __u8 reserved; 72 __be16 frag_off; 73 __be32 identification; 74 } ipf; 75 struct tcphdr tcp; 76 } __packed; 77 78 struct dvlan_ipv6_pkt { 79 struct ethhdr eth; 80 __u16 vlan_tci; 81 __u16 vlan_proto; 82 __u16 vlan_tci2; 83 __u16 vlan_proto2; 84 struct ipv6hdr iph; 85 struct tcphdr tcp; 86 } __packed; 87 88 struct test { 89 const char *name; 90 union { 91 struct ipv4_pkt ipv4; 92 struct svlan_ipv4_pkt svlan_ipv4; 93 struct ipip_pkt ipip; 94 struct ipv6_pkt ipv6; 95 struct ipv6_frag_pkt ipv6_frag; 96 struct dvlan_ipv6_pkt dvlan_ipv6; 97 } pkt; 98 struct bpf_flow_keys keys; 99 __u32 flags; 100 }; 101 102 #define VLAN_HLEN 4 103 104 struct test tests[] = { 105 { 106 .name = "ipv4", 107 .pkt.ipv4 = { 108 .eth.h_proto = __bpf_constant_htons(ETH_P_IP), 109 .iph.ihl = 5, 110 .iph.protocol = IPPROTO_TCP, 111 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 112 .tcp.doff = 5, 113 .tcp.source = 80, 114 .tcp.dest = 8080, 115 }, 116 .keys = { 117 .nhoff = ETH_HLEN, 118 .thoff = ETH_HLEN + sizeof(struct iphdr), 119 .addr_proto = ETH_P_IP, 120 .ip_proto = IPPROTO_TCP, 121 .n_proto = __bpf_constant_htons(ETH_P_IP), 122 .sport = 80, 123 .dport = 8080, 124 }, 125 }, 126 { 127 .name = "ipv6", 128 .pkt.ipv6 = { 129 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), 130 .iph.nexthdr = IPPROTO_TCP, 131 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 132 .tcp.doff = 5, 133 .tcp.source = 80, 134 .tcp.dest = 8080, 135 }, 136 .keys = { 137 .nhoff = ETH_HLEN, 138 .thoff = ETH_HLEN + sizeof(struct ipv6hdr), 139 .addr_proto = ETH_P_IPV6, 140 .ip_proto = IPPROTO_TCP, 141 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 142 .sport = 80, 143 .dport = 8080, 144 }, 145 }, 146 { 147 .name = "802.1q-ipv4", 148 .pkt.svlan_ipv4 = { 149 .eth.h_proto = __bpf_constant_htons(ETH_P_8021Q), 150 .vlan_proto = __bpf_constant_htons(ETH_P_IP), 151 .iph.ihl = 5, 152 .iph.protocol = IPPROTO_TCP, 153 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 154 .tcp.doff = 5, 155 .tcp.source = 80, 156 .tcp.dest = 8080, 157 }, 158 .keys = { 159 .nhoff = ETH_HLEN + VLAN_HLEN, 160 .thoff = ETH_HLEN + VLAN_HLEN + sizeof(struct iphdr), 161 .addr_proto = ETH_P_IP, 162 .ip_proto = IPPROTO_TCP, 163 .n_proto = __bpf_constant_htons(ETH_P_IP), 164 .sport = 80, 165 .dport = 8080, 166 }, 167 }, 168 { 169 .name = "802.1ad-ipv6", 170 .pkt.dvlan_ipv6 = { 171 .eth.h_proto = __bpf_constant_htons(ETH_P_8021AD), 172 .vlan_proto = __bpf_constant_htons(ETH_P_8021Q), 173 .vlan_proto2 = __bpf_constant_htons(ETH_P_IPV6), 174 .iph.nexthdr = IPPROTO_TCP, 175 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 176 .tcp.doff = 5, 177 .tcp.source = 80, 178 .tcp.dest = 8080, 179 }, 180 .keys = { 181 .nhoff = ETH_HLEN + VLAN_HLEN * 2, 182 .thoff = ETH_HLEN + VLAN_HLEN * 2 + 183 sizeof(struct ipv6hdr), 184 .addr_proto = ETH_P_IPV6, 185 .ip_proto = IPPROTO_TCP, 186 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 187 .sport = 80, 188 .dport = 8080, 189 }, 190 }, 191 { 192 .name = "ipv4-frag", 193 .pkt.ipv4 = { 194 .eth.h_proto = __bpf_constant_htons(ETH_P_IP), 195 .iph.ihl = 5, 196 .iph.protocol = IPPROTO_TCP, 197 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 198 .iph.frag_off = __bpf_constant_htons(IP_MF), 199 .tcp.doff = 5, 200 .tcp.source = 80, 201 .tcp.dest = 8080, 202 }, 203 .keys = { 204 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG, 205 .nhoff = ETH_HLEN, 206 .thoff = ETH_HLEN + sizeof(struct iphdr), 207 .addr_proto = ETH_P_IP, 208 .ip_proto = IPPROTO_TCP, 209 .n_proto = __bpf_constant_htons(ETH_P_IP), 210 .is_frag = true, 211 .is_first_frag = true, 212 .sport = 80, 213 .dport = 8080, 214 }, 215 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG, 216 }, 217 { 218 .name = "ipv4-no-frag", 219 .pkt.ipv4 = { 220 .eth.h_proto = __bpf_constant_htons(ETH_P_IP), 221 .iph.ihl = 5, 222 .iph.protocol = IPPROTO_TCP, 223 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 224 .iph.frag_off = __bpf_constant_htons(IP_MF), 225 .tcp.doff = 5, 226 .tcp.source = 80, 227 .tcp.dest = 8080, 228 }, 229 .keys = { 230 .nhoff = ETH_HLEN, 231 .thoff = ETH_HLEN + sizeof(struct iphdr), 232 .addr_proto = ETH_P_IP, 233 .ip_proto = IPPROTO_TCP, 234 .n_proto = __bpf_constant_htons(ETH_P_IP), 235 .is_frag = true, 236 .is_first_frag = true, 237 }, 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 }, 265 { 266 .name = "ipv6-no-frag", 267 .pkt.ipv6_frag = { 268 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), 269 .iph.nexthdr = IPPROTO_FRAGMENT, 270 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 271 .ipf.nexthdr = IPPROTO_TCP, 272 .tcp.doff = 5, 273 .tcp.source = 80, 274 .tcp.dest = 8080, 275 }, 276 .keys = { 277 .nhoff = ETH_HLEN, 278 .thoff = ETH_HLEN + sizeof(struct ipv6hdr) + 279 sizeof(struct frag_hdr), 280 .addr_proto = ETH_P_IPV6, 281 .ip_proto = IPPROTO_TCP, 282 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 283 .is_frag = true, 284 .is_first_frag = true, 285 }, 286 }, 287 { 288 .name = "ipv6-flow-label", 289 .pkt.ipv6 = { 290 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), 291 .iph.nexthdr = IPPROTO_TCP, 292 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 293 .iph.flow_lbl = { 0xb, 0xee, 0xef }, 294 .tcp.doff = 5, 295 .tcp.source = 80, 296 .tcp.dest = 8080, 297 }, 298 .keys = { 299 .nhoff = ETH_HLEN, 300 .thoff = ETH_HLEN + sizeof(struct ipv6hdr), 301 .addr_proto = ETH_P_IPV6, 302 .ip_proto = IPPROTO_TCP, 303 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 304 .sport = 80, 305 .dport = 8080, 306 .flow_label = __bpf_constant_htonl(0xbeeef), 307 }, 308 }, 309 { 310 .name = "ipv6-no-flow-label", 311 .pkt.ipv6 = { 312 .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), 313 .iph.nexthdr = IPPROTO_TCP, 314 .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), 315 .iph.flow_lbl = { 0xb, 0xee, 0xef }, 316 .tcp.doff = 5, 317 .tcp.source = 80, 318 .tcp.dest = 8080, 319 }, 320 .keys = { 321 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL, 322 .nhoff = ETH_HLEN, 323 .thoff = ETH_HLEN + sizeof(struct ipv6hdr), 324 .addr_proto = ETH_P_IPV6, 325 .ip_proto = IPPROTO_TCP, 326 .n_proto = __bpf_constant_htons(ETH_P_IPV6), 327 .flow_label = __bpf_constant_htonl(0xbeeef), 328 }, 329 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL, 330 }, 331 { 332 .name = "ipip-encap", 333 .pkt.ipip = { 334 .eth.h_proto = __bpf_constant_htons(ETH_P_IP), 335 .iph.ihl = 5, 336 .iph.protocol = IPPROTO_IPIP, 337 .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES), 338 .iph_inner.ihl = 5, 339 .iph_inner.protocol = IPPROTO_TCP, 340 .iph_inner.tot_len = 341 __bpf_constant_htons(MAGIC_BYTES) - 342 sizeof(struct iphdr), 343 .tcp.doff = 5, 344 .tcp.source = 80, 345 .tcp.dest = 8080, 346 }, 347 .keys = { 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 (CHECK_FAIL(err)) 456 return; 457 458 for (i = 0; i < ARRAY_SIZE(tests); i++) { 459 struct bpf_flow_keys flow_keys; 460 struct bpf_prog_test_run_attr tattr = { 461 .prog_fd = prog_fd, 462 .data_in = &tests[i].pkt, 463 .data_size_in = sizeof(tests[i].pkt), 464 .data_out = &flow_keys, 465 }; 466 static struct bpf_flow_keys ctx = {}; 467 468 if (tests[i].flags) { 469 tattr.ctx_in = &ctx; 470 tattr.ctx_size_in = sizeof(ctx); 471 ctx.flags = tests[i].flags; 472 } 473 474 err = bpf_prog_test_run_xattr(&tattr); 475 CHECK_ATTR(tattr.data_size_out != sizeof(flow_keys) || 476 err || tattr.retval != 1, 477 tests[i].name, 478 "err %d errno %d retval %d duration %d size %u/%lu\n", 479 err, errno, tattr.retval, tattr.duration, 480 tattr.data_size_out, sizeof(flow_keys)); 481 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys); 482 } 483 484 /* Do the same tests but for skb-less flow dissector. 485 * We use a known path in the net/tun driver that calls 486 * eth_get_headlen and we manually export bpf_flow_keys 487 * via BPF map in this case. 488 */ 489 490 err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0); 491 CHECK(err, "bpf_prog_attach", "err %d errno %d\n", err, errno); 492 493 tap_fd = create_tap("tap0"); 494 CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d\n", tap_fd, errno); 495 err = ifup("tap0"); 496 CHECK(err, "ifup", "err %d errno %d\n", err, errno); 497 498 for (i = 0; i < ARRAY_SIZE(tests); i++) { 499 /* Keep in sync with 'flags' from eth_get_headlen. */ 500 __u32 eth_get_headlen_flags = 501 BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG; 502 struct bpf_prog_test_run_attr tattr = {}; 503 struct bpf_flow_keys flow_keys = {}; 504 __u32 key = (__u32)(tests[i].keys.sport) << 16 | 505 tests[i].keys.dport; 506 507 /* For skb-less case we can't pass input flags; run 508 * only the tests that have a matching set of flags. 509 */ 510 511 if (tests[i].flags != eth_get_headlen_flags) 512 continue; 513 514 err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt)); 515 CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno); 516 517 err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys); 518 CHECK_ATTR(err, tests[i].name, "bpf_map_lookup_elem %d\n", err); 519 520 CHECK_ATTR(err, tests[i].name, "skb-less err %d\n", err); 521 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys); 522 523 err = bpf_map_delete_elem(keys_fd, &key); 524 CHECK_ATTR(err, tests[i].name, "bpf_map_delete_elem %d\n", err); 525 } 526 527 bpf_prog_detach(prog_fd, BPF_FLOW_DISSECTOR); 528 bpf_object__close(obj); 529 } 530