Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Inject packets with all sorts of encapsulation into the kernel.
0004  *
0005  * IPv4/IPv6    outer layer 3
0006  * GRE/GUE/BARE outer layer 4, where bare is IPIP/SIT/IPv4-in-IPv6/..
0007  * IPv4/IPv6    inner layer 3
0008  */
0009 
0010 #define _GNU_SOURCE
0011 
0012 #include <stddef.h>
0013 #include <arpa/inet.h>
0014 #include <asm/byteorder.h>
0015 #include <error.h>
0016 #include <errno.h>
0017 #include <linux/if_packet.h>
0018 #include <linux/if_ether.h>
0019 #include <linux/ipv6.h>
0020 #include <netinet/ip.h>
0021 #include <netinet/in.h>
0022 #include <netinet/udp.h>
0023 #include <poll.h>
0024 #include <stdbool.h>
0025 #include <stdlib.h>
0026 #include <stdio.h>
0027 #include <string.h>
0028 #include <sys/ioctl.h>
0029 #include <sys/socket.h>
0030 #include <sys/stat.h>
0031 #include <sys/time.h>
0032 #include <sys/types.h>
0033 #include <unistd.h>
0034 
0035 #define CFG_PORT_INNER  8000
0036 
0037 /* Add some protocol definitions that do not exist in userspace */
0038 
0039 struct grehdr {
0040     uint16_t unused;
0041     uint16_t protocol;
0042 } __attribute__((packed));
0043 
0044 struct guehdr {
0045     union {
0046         struct {
0047 #if defined(__LITTLE_ENDIAN_BITFIELD)
0048             __u8    hlen:5,
0049                 control:1,
0050                 version:2;
0051 #elif defined (__BIG_ENDIAN_BITFIELD)
0052             __u8    version:2,
0053                 control:1,
0054                 hlen:5;
0055 #else
0056 #error  "Please fix <asm/byteorder.h>"
0057 #endif
0058             __u8    proto_ctype;
0059             __be16  flags;
0060         };
0061         __be32  word;
0062     };
0063 };
0064 
0065 static uint8_t  cfg_dsfield_inner;
0066 static uint8_t  cfg_dsfield_outer;
0067 static uint8_t  cfg_encap_proto;
0068 static bool cfg_expect_failure = false;
0069 static int  cfg_l3_extra = AF_UNSPEC;   /* optional SIT prefix */
0070 static int  cfg_l3_inner = AF_UNSPEC;
0071 static int  cfg_l3_outer = AF_UNSPEC;
0072 static int  cfg_num_pkt = 10;
0073 static int  cfg_num_secs = 0;
0074 static char cfg_payload_char = 'a';
0075 static int  cfg_payload_len = 100;
0076 static int  cfg_port_gue = 6080;
0077 static bool cfg_only_rx;
0078 static bool cfg_only_tx;
0079 static int  cfg_src_port = 9;
0080 
0081 static char buf[ETH_DATA_LEN];
0082 
0083 #define INIT_ADDR4(name, addr4, port)               \
0084     static struct sockaddr_in name = {          \
0085         .sin_family = AF_INET,              \
0086         .sin_port = __constant_htons(port),     \
0087         .sin_addr.s_addr = __constant_htonl(addr4), \
0088     };
0089 
0090 #define INIT_ADDR6(name, addr6, port)               \
0091     static struct sockaddr_in6 name = {         \
0092         .sin6_family = AF_INET6,            \
0093         .sin6_port = __constant_htons(port),        \
0094         .sin6_addr = addr6,             \
0095     };
0096 
0097 INIT_ADDR4(in_daddr4, INADDR_LOOPBACK, CFG_PORT_INNER)
0098 INIT_ADDR4(in_saddr4, INADDR_LOOPBACK + 2, 0)
0099 INIT_ADDR4(out_daddr4, INADDR_LOOPBACK, 0)
0100 INIT_ADDR4(out_saddr4, INADDR_LOOPBACK + 1, 0)
0101 INIT_ADDR4(extra_daddr4, INADDR_LOOPBACK, 0)
0102 INIT_ADDR4(extra_saddr4, INADDR_LOOPBACK + 1, 0)
0103 
0104 INIT_ADDR6(in_daddr6, IN6ADDR_LOOPBACK_INIT, CFG_PORT_INNER)
0105 INIT_ADDR6(in_saddr6, IN6ADDR_LOOPBACK_INIT, 0)
0106 INIT_ADDR6(out_daddr6, IN6ADDR_LOOPBACK_INIT, 0)
0107 INIT_ADDR6(out_saddr6, IN6ADDR_LOOPBACK_INIT, 0)
0108 INIT_ADDR6(extra_daddr6, IN6ADDR_LOOPBACK_INIT, 0)
0109 INIT_ADDR6(extra_saddr6, IN6ADDR_LOOPBACK_INIT, 0)
0110 
0111 static unsigned long util_gettime(void)
0112 {
0113     struct timeval tv;
0114 
0115     gettimeofday(&tv, NULL);
0116     return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
0117 }
0118 
0119 static void util_printaddr(const char *msg, struct sockaddr *addr)
0120 {
0121     unsigned long off = 0;
0122     char nbuf[INET6_ADDRSTRLEN];
0123 
0124     switch (addr->sa_family) {
0125     case PF_INET:
0126         off = __builtin_offsetof(struct sockaddr_in, sin_addr);
0127         break;
0128     case PF_INET6:
0129         off = __builtin_offsetof(struct sockaddr_in6, sin6_addr);
0130         break;
0131     default:
0132         error(1, 0, "printaddr: unsupported family %u\n",
0133               addr->sa_family);
0134     }
0135 
0136     if (!inet_ntop(addr->sa_family, ((void *) addr) + off, nbuf,
0137                sizeof(nbuf)))
0138         error(1, errno, "inet_ntop");
0139 
0140     fprintf(stderr, "%s: %s\n", msg, nbuf);
0141 }
0142 
0143 static unsigned long add_csum_hword(const uint16_t *start, int num_u16)
0144 {
0145     unsigned long sum = 0;
0146     int i;
0147 
0148     for (i = 0; i < num_u16; i++)
0149         sum += start[i];
0150 
0151     return sum;
0152 }
0153 
0154 static uint16_t build_ip_csum(const uint16_t *start, int num_u16,
0155                   unsigned long sum)
0156 {
0157     sum += add_csum_hword(start, num_u16);
0158 
0159     while (sum >> 16)
0160         sum = (sum & 0xffff) + (sum >> 16);
0161 
0162     return ~sum;
0163 }
0164 
0165 static void build_ipv4_header(void *header, uint8_t proto,
0166                   uint32_t src, uint32_t dst,
0167                   int payload_len, uint8_t tos)
0168 {
0169     struct iphdr *iph = header;
0170 
0171     iph->ihl = 5;
0172     iph->version = 4;
0173     iph->tos = tos;
0174     iph->ttl = 8;
0175     iph->tot_len = htons(sizeof(*iph) + payload_len);
0176     iph->id = htons(1337);
0177     iph->protocol = proto;
0178     iph->saddr = src;
0179     iph->daddr = dst;
0180     iph->check = build_ip_csum((void *) iph, iph->ihl << 1, 0);
0181 }
0182 
0183 static void ipv6_set_dsfield(struct ipv6hdr *ip6h, uint8_t dsfield)
0184 {
0185     uint16_t val, *ptr = (uint16_t *)ip6h;
0186 
0187     val = ntohs(*ptr);
0188     val &= 0xF00F;
0189     val |= ((uint16_t) dsfield) << 4;
0190     *ptr = htons(val);
0191 }
0192 
0193 static void build_ipv6_header(void *header, uint8_t proto,
0194                   struct sockaddr_in6 *src,
0195                   struct sockaddr_in6 *dst,
0196                   int payload_len, uint8_t dsfield)
0197 {
0198     struct ipv6hdr *ip6h = header;
0199 
0200     ip6h->version = 6;
0201     ip6h->payload_len = htons(payload_len);
0202     ip6h->nexthdr = proto;
0203     ip6h->hop_limit = 8;
0204     ipv6_set_dsfield(ip6h, dsfield);
0205 
0206     memcpy(&ip6h->saddr, &src->sin6_addr, sizeof(ip6h->saddr));
0207     memcpy(&ip6h->daddr, &dst->sin6_addr, sizeof(ip6h->daddr));
0208 }
0209 
0210 static uint16_t build_udp_v4_csum(const struct iphdr *iph,
0211                   const struct udphdr *udph,
0212                   int num_words)
0213 {
0214     unsigned long pseudo_sum;
0215     int num_u16 = sizeof(iph->saddr);   /* halfwords: twice byte len */
0216 
0217     pseudo_sum = add_csum_hword((void *) &iph->saddr, num_u16);
0218     pseudo_sum += htons(IPPROTO_UDP);
0219     pseudo_sum += udph->len;
0220     return build_ip_csum((void *) udph, num_words, pseudo_sum);
0221 }
0222 
0223 static uint16_t build_udp_v6_csum(const struct ipv6hdr *ip6h,
0224                   const struct udphdr *udph,
0225                   int num_words)
0226 {
0227     unsigned long pseudo_sum;
0228     int num_u16 = sizeof(ip6h->saddr);  /* halfwords: twice byte len */
0229 
0230     pseudo_sum = add_csum_hword((void *) &ip6h->saddr, num_u16);
0231     pseudo_sum += htons(ip6h->nexthdr);
0232     pseudo_sum += ip6h->payload_len;
0233     return build_ip_csum((void *) udph, num_words, pseudo_sum);
0234 }
0235 
0236 static void build_udp_header(void *header, int payload_len,
0237                  uint16_t dport, int family)
0238 {
0239     struct udphdr *udph = header;
0240     int len = sizeof(*udph) + payload_len;
0241 
0242     udph->source = htons(cfg_src_port);
0243     udph->dest = htons(dport);
0244     udph->len = htons(len);
0245     udph->check = 0;
0246     if (family == AF_INET)
0247         udph->check = build_udp_v4_csum(header - sizeof(struct iphdr),
0248                         udph, len >> 1);
0249     else
0250         udph->check = build_udp_v6_csum(header - sizeof(struct ipv6hdr),
0251                         udph, len >> 1);
0252 }
0253 
0254 static void build_gue_header(void *header, uint8_t proto)
0255 {
0256     struct guehdr *gueh = header;
0257 
0258     gueh->proto_ctype = proto;
0259 }
0260 
0261 static void build_gre_header(void *header, uint16_t proto)
0262 {
0263     struct grehdr *greh = header;
0264 
0265     greh->protocol = htons(proto);
0266 }
0267 
0268 static int l3_length(int family)
0269 {
0270     if (family == AF_INET)
0271         return sizeof(struct iphdr);
0272     else
0273         return sizeof(struct ipv6hdr);
0274 }
0275 
0276 static int build_packet(void)
0277 {
0278     int ol3_len = 0, ol4_len = 0, il3_len = 0, il4_len = 0;
0279     int el3_len = 0;
0280 
0281     if (cfg_l3_extra)
0282         el3_len = l3_length(cfg_l3_extra);
0283 
0284     /* calculate header offsets */
0285     if (cfg_encap_proto) {
0286         ol3_len = l3_length(cfg_l3_outer);
0287 
0288         if (cfg_encap_proto == IPPROTO_GRE)
0289             ol4_len = sizeof(struct grehdr);
0290         else if (cfg_encap_proto == IPPROTO_UDP)
0291             ol4_len = sizeof(struct udphdr) + sizeof(struct guehdr);
0292     }
0293 
0294     il3_len = l3_length(cfg_l3_inner);
0295     il4_len = sizeof(struct udphdr);
0296 
0297     if (el3_len + ol3_len + ol4_len + il3_len + il4_len + cfg_payload_len >=
0298         sizeof(buf))
0299         error(1, 0, "packet too large\n");
0300 
0301     /*
0302      * Fill packet from inside out, to calculate correct checksums.
0303      * But create ip before udp headers, as udp uses ip for pseudo-sum.
0304      */
0305     memset(buf + el3_len + ol3_len + ol4_len + il3_len + il4_len,
0306            cfg_payload_char, cfg_payload_len);
0307 
0308     /* add zero byte for udp csum padding */
0309     buf[el3_len + ol3_len + ol4_len + il3_len + il4_len + cfg_payload_len] = 0;
0310 
0311     switch (cfg_l3_inner) {
0312     case PF_INET:
0313         build_ipv4_header(buf + el3_len + ol3_len + ol4_len,
0314                   IPPROTO_UDP,
0315                   in_saddr4.sin_addr.s_addr,
0316                   in_daddr4.sin_addr.s_addr,
0317                   il4_len + cfg_payload_len,
0318                   cfg_dsfield_inner);
0319         break;
0320     case PF_INET6:
0321         build_ipv6_header(buf + el3_len + ol3_len + ol4_len,
0322                   IPPROTO_UDP,
0323                   &in_saddr6, &in_daddr6,
0324                   il4_len + cfg_payload_len,
0325                   cfg_dsfield_inner);
0326         break;
0327     }
0328 
0329     build_udp_header(buf + el3_len + ol3_len + ol4_len + il3_len,
0330              cfg_payload_len, CFG_PORT_INNER, cfg_l3_inner);
0331 
0332     if (!cfg_encap_proto)
0333         return il3_len + il4_len + cfg_payload_len;
0334 
0335     switch (cfg_l3_outer) {
0336     case PF_INET:
0337         build_ipv4_header(buf + el3_len, cfg_encap_proto,
0338                   out_saddr4.sin_addr.s_addr,
0339                   out_daddr4.sin_addr.s_addr,
0340                   ol4_len + il3_len + il4_len + cfg_payload_len,
0341                   cfg_dsfield_outer);
0342         break;
0343     case PF_INET6:
0344         build_ipv6_header(buf + el3_len, cfg_encap_proto,
0345                   &out_saddr6, &out_daddr6,
0346                   ol4_len + il3_len + il4_len + cfg_payload_len,
0347                   cfg_dsfield_outer);
0348         break;
0349     }
0350 
0351     switch (cfg_encap_proto) {
0352     case IPPROTO_UDP:
0353         build_gue_header(buf + el3_len + ol3_len + ol4_len -
0354                  sizeof(struct guehdr),
0355                  cfg_l3_inner == PF_INET ? IPPROTO_IPIP
0356                              : IPPROTO_IPV6);
0357         build_udp_header(buf + el3_len + ol3_len,
0358                  sizeof(struct guehdr) + il3_len + il4_len +
0359                  cfg_payload_len,
0360                  cfg_port_gue, cfg_l3_outer);
0361         break;
0362     case IPPROTO_GRE:
0363         build_gre_header(buf + el3_len + ol3_len,
0364                  cfg_l3_inner == PF_INET ? ETH_P_IP
0365                              : ETH_P_IPV6);
0366         break;
0367     }
0368 
0369     switch (cfg_l3_extra) {
0370     case PF_INET:
0371         build_ipv4_header(buf,
0372                   cfg_l3_outer == PF_INET ? IPPROTO_IPIP
0373                               : IPPROTO_IPV6,
0374                   extra_saddr4.sin_addr.s_addr,
0375                   extra_daddr4.sin_addr.s_addr,
0376                   ol3_len + ol4_len + il3_len + il4_len +
0377                   cfg_payload_len, 0);
0378         break;
0379     case PF_INET6:
0380         build_ipv6_header(buf,
0381                   cfg_l3_outer == PF_INET ? IPPROTO_IPIP
0382                               : IPPROTO_IPV6,
0383                   &extra_saddr6, &extra_daddr6,
0384                   ol3_len + ol4_len + il3_len + il4_len +
0385                   cfg_payload_len, 0);
0386         break;
0387     }
0388 
0389     return el3_len + ol3_len + ol4_len + il3_len + il4_len +
0390            cfg_payload_len;
0391 }
0392 
0393 /* sender transmits encapsulated over RAW or unencap'd over UDP */
0394 static int setup_tx(void)
0395 {
0396     int family, fd, ret;
0397 
0398     if (cfg_l3_extra)
0399         family = cfg_l3_extra;
0400     else if (cfg_l3_outer)
0401         family = cfg_l3_outer;
0402     else
0403         family = cfg_l3_inner;
0404 
0405     fd = socket(family, SOCK_RAW, IPPROTO_RAW);
0406     if (fd == -1)
0407         error(1, errno, "socket tx");
0408 
0409     if (cfg_l3_extra) {
0410         if (cfg_l3_extra == PF_INET)
0411             ret = connect(fd, (void *) &extra_daddr4,
0412                       sizeof(extra_daddr4));
0413         else
0414             ret = connect(fd, (void *) &extra_daddr6,
0415                       sizeof(extra_daddr6));
0416         if (ret)
0417             error(1, errno, "connect tx");
0418     } else if (cfg_l3_outer) {
0419         /* connect to destination if not encapsulated */
0420         if (cfg_l3_outer == PF_INET)
0421             ret = connect(fd, (void *) &out_daddr4,
0422                       sizeof(out_daddr4));
0423         else
0424             ret = connect(fd, (void *) &out_daddr6,
0425                       sizeof(out_daddr6));
0426         if (ret)
0427             error(1, errno, "connect tx");
0428     } else {
0429         /* otherwise using loopback */
0430         if (cfg_l3_inner == PF_INET)
0431             ret = connect(fd, (void *) &in_daddr4,
0432                       sizeof(in_daddr4));
0433         else
0434             ret = connect(fd, (void *) &in_daddr6,
0435                       sizeof(in_daddr6));
0436         if (ret)
0437             error(1, errno, "connect tx");
0438     }
0439 
0440     return fd;
0441 }
0442 
0443 /* receiver reads unencapsulated UDP */
0444 static int setup_rx(void)
0445 {
0446     int fd, ret;
0447 
0448     fd = socket(cfg_l3_inner, SOCK_DGRAM, 0);
0449     if (fd == -1)
0450         error(1, errno, "socket rx");
0451 
0452     if (cfg_l3_inner == PF_INET)
0453         ret = bind(fd, (void *) &in_daddr4, sizeof(in_daddr4));
0454     else
0455         ret = bind(fd, (void *) &in_daddr6, sizeof(in_daddr6));
0456     if (ret)
0457         error(1, errno, "bind rx");
0458 
0459     return fd;
0460 }
0461 
0462 static int do_tx(int fd, const char *pkt, int len)
0463 {
0464     int ret;
0465 
0466     ret = write(fd, pkt, len);
0467     if (ret == -1)
0468         error(1, errno, "send");
0469     if (ret != len)
0470         error(1, errno, "send: len (%d < %d)\n", ret, len);
0471 
0472     return 1;
0473 }
0474 
0475 static int do_poll(int fd, short events, int timeout)
0476 {
0477     struct pollfd pfd;
0478     int ret;
0479 
0480     pfd.fd = fd;
0481     pfd.events = events;
0482 
0483     ret = poll(&pfd, 1, timeout);
0484     if (ret == -1)
0485         error(1, errno, "poll");
0486     if (ret && !(pfd.revents & POLLIN))
0487         error(1, errno, "poll: unexpected event 0x%x\n", pfd.revents);
0488 
0489     return ret;
0490 }
0491 
0492 static int do_rx(int fd)
0493 {
0494     char rbuf;
0495     int ret, num = 0;
0496 
0497     while (1) {
0498         ret = recv(fd, &rbuf, 1, MSG_DONTWAIT);
0499         if (ret == -1 && errno == EAGAIN)
0500             break;
0501         if (ret == -1)
0502             error(1, errno, "recv");
0503         if (rbuf != cfg_payload_char)
0504             error(1, 0, "recv: payload mismatch");
0505         num++;
0506     }
0507 
0508     return num;
0509 }
0510 
0511 static int do_main(void)
0512 {
0513     unsigned long tstop, treport, tcur;
0514     int fdt = -1, fdr = -1, len, tx = 0, rx = 0;
0515 
0516     if (!cfg_only_tx)
0517         fdr = setup_rx();
0518     if (!cfg_only_rx)
0519         fdt = setup_tx();
0520 
0521     len = build_packet();
0522 
0523     tcur = util_gettime();
0524     treport = tcur + 1000;
0525     tstop = tcur + (cfg_num_secs * 1000);
0526 
0527     while (1) {
0528         if (!cfg_only_rx)
0529             tx += do_tx(fdt, buf, len);
0530 
0531         if (!cfg_only_tx)
0532             rx += do_rx(fdr);
0533 
0534         if (cfg_num_secs) {
0535             tcur = util_gettime();
0536             if (tcur >= tstop)
0537                 break;
0538             if (tcur >= treport) {
0539                 fprintf(stderr, "pkts: tx=%u rx=%u\n", tx, rx);
0540                 tx = 0;
0541                 rx = 0;
0542                 treport = tcur + 1000;
0543             }
0544         } else {
0545             if (tx == cfg_num_pkt)
0546                 break;
0547         }
0548     }
0549 
0550     /* read straggler packets, if any */
0551     if (rx < tx) {
0552         tstop = util_gettime() + 100;
0553         while (rx < tx) {
0554             tcur = util_gettime();
0555             if (tcur >= tstop)
0556                 break;
0557 
0558             do_poll(fdr, POLLIN, tstop - tcur);
0559             rx += do_rx(fdr);
0560         }
0561     }
0562 
0563     fprintf(stderr, "pkts: tx=%u rx=%u\n", tx, rx);
0564 
0565     if (fdr != -1 && close(fdr))
0566         error(1, errno, "close rx");
0567     if (fdt != -1 && close(fdt))
0568         error(1, errno, "close tx");
0569 
0570     /*
0571      * success (== 0) only if received all packets
0572      * unless failure is expected, in which case none must arrive.
0573      */
0574     if (cfg_expect_failure)
0575         return rx != 0;
0576     else
0577         return rx != tx;
0578 }
0579 
0580 
0581 static void __attribute__((noreturn)) usage(const char *filepath)
0582 {
0583     fprintf(stderr, "Usage: %s [-e gre|gue|bare|none] [-i 4|6] [-l len] "
0584             "[-O 4|6] [-o 4|6] [-n num] [-t secs] [-R] [-T] "
0585             "[-s <osrc> [-d <odst>] [-S <isrc>] [-D <idst>] "
0586             "[-x <otos>] [-X <itos>] [-f <isport>] [-F]\n",
0587         filepath);
0588     exit(1);
0589 }
0590 
0591 static void parse_addr(int family, void *addr, const char *optarg)
0592 {
0593     int ret;
0594 
0595     ret = inet_pton(family, optarg, addr);
0596     if (ret == -1)
0597         error(1, errno, "inet_pton");
0598     if (ret == 0)
0599         error(1, 0, "inet_pton: bad string");
0600 }
0601 
0602 static void parse_addr4(struct sockaddr_in *addr, const char *optarg)
0603 {
0604     parse_addr(AF_INET, &addr->sin_addr, optarg);
0605 }
0606 
0607 static void parse_addr6(struct sockaddr_in6 *addr, const char *optarg)
0608 {
0609     parse_addr(AF_INET6, &addr->sin6_addr, optarg);
0610 }
0611 
0612 static int parse_protocol_family(const char *filepath, const char *optarg)
0613 {
0614     if (!strcmp(optarg, "4"))
0615         return PF_INET;
0616     if (!strcmp(optarg, "6"))
0617         return PF_INET6;
0618 
0619     usage(filepath);
0620 }
0621 
0622 static void parse_opts(int argc, char **argv)
0623 {
0624     int c;
0625 
0626     while ((c = getopt(argc, argv, "d:D:e:f:Fhi:l:n:o:O:Rs:S:t:Tx:X:")) != -1) {
0627         switch (c) {
0628         case 'd':
0629             if (cfg_l3_outer == AF_UNSPEC)
0630                 error(1, 0, "-d must be preceded by -o");
0631             if (cfg_l3_outer == AF_INET)
0632                 parse_addr4(&out_daddr4, optarg);
0633             else
0634                 parse_addr6(&out_daddr6, optarg);
0635             break;
0636         case 'D':
0637             if (cfg_l3_inner == AF_UNSPEC)
0638                 error(1, 0, "-D must be preceded by -i");
0639             if (cfg_l3_inner == AF_INET)
0640                 parse_addr4(&in_daddr4, optarg);
0641             else
0642                 parse_addr6(&in_daddr6, optarg);
0643             break;
0644         case 'e':
0645             if (!strcmp(optarg, "gre"))
0646                 cfg_encap_proto = IPPROTO_GRE;
0647             else if (!strcmp(optarg, "gue"))
0648                 cfg_encap_proto = IPPROTO_UDP;
0649             else if (!strcmp(optarg, "bare"))
0650                 cfg_encap_proto = IPPROTO_IPIP;
0651             else if (!strcmp(optarg, "none"))
0652                 cfg_encap_proto = IPPROTO_IP;   /* == 0 */
0653             else
0654                 usage(argv[0]);
0655             break;
0656         case 'f':
0657             cfg_src_port = strtol(optarg, NULL, 0);
0658             break;
0659         case 'F':
0660             cfg_expect_failure = true;
0661             break;
0662         case 'h':
0663             usage(argv[0]);
0664             break;
0665         case 'i':
0666             if (!strcmp(optarg, "4"))
0667                 cfg_l3_inner = PF_INET;
0668             else if (!strcmp(optarg, "6"))
0669                 cfg_l3_inner = PF_INET6;
0670             else
0671                 usage(argv[0]);
0672             break;
0673         case 'l':
0674             cfg_payload_len = strtol(optarg, NULL, 0);
0675             break;
0676         case 'n':
0677             cfg_num_pkt = strtol(optarg, NULL, 0);
0678             break;
0679         case 'o':
0680             cfg_l3_outer = parse_protocol_family(argv[0], optarg);
0681             break;
0682         case 'O':
0683             cfg_l3_extra = parse_protocol_family(argv[0], optarg);
0684             break;
0685         case 'R':
0686             cfg_only_rx = true;
0687             break;
0688         case 's':
0689             if (cfg_l3_outer == AF_INET)
0690                 parse_addr4(&out_saddr4, optarg);
0691             else
0692                 parse_addr6(&out_saddr6, optarg);
0693             break;
0694         case 'S':
0695             if (cfg_l3_inner == AF_INET)
0696                 parse_addr4(&in_saddr4, optarg);
0697             else
0698                 parse_addr6(&in_saddr6, optarg);
0699             break;
0700         case 't':
0701             cfg_num_secs = strtol(optarg, NULL, 0);
0702             break;
0703         case 'T':
0704             cfg_only_tx = true;
0705             break;
0706         case 'x':
0707             cfg_dsfield_outer = strtol(optarg, NULL, 0);
0708             break;
0709         case 'X':
0710             cfg_dsfield_inner = strtol(optarg, NULL, 0);
0711             break;
0712         }
0713     }
0714 
0715     if (cfg_only_rx && cfg_only_tx)
0716         error(1, 0, "options: cannot combine rx-only and tx-only");
0717 
0718     if (cfg_encap_proto && cfg_l3_outer == AF_UNSPEC)
0719         error(1, 0, "options: must specify outer with encap");
0720     else if ((!cfg_encap_proto) && cfg_l3_outer != AF_UNSPEC)
0721         error(1, 0, "options: cannot combine no-encap and outer");
0722     else if ((!cfg_encap_proto) && cfg_l3_extra != AF_UNSPEC)
0723         error(1, 0, "options: cannot combine no-encap and extra");
0724 
0725     if (cfg_l3_inner == AF_UNSPEC)
0726         cfg_l3_inner = AF_INET6;
0727     if (cfg_l3_inner == AF_INET6 && cfg_encap_proto == IPPROTO_IPIP)
0728         cfg_encap_proto = IPPROTO_IPV6;
0729 
0730     /* RFC 6040 4.2:
0731      *   on decap, if outer encountered congestion (CE == 0x3),
0732      *   but inner cannot encode ECN (NoECT == 0x0), then drop packet.
0733      */
0734     if (((cfg_dsfield_outer & 0x3) == 0x3) &&
0735         ((cfg_dsfield_inner & 0x3) == 0x0))
0736         cfg_expect_failure = true;
0737 }
0738 
0739 static void print_opts(void)
0740 {
0741     if (cfg_l3_inner == PF_INET6) {
0742         util_printaddr("inner.dest6", (void *) &in_daddr6);
0743         util_printaddr("inner.source6", (void *) &in_saddr6);
0744     } else {
0745         util_printaddr("inner.dest4", (void *) &in_daddr4);
0746         util_printaddr("inner.source4", (void *) &in_saddr4);
0747     }
0748 
0749     if (!cfg_l3_outer)
0750         return;
0751 
0752     fprintf(stderr, "encap proto:   %u\n", cfg_encap_proto);
0753 
0754     if (cfg_l3_outer == PF_INET6) {
0755         util_printaddr("outer.dest6", (void *) &out_daddr6);
0756         util_printaddr("outer.source6", (void *) &out_saddr6);
0757     } else {
0758         util_printaddr("outer.dest4", (void *) &out_daddr4);
0759         util_printaddr("outer.source4", (void *) &out_saddr4);
0760     }
0761 
0762     if (!cfg_l3_extra)
0763         return;
0764 
0765     if (cfg_l3_outer == PF_INET6) {
0766         util_printaddr("extra.dest6", (void *) &extra_daddr6);
0767         util_printaddr("extra.source6", (void *) &extra_saddr6);
0768     } else {
0769         util_printaddr("extra.dest4", (void *) &extra_daddr4);
0770         util_printaddr("extra.source4", (void *) &extra_saddr4);
0771     }
0772 
0773 }
0774 
0775 int main(int argc, char **argv)
0776 {
0777     parse_opts(argc, argv);
0778     print_opts();
0779     return do_main();
0780 }