root/tools/testing/selftests/bpf/prog_tests/flow_dissector.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. create_tap
  2. tx_tap
  3. ifup
  4. test_flow_dissector

   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 = ETH_HLEN,
 348                         .thoff = ETH_HLEN + sizeof(struct iphdr) +
 349                                 sizeof(struct iphdr),
 350                         .addr_proto = ETH_P_IP,
 351                         .ip_proto = IPPROTO_TCP,
 352                         .n_proto = __bpf_constant_htons(ETH_P_IP),
 353                         .is_encap = true,
 354                         .sport = 80,
 355                         .dport = 8080,
 356                 },
 357         },
 358         {
 359                 .name = "ipip-no-encap",
 360                 .pkt.ipip = {
 361                         .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
 362                         .iph.ihl = 5,
 363                         .iph.protocol = IPPROTO_IPIP,
 364                         .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
 365                         .iph_inner.ihl = 5,
 366                         .iph_inner.protocol = IPPROTO_TCP,
 367                         .iph_inner.tot_len =
 368                                 __bpf_constant_htons(MAGIC_BYTES) -
 369                                 sizeof(struct iphdr),
 370                         .tcp.doff = 5,
 371                         .tcp.source = 80,
 372                         .tcp.dest = 8080,
 373                 },
 374                 .keys = {
 375                         .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
 376                         .nhoff = ETH_HLEN,
 377                         .thoff = ETH_HLEN + sizeof(struct iphdr),
 378                         .addr_proto = ETH_P_IP,
 379                         .ip_proto = IPPROTO_IPIP,
 380                         .n_proto = __bpf_constant_htons(ETH_P_IP),
 381                         .is_encap = true,
 382                 },
 383                 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
 384         },
 385 };
 386 
 387 static int create_tap(const char *ifname)
 388 {
 389         struct ifreq ifr = {
 390                 .ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS,
 391         };
 392         int fd, ret;
 393 
 394         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
 395 
 396         fd = open("/dev/net/tun", O_RDWR);
 397         if (fd < 0)
 398                 return -1;
 399 
 400         ret = ioctl(fd, TUNSETIFF, &ifr);
 401         if (ret)
 402                 return -1;
 403 
 404         return fd;
 405 }
 406 
 407 static int tx_tap(int fd, void *pkt, size_t len)
 408 {
 409         struct iovec iov[] = {
 410                 {
 411                         .iov_len = len,
 412                         .iov_base = pkt,
 413                 },
 414         };
 415         return writev(fd, iov, ARRAY_SIZE(iov));
 416 }
 417 
 418 static int ifup(const char *ifname)
 419 {
 420         struct ifreq ifr = {};
 421         int sk, ret;
 422 
 423         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
 424 
 425         sk = socket(PF_INET, SOCK_DGRAM, 0);
 426         if (sk < 0)
 427                 return -1;
 428 
 429         ret = ioctl(sk, SIOCGIFFLAGS, &ifr);
 430         if (ret) {
 431                 close(sk);
 432                 return -1;
 433         }
 434 
 435         ifr.ifr_flags |= IFF_UP;
 436         ret = ioctl(sk, SIOCSIFFLAGS, &ifr);
 437         if (ret) {
 438                 close(sk);
 439                 return -1;
 440         }
 441 
 442         close(sk);
 443         return 0;
 444 }
 445 
 446 void test_flow_dissector(void)
 447 {
 448         int i, err, prog_fd, keys_fd = -1, tap_fd;
 449         struct bpf_object *obj;
 450         __u32 duration = 0;
 451 
 452         err = bpf_flow_load(&obj, "./bpf_flow.o", "flow_dissector",
 453                             "jmp_table", "last_dissection", &prog_fd, &keys_fd);
 454         if (CHECK_FAIL(err))
 455                 return;
 456 
 457         for (i = 0; i < ARRAY_SIZE(tests); i++) {
 458                 struct bpf_flow_keys flow_keys;
 459                 struct bpf_prog_test_run_attr tattr = {
 460                         .prog_fd = prog_fd,
 461                         .data_in = &tests[i].pkt,
 462                         .data_size_in = sizeof(tests[i].pkt),
 463                         .data_out = &flow_keys,
 464                 };
 465                 static struct bpf_flow_keys ctx = {};
 466 
 467                 if (tests[i].flags) {
 468                         tattr.ctx_in = &ctx;
 469                         tattr.ctx_size_in = sizeof(ctx);
 470                         ctx.flags = tests[i].flags;
 471                 }
 472 
 473                 err = bpf_prog_test_run_xattr(&tattr);
 474                 CHECK_ATTR(tattr.data_size_out != sizeof(flow_keys) ||
 475                            err || tattr.retval != 1,
 476                            tests[i].name,
 477                            "err %d errno %d retval %d duration %d size %u/%lu\n",
 478                            err, errno, tattr.retval, tattr.duration,
 479                            tattr.data_size_out, sizeof(flow_keys));
 480                 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
 481         }
 482 
 483         /* Do the same tests but for skb-less flow dissector.
 484          * We use a known path in the net/tun driver that calls
 485          * eth_get_headlen and we manually export bpf_flow_keys
 486          * via BPF map in this case.
 487          */
 488 
 489         err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
 490         CHECK(err, "bpf_prog_attach", "err %d errno %d\n", err, errno);
 491 
 492         tap_fd = create_tap("tap0");
 493         CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d\n", tap_fd, errno);
 494         err = ifup("tap0");
 495         CHECK(err, "ifup", "err %d errno %d\n", err, errno);
 496 
 497         for (i = 0; i < ARRAY_SIZE(tests); i++) {
 498                 /* Keep in sync with 'flags' from eth_get_headlen. */
 499                 __u32 eth_get_headlen_flags =
 500                         BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
 501                 struct bpf_prog_test_run_attr tattr = {};
 502                 struct bpf_flow_keys flow_keys = {};
 503                 __u32 key = (__u32)(tests[i].keys.sport) << 16 |
 504                             tests[i].keys.dport;
 505 
 506                 /* For skb-less case we can't pass input flags; run
 507                  * only the tests that have a matching set of flags.
 508                  */
 509 
 510                 if (tests[i].flags != eth_get_headlen_flags)
 511                         continue;
 512 
 513                 err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
 514                 CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno);
 515 
 516                 err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
 517                 CHECK_ATTR(err, tests[i].name, "bpf_map_lookup_elem %d\n", err);
 518 
 519                 CHECK_ATTR(err, tests[i].name, "skb-less err %d\n", err);
 520                 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
 521 
 522                 err = bpf_map_delete_elem(keys_fd, &key);
 523                 CHECK_ATTR(err, tests[i].name, "bpf_map_delete_elem %d\n", err);
 524         }
 525 
 526         bpf_prog_detach(prog_fd, BPF_FLOW_DISSECTOR);
 527         bpf_object__close(obj);
 528 }

/* [<][>][^][v][top][bottom][index][help] */