cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

test_flow_dissector.c (18626B)


      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}