linux/tools/testing/selftests/bpf/test_flow_dissector.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Inject packets with all sorts of encapsulation into the kernel.
   4 *
   5 * IPv4/IPv6    outer layer 3
   6 * GRE/GUE/BARE outer layer 4, where bare is IPIP/SIT/IPv4-in-IPv6/..
   7 * IPv4/IPv6    inner layer 3
   8 */
   9
  10#define _GNU_SOURCE
  11
  12#include <stddef.h>
  13#include <arpa/inet.h>
  14#include <asm/byteorder.h>
  15#include <error.h>
  16#include <errno.h>
  17#include <linux/if_packet.h>
  18#include <linux/if_ether.h>
  19#include <linux/ipv6.h>
  20#include <netinet/ip.h>
  21#include <netinet/in.h>
  22#include <netinet/udp.h>
  23#include <poll.h>
  24#include <stdbool.h>
  25#include <stdlib.h>
  26#include <stdio.h>
  27#include <string.h>
  28#include <sys/ioctl.h>
  29#include <sys/socket.h>
  30#include <sys/stat.h>
  31#include <sys/time.h>
  32#include <sys/types.h>
  33#include <unistd.h>
  34
  35#define CFG_PORT_INNER  8000
  36
  37/* Add some protocol definitions that do not exist in userspace */
  38
  39struct grehdr {
  40        uint16_t unused;
  41        uint16_t protocol;
  42} __attribute__((packed));
  43
  44struct guehdr {
  45        union {
  46                struct {
  47#if defined(__LITTLE_ENDIAN_BITFIELD)
  48                        __u8    hlen:5,
  49                                control:1,
  50                                version:2;
  51#elif defined (__BIG_ENDIAN_BITFIELD)
  52                        __u8    version:2,
  53                                control:1,
  54                                hlen:5;
  55#else
  56#error  "Please fix <asm/byteorder.h>"
  57#endif
  58                        __u8    proto_ctype;
  59                        __be16  flags;
  60                };
  61                __be32  word;
  62        };
  63};
  64
  65static uint8_t  cfg_dsfield_inner;
  66static uint8_t  cfg_dsfield_outer;
  67static uint8_t  cfg_encap_proto;
  68static bool     cfg_expect_failure = false;
  69static int      cfg_l3_extra = AF_UNSPEC;       /* optional SIT prefix */
  70static int      cfg_l3_inner = AF_UNSPEC;
  71static int      cfg_l3_outer = AF_UNSPEC;
  72static int      cfg_num_pkt = 10;
  73static int      cfg_num_secs = 0;
  74static char     cfg_payload_char = 'a';
  75static int      cfg_payload_len = 100;
  76static int      cfg_port_gue = 6080;
  77static bool     cfg_only_rx;
  78static bool     cfg_only_tx;
  79static int      cfg_src_port = 9;
  80
  81static char     buf[ETH_DATA_LEN];
  82
  83#define INIT_ADDR4(name, addr4, port)                           \
  84        static struct sockaddr_in name = {                      \
  85                .sin_family = AF_INET,                          \
  86                .sin_port = __constant_htons(port),             \
  87                .sin_addr.s_addr = __constant_htonl(addr4),     \
  88        };
  89
  90#define INIT_ADDR6(name, addr6, port)                           \
  91        static struct sockaddr_in6 name = {                     \
  92                .sin6_family = AF_INET6,                        \
  93                .sin6_port = __constant_htons(port),            \
  94                .sin6_addr = addr6,                             \
  95        };
  96
  97INIT_ADDR4(in_daddr4, INADDR_LOOPBACK, CFG_PORT_INNER)
  98INIT_ADDR4(in_saddr4, INADDR_LOOPBACK + 2, 0)
  99INIT_ADDR4(out_daddr4, INADDR_LOOPBACK, 0)
 100INIT_ADDR4(out_saddr4, INADDR_LOOPBACK + 1, 0)
 101INIT_ADDR4(extra_daddr4, INADDR_LOOPBACK, 0)
 102INIT_ADDR4(extra_saddr4, INADDR_LOOPBACK + 1, 0)
 103
 104INIT_ADDR6(in_daddr6, IN6ADDR_LOOPBACK_INIT, CFG_PORT_INNER)
 105INIT_ADDR6(in_saddr6, IN6ADDR_LOOPBACK_INIT, 0)
 106INIT_ADDR6(out_daddr6, IN6ADDR_LOOPBACK_INIT, 0)
 107INIT_ADDR6(out_saddr6, IN6ADDR_LOOPBACK_INIT, 0)
 108INIT_ADDR6(extra_daddr6, IN6ADDR_LOOPBACK_INIT, 0)
 109INIT_ADDR6(extra_saddr6, IN6ADDR_LOOPBACK_INIT, 0)
 110
 111static unsigned long util_gettime(void)
 112{
 113        struct timeval tv;
 114
 115        gettimeofday(&tv, NULL);
 116        return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
 117}
 118
 119static void util_printaddr(const char *msg, struct sockaddr *addr)
 120{
 121        unsigned long off = 0;
 122        char nbuf[INET6_ADDRSTRLEN];
 123
 124        switch (addr->sa_family) {
 125        case PF_INET:
 126                off = __builtin_offsetof(struct sockaddr_in, sin_addr);
 127                break;
 128        case PF_INET6:
 129                off = __builtin_offsetof(struct sockaddr_in6, sin6_addr);
 130                break;
 131        default:
 132                error(1, 0, "printaddr: unsupported family %u\n",
 133                      addr->sa_family);
 134        }
 135
 136        if (!inet_ntop(addr->sa_family, ((void *) addr) + off, nbuf,
 137                       sizeof(nbuf)))
 138                error(1, errno, "inet_ntop");
 139
 140        fprintf(stderr, "%s: %s\n", msg, nbuf);
 141}
 142
 143static unsigned long add_csum_hword(const uint16_t *start, int num_u16)
 144{
 145        unsigned long sum = 0;
 146        int i;
 147
 148        for (i = 0; i < num_u16; i++)
 149                sum += start[i];
 150
 151        return sum;
 152}
 153
 154static uint16_t build_ip_csum(const uint16_t *start, int num_u16,
 155                              unsigned long sum)
 156{
 157        sum += add_csum_hword(start, num_u16);
 158
 159        while (sum >> 16)
 160                sum = (sum & 0xffff) + (sum >> 16);
 161
 162        return ~sum;
 163}
 164
 165static void build_ipv4_header(void *header, uint8_t proto,
 166                              uint32_t src, uint32_t dst,
 167                              int payload_len, uint8_t tos)
 168{
 169        struct iphdr *iph = header;
 170
 171        iph->ihl = 5;
 172        iph->version = 4;
 173        iph->tos = tos;
 174        iph->ttl = 8;
 175        iph->tot_len = htons(sizeof(*iph) + payload_len);
 176        iph->id = htons(1337);
 177        iph->protocol = proto;
 178        iph->saddr = src;
 179        iph->daddr = dst;
 180        iph->check = build_ip_csum((void *) iph, iph->ihl << 1, 0);
 181}
 182
 183static void ipv6_set_dsfield(struct ipv6hdr *ip6h, uint8_t dsfield)
 184{
 185        uint16_t val, *ptr = (uint16_t *)ip6h;
 186
 187        val = ntohs(*ptr);
 188        val &= 0xF00F;
 189        val |= ((uint16_t) dsfield) << 4;
 190        *ptr = htons(val);
 191}
 192
 193static void build_ipv6_header(void *header, uint8_t proto,
 194                              struct sockaddr_in6 *src,
 195                              struct sockaddr_in6 *dst,
 196                              int payload_len, uint8_t dsfield)
 197{
 198        struct ipv6hdr *ip6h = header;
 199
 200        ip6h->version = 6;
 201        ip6h->payload_len = htons(payload_len);
 202        ip6h->nexthdr = proto;
 203        ip6h->hop_limit = 8;
 204        ipv6_set_dsfield(ip6h, dsfield);
 205
 206        memcpy(&ip6h->saddr, &src->sin6_addr, sizeof(ip6h->saddr));
 207        memcpy(&ip6h->daddr, &dst->sin6_addr, sizeof(ip6h->daddr));
 208}
 209
 210static uint16_t build_udp_v4_csum(const struct iphdr *iph,
 211                                  const struct udphdr *udph,
 212                                  int num_words)
 213{
 214        unsigned long pseudo_sum;
 215        int num_u16 = sizeof(iph->saddr);       /* halfwords: twice byte len */
 216
 217        pseudo_sum = add_csum_hword((void *) &iph->saddr, num_u16);
 218        pseudo_sum += htons(IPPROTO_UDP);
 219        pseudo_sum += udph->len;
 220        return build_ip_csum((void *) udph, num_words, pseudo_sum);
 221}
 222
 223static uint16_t build_udp_v6_csum(const struct ipv6hdr *ip6h,
 224                                  const struct udphdr *udph,
 225                                  int num_words)
 226{
 227        unsigned long pseudo_sum;
 228        int num_u16 = sizeof(ip6h->saddr);      /* halfwords: twice byte len */
 229
 230        pseudo_sum = add_csum_hword((void *) &ip6h->saddr, num_u16);
 231        pseudo_sum += htons(ip6h->nexthdr);
 232        pseudo_sum += ip6h->payload_len;
 233        return build_ip_csum((void *) udph, num_words, pseudo_sum);
 234}
 235
 236static void build_udp_header(void *header, int payload_len,
 237                             uint16_t dport, int family)
 238{
 239        struct udphdr *udph = header;
 240        int len = sizeof(*udph) + payload_len;
 241
 242        udph->source = htons(cfg_src_port);
 243        udph->dest = htons(dport);
 244        udph->len = htons(len);
 245        udph->check = 0;
 246        if (family == AF_INET)
 247                udph->check = build_udp_v4_csum(header - sizeof(struct iphdr),
 248                                                udph, len >> 1);
 249        else
 250                udph->check = build_udp_v6_csum(header - sizeof(struct ipv6hdr),
 251                                                udph, len >> 1);
 252}
 253
 254static void build_gue_header(void *header, uint8_t proto)
 255{
 256        struct guehdr *gueh = header;
 257
 258        gueh->proto_ctype = proto;
 259}
 260
 261static void build_gre_header(void *header, uint16_t proto)
 262{
 263        struct grehdr *greh = header;
 264
 265        greh->protocol = htons(proto);
 266}
 267
 268static int l3_length(int family)
 269{
 270        if (family == AF_INET)
 271                return sizeof(struct iphdr);
 272        else
 273                return sizeof(struct ipv6hdr);
 274}
 275
 276static int build_packet(void)
 277{
 278        int ol3_len = 0, ol4_len = 0, il3_len = 0, il4_len = 0;
 279        int el3_len = 0;
 280
 281        if (cfg_l3_extra)
 282                el3_len = l3_length(cfg_l3_extra);
 283
 284        /* calculate header offsets */
 285        if (cfg_encap_proto) {
 286                ol3_len = l3_length(cfg_l3_outer);
 287
 288                if (cfg_encap_proto == IPPROTO_GRE)
 289                        ol4_len = sizeof(struct grehdr);
 290                else if (cfg_encap_proto == IPPROTO_UDP)
 291                        ol4_len = sizeof(struct udphdr) + sizeof(struct guehdr);
 292        }
 293
 294        il3_len = l3_length(cfg_l3_inner);
 295        il4_len = sizeof(struct udphdr);
 296
 297        if (el3_len + ol3_len + ol4_len + il3_len + il4_len + cfg_payload_len >=
 298            sizeof(buf))
 299                error(1, 0, "packet too large\n");
 300
 301        /*
 302         * Fill packet from inside out, to calculate correct checksums.
 303         * But create ip before udp headers, as udp uses ip for pseudo-sum.
 304         */
 305        memset(buf + el3_len + ol3_len + ol4_len + il3_len + il4_len,
 306               cfg_payload_char, cfg_payload_len);
 307
 308        /* add zero byte for udp csum padding */
 309        buf[el3_len + ol3_len + ol4_len + il3_len + il4_len + cfg_payload_len] = 0;
 310
 311        switch (cfg_l3_inner) {
 312        case PF_INET:
 313                build_ipv4_header(buf + el3_len + ol3_len + ol4_len,
 314                                  IPPROTO_UDP,
 315                                  in_saddr4.sin_addr.s_addr,
 316                                  in_daddr4.sin_addr.s_addr,
 317                                  il4_len + cfg_payload_len,
 318                                  cfg_dsfield_inner);
 319                break;
 320        case PF_INET6:
 321                build_ipv6_header(buf + el3_len + ol3_len + ol4_len,
 322                                  IPPROTO_UDP,
 323                                  &in_saddr6, &in_daddr6,
 324                                  il4_len + cfg_payload_len,
 325                                  cfg_dsfield_inner);
 326                break;
 327        }
 328
 329        build_udp_header(buf + el3_len + ol3_len + ol4_len + il3_len,
 330                         cfg_payload_len, CFG_PORT_INNER, cfg_l3_inner);
 331
 332        if (!cfg_encap_proto)
 333                return il3_len + il4_len + cfg_payload_len;
 334
 335        switch (cfg_l3_outer) {
 336        case PF_INET:
 337                build_ipv4_header(buf + el3_len, cfg_encap_proto,
 338                                  out_saddr4.sin_addr.s_addr,
 339                                  out_daddr4.sin_addr.s_addr,
 340                                  ol4_len + il3_len + il4_len + cfg_payload_len,
 341                                  cfg_dsfield_outer);
 342                break;
 343        case PF_INET6:
 344                build_ipv6_header(buf + el3_len, cfg_encap_proto,
 345                                  &out_saddr6, &out_daddr6,
 346                                  ol4_len + il3_len + il4_len + cfg_payload_len,
 347                                  cfg_dsfield_outer);
 348                break;
 349        }
 350
 351        switch (cfg_encap_proto) {
 352        case IPPROTO_UDP:
 353                build_gue_header(buf + el3_len + ol3_len + ol4_len -
 354                                 sizeof(struct guehdr),
 355                                 cfg_l3_inner == PF_INET ? IPPROTO_IPIP
 356                                                         : IPPROTO_IPV6);
 357                build_udp_header(buf + el3_len + ol3_len,
 358                                 sizeof(struct guehdr) + il3_len + il4_len +
 359                                 cfg_payload_len,
 360                                 cfg_port_gue, cfg_l3_outer);
 361                break;
 362        case IPPROTO_GRE:
 363                build_gre_header(buf + el3_len + ol3_len,
 364                                 cfg_l3_inner == PF_INET ? ETH_P_IP
 365                                                         : ETH_P_IPV6);
 366                break;
 367        }
 368
 369        switch (cfg_l3_extra) {
 370        case PF_INET:
 371                build_ipv4_header(buf,
 372                                  cfg_l3_outer == PF_INET ? IPPROTO_IPIP
 373                                                          : IPPROTO_IPV6,
 374                                  extra_saddr4.sin_addr.s_addr,
 375                                  extra_daddr4.sin_addr.s_addr,
 376                                  ol3_len + ol4_len + il3_len + il4_len +
 377                                  cfg_payload_len, 0);
 378                break;
 379        case PF_INET6:
 380                build_ipv6_header(buf,
 381                                  cfg_l3_outer == PF_INET ? IPPROTO_IPIP
 382                                                          : IPPROTO_IPV6,
 383                                  &extra_saddr6, &extra_daddr6,
 384                                  ol3_len + ol4_len + il3_len + il4_len +
 385                                  cfg_payload_len, 0);
 386                break;
 387        }
 388
 389        return el3_len + ol3_len + ol4_len + il3_len + il4_len +
 390               cfg_payload_len;
 391}
 392
 393/* sender transmits encapsulated over RAW or unencap'd over UDP */
 394static int setup_tx(void)
 395{
 396        int family, fd, ret;
 397
 398        if (cfg_l3_extra)
 399                family = cfg_l3_extra;
 400        else if (cfg_l3_outer)
 401                family = cfg_l3_outer;
 402        else
 403                family = cfg_l3_inner;
 404
 405        fd = socket(family, SOCK_RAW, IPPROTO_RAW);
 406        if (fd == -1)
 407                error(1, errno, "socket tx");
 408
 409        if (cfg_l3_extra) {
 410                if (cfg_l3_extra == PF_INET)
 411                        ret = connect(fd, (void *) &extra_daddr4,
 412                                      sizeof(extra_daddr4));
 413                else
 414                        ret = connect(fd, (void *) &extra_daddr6,
 415                                      sizeof(extra_daddr6));
 416                if (ret)
 417                        error(1, errno, "connect tx");
 418        } else if (cfg_l3_outer) {
 419                /* connect to destination if not encapsulated */
 420                if (cfg_l3_outer == PF_INET)
 421                        ret = connect(fd, (void *) &out_daddr4,
 422                                      sizeof(out_daddr4));
 423                else
 424                        ret = connect(fd, (void *) &out_daddr6,
 425                                      sizeof(out_daddr6));
 426                if (ret)
 427                        error(1, errno, "connect tx");
 428        } else {
 429                /* otherwise using loopback */
 430                if (cfg_l3_inner == PF_INET)
 431                        ret = connect(fd, (void *) &in_daddr4,
 432                                      sizeof(in_daddr4));
 433                else
 434                        ret = connect(fd, (void *) &in_daddr6,
 435                                      sizeof(in_daddr6));
 436                if (ret)
 437                        error(1, errno, "connect tx");
 438        }
 439
 440        return fd;
 441}
 442
 443/* receiver reads unencapsulated UDP */
 444static int setup_rx(void)
 445{
 446        int fd, ret;
 447
 448        fd = socket(cfg_l3_inner, SOCK_DGRAM, 0);
 449        if (fd == -1)
 450                error(1, errno, "socket rx");
 451
 452        if (cfg_l3_inner == PF_INET)
 453                ret = bind(fd, (void *) &in_daddr4, sizeof(in_daddr4));
 454        else
 455                ret = bind(fd, (void *) &in_daddr6, sizeof(in_daddr6));
 456        if (ret)
 457                error(1, errno, "bind rx");
 458
 459        return fd;
 460}
 461
 462static int do_tx(int fd, const char *pkt, int len)
 463{
 464        int ret;
 465
 466        ret = write(fd, pkt, len);
 467        if (ret == -1)
 468                error(1, errno, "send");
 469        if (ret != len)
 470                error(1, errno, "send: len (%d < %d)\n", ret, len);
 471
 472        return 1;
 473}
 474
 475static int do_poll(int fd, short events, int timeout)
 476{
 477        struct pollfd pfd;
 478        int ret;
 479
 480        pfd.fd = fd;
 481        pfd.events = events;
 482
 483        ret = poll(&pfd, 1, timeout);
 484        if (ret == -1)
 485                error(1, errno, "poll");
 486        if (ret && !(pfd.revents & POLLIN))
 487                error(1, errno, "poll: unexpected event 0x%x\n", pfd.revents);
 488
 489        return ret;
 490}
 491
 492static int do_rx(int fd)
 493{
 494        char rbuf;
 495        int ret, num = 0;
 496
 497        while (1) {
 498                ret = recv(fd, &rbuf, 1, MSG_DONTWAIT);
 499                if (ret == -1 && errno == EAGAIN)
 500                        break;
 501                if (ret == -1)
 502                        error(1, errno, "recv");
 503                if (rbuf != cfg_payload_char)
 504                        error(1, 0, "recv: payload mismatch");
 505                num++;
 506        }
 507
 508        return num;
 509}
 510
 511static int do_main(void)
 512{
 513        unsigned long tstop, treport, tcur;
 514        int fdt = -1, fdr = -1, len, tx = 0, rx = 0;
 515
 516        if (!cfg_only_tx)
 517                fdr = setup_rx();
 518        if (!cfg_only_rx)
 519                fdt = setup_tx();
 520
 521        len = build_packet();
 522
 523        tcur = util_gettime();
 524        treport = tcur + 1000;
 525        tstop = tcur + (cfg_num_secs * 1000);
 526
 527        while (1) {
 528                if (!cfg_only_rx)
 529                        tx += do_tx(fdt, buf, len);
 530
 531                if (!cfg_only_tx)
 532                        rx += do_rx(fdr);
 533
 534                if (cfg_num_secs) {
 535                        tcur = util_gettime();
 536                        if (tcur >= tstop)
 537                                break;
 538                        if (tcur >= treport) {
 539                                fprintf(stderr, "pkts: tx=%u rx=%u\n", tx, rx);
 540                                tx = 0;
 541                                rx = 0;
 542                                treport = tcur + 1000;
 543                        }
 544                } else {
 545                        if (tx == cfg_num_pkt)
 546                                break;
 547                }
 548        }
 549
 550        /* read straggler packets, if any */
 551        if (rx < tx) {
 552                tstop = util_gettime() + 100;
 553                while (rx < tx) {
 554                        tcur = util_gettime();
 555                        if (tcur >= tstop)
 556                                break;
 557
 558                        do_poll(fdr, POLLIN, tstop - tcur);
 559                        rx += do_rx(fdr);
 560                }
 561        }
 562
 563        fprintf(stderr, "pkts: tx=%u rx=%u\n", tx, rx);
 564
 565        if (fdr != -1 && close(fdr))
 566                error(1, errno, "close rx");
 567        if (fdt != -1 && close(fdt))
 568                error(1, errno, "close tx");
 569
 570        /*
 571         * success (== 0) only if received all packets
 572         * unless failure is expected, in which case none must arrive.
 573         */
 574        if (cfg_expect_failure)
 575                return rx != 0;
 576        else
 577                return rx != tx;
 578}
 579
 580
 581static void __attribute__((noreturn)) usage(const char *filepath)
 582{
 583        fprintf(stderr, "Usage: %s [-e gre|gue|bare|none] [-i 4|6] [-l len] "
 584                        "[-O 4|6] [-o 4|6] [-n num] [-t secs] [-R] [-T] "
 585                        "[-s <osrc> [-d <odst>] [-S <isrc>] [-D <idst>] "
 586                        "[-x <otos>] [-X <itos>] [-f <isport>] [-F]\n",
 587                filepath);
 588        exit(1);
 589}
 590
 591static void parse_addr(int family, void *addr, const char *optarg)
 592{
 593        int ret;
 594
 595        ret = inet_pton(family, optarg, addr);
 596        if (ret == -1)
 597                error(1, errno, "inet_pton");
 598        if (ret == 0)
 599                error(1, 0, "inet_pton: bad string");
 600}
 601
 602static void parse_addr4(struct sockaddr_in *addr, const char *optarg)
 603{
 604        parse_addr(AF_INET, &addr->sin_addr, optarg);
 605}
 606
 607static void parse_addr6(struct sockaddr_in6 *addr, const char *optarg)
 608{
 609        parse_addr(AF_INET6, &addr->sin6_addr, optarg);
 610}
 611
 612static int parse_protocol_family(const char *filepath, const char *optarg)
 613{
 614        if (!strcmp(optarg, "4"))
 615                return PF_INET;
 616        if (!strcmp(optarg, "6"))
 617                return PF_INET6;
 618
 619        usage(filepath);
 620}
 621
 622static void parse_opts(int argc, char **argv)
 623{
 624        int c;
 625
 626        while ((c = getopt(argc, argv, "d:D:e:f:Fhi:l:n:o:O:Rs:S:t:Tx:X:")) != -1) {
 627                switch (c) {
 628                case 'd':
 629                        if (cfg_l3_outer == AF_UNSPEC)
 630                                error(1, 0, "-d must be preceded by -o");
 631                        if (cfg_l3_outer == AF_INET)
 632                                parse_addr4(&out_daddr4, optarg);
 633                        else
 634                                parse_addr6(&out_daddr6, optarg);
 635                        break;
 636                case 'D':
 637                        if (cfg_l3_inner == AF_UNSPEC)
 638                                error(1, 0, "-D must be preceded by -i");
 639                        if (cfg_l3_inner == AF_INET)
 640                                parse_addr4(&in_daddr4, optarg);
 641                        else
 642                                parse_addr6(&in_daddr6, optarg);
 643                        break;
 644                case 'e':
 645                        if (!strcmp(optarg, "gre"))
 646                                cfg_encap_proto = IPPROTO_GRE;
 647                        else if (!strcmp(optarg, "gue"))
 648                                cfg_encap_proto = IPPROTO_UDP;
 649                        else if (!strcmp(optarg, "bare"))
 650                                cfg_encap_proto = IPPROTO_IPIP;
 651                        else if (!strcmp(optarg, "none"))
 652                                cfg_encap_proto = IPPROTO_IP;   /* == 0 */
 653                        else
 654                                usage(argv[0]);
 655                        break;
 656                case 'f':
 657                        cfg_src_port = strtol(optarg, NULL, 0);
 658                        break;
 659                case 'F':
 660                        cfg_expect_failure = true;
 661                        break;
 662                case 'h':
 663                        usage(argv[0]);
 664                        break;
 665                case 'i':
 666                        if (!strcmp(optarg, "4"))
 667                                cfg_l3_inner = PF_INET;
 668                        else if (!strcmp(optarg, "6"))
 669                                cfg_l3_inner = PF_INET6;
 670                        else
 671                                usage(argv[0]);
 672                        break;
 673                case 'l':
 674                        cfg_payload_len = strtol(optarg, NULL, 0);
 675                        break;
 676                case 'n':
 677                        cfg_num_pkt = strtol(optarg, NULL, 0);
 678                        break;
 679                case 'o':
 680                        cfg_l3_outer = parse_protocol_family(argv[0], optarg);
 681                        break;
 682                case 'O':
 683                        cfg_l3_extra = parse_protocol_family(argv[0], optarg);
 684                        break;
 685                case 'R':
 686                        cfg_only_rx = true;
 687                        break;
 688                case 's':
 689                        if (cfg_l3_outer == AF_INET)
 690                                parse_addr4(&out_saddr4, optarg);
 691                        else
 692                                parse_addr6(&out_saddr6, optarg);
 693                        break;
 694                case 'S':
 695                        if (cfg_l3_inner == AF_INET)
 696                                parse_addr4(&in_saddr4, optarg);
 697                        else
 698                                parse_addr6(&in_saddr6, optarg);
 699                        break;
 700                case 't':
 701                        cfg_num_secs = strtol(optarg, NULL, 0);
 702                        break;
 703                case 'T':
 704                        cfg_only_tx = true;
 705                        break;
 706                case 'x':
 707                        cfg_dsfield_outer = strtol(optarg, NULL, 0);
 708                        break;
 709                case 'X':
 710                        cfg_dsfield_inner = strtol(optarg, NULL, 0);
 711                        break;
 712                }
 713        }
 714
 715        if (cfg_only_rx && cfg_only_tx)
 716                error(1, 0, "options: cannot combine rx-only and tx-only");
 717
 718        if (cfg_encap_proto && cfg_l3_outer == AF_UNSPEC)
 719                error(1, 0, "options: must specify outer with encap");
 720        else if ((!cfg_encap_proto) && cfg_l3_outer != AF_UNSPEC)
 721                error(1, 0, "options: cannot combine no-encap and outer");
 722        else if ((!cfg_encap_proto) && cfg_l3_extra != AF_UNSPEC)
 723                error(1, 0, "options: cannot combine no-encap and extra");
 724
 725        if (cfg_l3_inner == AF_UNSPEC)
 726                cfg_l3_inner = AF_INET6;
 727        if (cfg_l3_inner == AF_INET6 && cfg_encap_proto == IPPROTO_IPIP)
 728                cfg_encap_proto = IPPROTO_IPV6;
 729
 730        /* RFC 6040 4.2:
 731         *   on decap, if outer encountered congestion (CE == 0x3),
 732         *   but inner cannot encode ECN (NoECT == 0x0), then drop packet.
 733         */
 734        if (((cfg_dsfield_outer & 0x3) == 0x3) &&
 735            ((cfg_dsfield_inner & 0x3) == 0x0))
 736                cfg_expect_failure = true;
 737}
 738
 739static void print_opts(void)
 740{
 741        if (cfg_l3_inner == PF_INET6) {
 742                util_printaddr("inner.dest6", (void *) &in_daddr6);
 743                util_printaddr("inner.source6", (void *) &in_saddr6);
 744        } else {
 745                util_printaddr("inner.dest4", (void *) &in_daddr4);
 746                util_printaddr("inner.source4", (void *) &in_saddr4);
 747        }
 748
 749        if (!cfg_l3_outer)
 750                return;
 751
 752        fprintf(stderr, "encap proto:   %u\n", cfg_encap_proto);
 753
 754        if (cfg_l3_outer == PF_INET6) {
 755                util_printaddr("outer.dest6", (void *) &out_daddr6);
 756                util_printaddr("outer.source6", (void *) &out_saddr6);
 757        } else {
 758                util_printaddr("outer.dest4", (void *) &out_daddr4);
 759                util_printaddr("outer.source4", (void *) &out_saddr4);
 760        }
 761
 762        if (!cfg_l3_extra)
 763                return;
 764
 765        if (cfg_l3_outer == PF_INET6) {
 766                util_printaddr("extra.dest6", (void *) &extra_daddr6);
 767                util_printaddr("extra.source6", (void *) &extra_saddr6);
 768        } else {
 769                util_printaddr("extra.dest4", (void *) &extra_daddr4);
 770                util_printaddr("extra.source4", (void *) &extra_saddr4);
 771        }
 772
 773}
 774
 775int main(int argc, char **argv)
 776{
 777        parse_opts(argc, argv);
 778        print_opts();
 779        return do_main();
 780}
 781