xref: /linux/tools/testing/selftests/bpf/test_flow_dissector.c (revision d0034a7a4ac7fae708146ac0059b9c47a1543f0d)
150b3ed57SPetar Penkov // SPDX-License-Identifier: GPL-2.0
250b3ed57SPetar Penkov /*
350b3ed57SPetar Penkov  * Inject packets with all sorts of encapsulation into the kernel.
450b3ed57SPetar Penkov  *
550b3ed57SPetar Penkov  * IPv4/IPv6	outer layer 3
650b3ed57SPetar Penkov  * GRE/GUE/BARE outer layer 4, where bare is IPIP/SIT/IPv4-in-IPv6/..
750b3ed57SPetar Penkov  * IPv4/IPv6    inner layer 3
850b3ed57SPetar Penkov  */
950b3ed57SPetar Penkov 
1050b3ed57SPetar Penkov #define _GNU_SOURCE
1150b3ed57SPetar Penkov 
1250b3ed57SPetar Penkov #include <stddef.h>
1350b3ed57SPetar Penkov #include <arpa/inet.h>
1450b3ed57SPetar Penkov #include <asm/byteorder.h>
1550b3ed57SPetar Penkov #include <error.h>
1650b3ed57SPetar Penkov #include <errno.h>
1750b3ed57SPetar Penkov #include <linux/if_packet.h>
1850b3ed57SPetar Penkov #include <linux/if_ether.h>
1950b3ed57SPetar Penkov #include <linux/ipv6.h>
2050b3ed57SPetar Penkov #include <netinet/ip.h>
2150b3ed57SPetar Penkov #include <netinet/in.h>
2250b3ed57SPetar Penkov #include <netinet/udp.h>
2350b3ed57SPetar Penkov #include <poll.h>
2450b3ed57SPetar Penkov #include <stdbool.h>
2550b3ed57SPetar Penkov #include <stdlib.h>
2650b3ed57SPetar Penkov #include <stdio.h>
2750b3ed57SPetar Penkov #include <string.h>
2850b3ed57SPetar Penkov #include <sys/ioctl.h>
2950b3ed57SPetar Penkov #include <sys/socket.h>
3050b3ed57SPetar Penkov #include <sys/stat.h>
3150b3ed57SPetar Penkov #include <sys/time.h>
3250b3ed57SPetar Penkov #include <sys/types.h>
3350b3ed57SPetar Penkov #include <unistd.h>
3450b3ed57SPetar Penkov 
3550b3ed57SPetar Penkov #define CFG_PORT_INNER	8000
3650b3ed57SPetar Penkov 
3750b3ed57SPetar Penkov /* Add some protocol definitions that do not exist in userspace */
3850b3ed57SPetar Penkov 
3950b3ed57SPetar Penkov struct grehdr {
4050b3ed57SPetar Penkov 	uint16_t unused;
4150b3ed57SPetar Penkov 	uint16_t protocol;
4250b3ed57SPetar Penkov } __attribute__((packed));
4350b3ed57SPetar Penkov 
4450b3ed57SPetar Penkov struct guehdr {
4550b3ed57SPetar Penkov 	union {
4650b3ed57SPetar Penkov 		struct {
4750b3ed57SPetar Penkov #if defined(__LITTLE_ENDIAN_BITFIELD)
4850b3ed57SPetar Penkov 			__u8	hlen:5,
4950b3ed57SPetar Penkov 				control:1,
5050b3ed57SPetar Penkov 				version:2;
5150b3ed57SPetar Penkov #elif defined (__BIG_ENDIAN_BITFIELD)
5250b3ed57SPetar Penkov 			__u8	version:2,
5350b3ed57SPetar Penkov 				control:1,
5450b3ed57SPetar Penkov 				hlen:5;
5550b3ed57SPetar Penkov #else
5650b3ed57SPetar Penkov #error  "Please fix <asm/byteorder.h>"
5750b3ed57SPetar Penkov #endif
5850b3ed57SPetar Penkov 			__u8	proto_ctype;
5950b3ed57SPetar Penkov 			__be16	flags;
6050b3ed57SPetar Penkov 		};
6150b3ed57SPetar Penkov 		__be32	word;
6250b3ed57SPetar Penkov 	};
6350b3ed57SPetar Penkov };
6450b3ed57SPetar Penkov 
6550b3ed57SPetar Penkov static uint8_t	cfg_dsfield_inner;
6650b3ed57SPetar Penkov static uint8_t	cfg_dsfield_outer;
6750b3ed57SPetar Penkov static uint8_t	cfg_encap_proto;
6850b3ed57SPetar Penkov static bool	cfg_expect_failure = false;
6950b3ed57SPetar Penkov static int	cfg_l3_extra = AF_UNSPEC;	/* optional SIT prefix */
7050b3ed57SPetar Penkov static int	cfg_l3_inner = AF_UNSPEC;
7150b3ed57SPetar Penkov static int	cfg_l3_outer = AF_UNSPEC;
7250b3ed57SPetar Penkov static int	cfg_num_pkt = 10;
7350b3ed57SPetar Penkov static int	cfg_num_secs = 0;
7450b3ed57SPetar Penkov static char	cfg_payload_char = 'a';
7550b3ed57SPetar Penkov static int	cfg_payload_len = 100;
7650b3ed57SPetar Penkov static int	cfg_port_gue = 6080;
7750b3ed57SPetar Penkov static bool	cfg_only_rx;
7850b3ed57SPetar Penkov static bool	cfg_only_tx;
7950b3ed57SPetar Penkov static int	cfg_src_port = 9;
8050b3ed57SPetar Penkov 
8150b3ed57SPetar Penkov static char	buf[ETH_DATA_LEN];
8250b3ed57SPetar Penkov 
8350b3ed57SPetar Penkov #define INIT_ADDR4(name, addr4, port)				\
8450b3ed57SPetar Penkov 	static struct sockaddr_in name = {			\
8550b3ed57SPetar Penkov 		.sin_family = AF_INET,				\
8650b3ed57SPetar Penkov 		.sin_port = __constant_htons(port),		\
8750b3ed57SPetar Penkov 		.sin_addr.s_addr = __constant_htonl(addr4),	\
8850b3ed57SPetar Penkov 	};
8950b3ed57SPetar Penkov 
9050b3ed57SPetar Penkov #define INIT_ADDR6(name, addr6, port)				\
9150b3ed57SPetar Penkov 	static struct sockaddr_in6 name = {			\
9250b3ed57SPetar Penkov 		.sin6_family = AF_INET6,			\
9350b3ed57SPetar Penkov 		.sin6_port = __constant_htons(port),		\
9450b3ed57SPetar Penkov 		.sin6_addr = addr6,				\
9550b3ed57SPetar Penkov 	};
9650b3ed57SPetar Penkov 
INIT_ADDR4(in_daddr4,INADDR_LOOPBACK,CFG_PORT_INNER)9750b3ed57SPetar Penkov INIT_ADDR4(in_daddr4, INADDR_LOOPBACK, CFG_PORT_INNER)
9850b3ed57SPetar Penkov INIT_ADDR4(in_saddr4, INADDR_LOOPBACK + 2, 0)
9950b3ed57SPetar Penkov INIT_ADDR4(out_daddr4, INADDR_LOOPBACK, 0)
10050b3ed57SPetar Penkov INIT_ADDR4(out_saddr4, INADDR_LOOPBACK + 1, 0)
10150b3ed57SPetar Penkov INIT_ADDR4(extra_daddr4, INADDR_LOOPBACK, 0)
10250b3ed57SPetar Penkov INIT_ADDR4(extra_saddr4, INADDR_LOOPBACK + 1, 0)
10350b3ed57SPetar Penkov 
10450b3ed57SPetar Penkov INIT_ADDR6(in_daddr6, IN6ADDR_LOOPBACK_INIT, CFG_PORT_INNER)
10550b3ed57SPetar Penkov INIT_ADDR6(in_saddr6, IN6ADDR_LOOPBACK_INIT, 0)
10650b3ed57SPetar Penkov INIT_ADDR6(out_daddr6, IN6ADDR_LOOPBACK_INIT, 0)
10750b3ed57SPetar Penkov INIT_ADDR6(out_saddr6, IN6ADDR_LOOPBACK_INIT, 0)
10850b3ed57SPetar Penkov INIT_ADDR6(extra_daddr6, IN6ADDR_LOOPBACK_INIT, 0)
10950b3ed57SPetar Penkov INIT_ADDR6(extra_saddr6, IN6ADDR_LOOPBACK_INIT, 0)
11050b3ed57SPetar Penkov 
11150b3ed57SPetar Penkov static unsigned long util_gettime(void)
11250b3ed57SPetar Penkov {
11350b3ed57SPetar Penkov 	struct timeval tv;
11450b3ed57SPetar Penkov 
11550b3ed57SPetar Penkov 	gettimeofday(&tv, NULL);
11650b3ed57SPetar Penkov 	return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
11750b3ed57SPetar Penkov }
11850b3ed57SPetar Penkov 
util_printaddr(const char * msg,struct sockaddr * addr)11950b3ed57SPetar Penkov static void util_printaddr(const char *msg, struct sockaddr *addr)
12050b3ed57SPetar Penkov {
12150b3ed57SPetar Penkov 	unsigned long off = 0;
12250b3ed57SPetar Penkov 	char nbuf[INET6_ADDRSTRLEN];
12350b3ed57SPetar Penkov 
12450b3ed57SPetar Penkov 	switch (addr->sa_family) {
12550b3ed57SPetar Penkov 	case PF_INET:
12650b3ed57SPetar Penkov 		off = __builtin_offsetof(struct sockaddr_in, sin_addr);
12750b3ed57SPetar Penkov 		break;
12850b3ed57SPetar Penkov 	case PF_INET6:
12950b3ed57SPetar Penkov 		off = __builtin_offsetof(struct sockaddr_in6, sin6_addr);
13050b3ed57SPetar Penkov 		break;
13150b3ed57SPetar Penkov 	default:
13250b3ed57SPetar Penkov 		error(1, 0, "printaddr: unsupported family %u\n",
13350b3ed57SPetar Penkov 		      addr->sa_family);
13450b3ed57SPetar Penkov 	}
13550b3ed57SPetar Penkov 
13650b3ed57SPetar Penkov 	if (!inet_ntop(addr->sa_family, ((void *) addr) + off, nbuf,
13750b3ed57SPetar Penkov 		       sizeof(nbuf)))
13850b3ed57SPetar Penkov 		error(1, errno, "inet_ntop");
13950b3ed57SPetar Penkov 
14050b3ed57SPetar Penkov 	fprintf(stderr, "%s: %s\n", msg, nbuf);
14150b3ed57SPetar Penkov }
14250b3ed57SPetar Penkov 
add_csum_hword(const uint16_t * start,int num_u16)14350b3ed57SPetar Penkov static unsigned long add_csum_hword(const uint16_t *start, int num_u16)
14450b3ed57SPetar Penkov {
14550b3ed57SPetar Penkov 	unsigned long sum = 0;
14650b3ed57SPetar Penkov 	int i;
14750b3ed57SPetar Penkov 
14850b3ed57SPetar Penkov 	for (i = 0; i < num_u16; i++)
14950b3ed57SPetar Penkov 		sum += start[i];
15050b3ed57SPetar Penkov 
15150b3ed57SPetar Penkov 	return sum;
15250b3ed57SPetar Penkov }
15350b3ed57SPetar Penkov 
build_ip_csum(const uint16_t * start,int num_u16,unsigned long sum)15450b3ed57SPetar Penkov static uint16_t build_ip_csum(const uint16_t *start, int num_u16,
15550b3ed57SPetar Penkov 			      unsigned long sum)
15650b3ed57SPetar Penkov {
15750b3ed57SPetar Penkov 	sum += add_csum_hword(start, num_u16);
15850b3ed57SPetar Penkov 
15950b3ed57SPetar Penkov 	while (sum >> 16)
16050b3ed57SPetar Penkov 		sum = (sum & 0xffff) + (sum >> 16);
16150b3ed57SPetar Penkov 
16250b3ed57SPetar Penkov 	return ~sum;
16350b3ed57SPetar Penkov }
16450b3ed57SPetar Penkov 
build_ipv4_header(void * header,uint8_t proto,uint32_t src,uint32_t dst,int payload_len,uint8_t tos)16550b3ed57SPetar Penkov static void build_ipv4_header(void *header, uint8_t proto,
16650b3ed57SPetar Penkov 			      uint32_t src, uint32_t dst,
16750b3ed57SPetar Penkov 			      int payload_len, uint8_t tos)
16850b3ed57SPetar Penkov {
16950b3ed57SPetar Penkov 	struct iphdr *iph = header;
17050b3ed57SPetar Penkov 
17150b3ed57SPetar Penkov 	iph->ihl = 5;
17250b3ed57SPetar Penkov 	iph->version = 4;
17350b3ed57SPetar Penkov 	iph->tos = tos;
17450b3ed57SPetar Penkov 	iph->ttl = 8;
17550b3ed57SPetar Penkov 	iph->tot_len = htons(sizeof(*iph) + payload_len);
17650b3ed57SPetar Penkov 	iph->id = htons(1337);
17750b3ed57SPetar Penkov 	iph->protocol = proto;
17850b3ed57SPetar Penkov 	iph->saddr = src;
17950b3ed57SPetar Penkov 	iph->daddr = dst;
18050b3ed57SPetar Penkov 	iph->check = build_ip_csum((void *) iph, iph->ihl << 1, 0);
18150b3ed57SPetar Penkov }
18250b3ed57SPetar Penkov 
ipv6_set_dsfield(struct ipv6hdr * ip6h,uint8_t dsfield)18350b3ed57SPetar Penkov static void ipv6_set_dsfield(struct ipv6hdr *ip6h, uint8_t dsfield)
18450b3ed57SPetar Penkov {
18550b3ed57SPetar Penkov 	uint16_t val, *ptr = (uint16_t *)ip6h;
18650b3ed57SPetar Penkov 
18750b3ed57SPetar Penkov 	val = ntohs(*ptr);
18850b3ed57SPetar Penkov 	val &= 0xF00F;
18950b3ed57SPetar Penkov 	val |= ((uint16_t) dsfield) << 4;
19050b3ed57SPetar Penkov 	*ptr = htons(val);
19150b3ed57SPetar Penkov }
19250b3ed57SPetar Penkov 
build_ipv6_header(void * header,uint8_t proto,struct sockaddr_in6 * src,struct sockaddr_in6 * dst,int payload_len,uint8_t dsfield)19350b3ed57SPetar Penkov static void build_ipv6_header(void *header, uint8_t proto,
19450b3ed57SPetar Penkov 			      struct sockaddr_in6 *src,
19550b3ed57SPetar Penkov 			      struct sockaddr_in6 *dst,
19650b3ed57SPetar Penkov 			      int payload_len, uint8_t dsfield)
19750b3ed57SPetar Penkov {
19850b3ed57SPetar Penkov 	struct ipv6hdr *ip6h = header;
19950b3ed57SPetar Penkov 
20050b3ed57SPetar Penkov 	ip6h->version = 6;
20150b3ed57SPetar Penkov 	ip6h->payload_len = htons(payload_len);
20250b3ed57SPetar Penkov 	ip6h->nexthdr = proto;
20350b3ed57SPetar Penkov 	ip6h->hop_limit = 8;
20450b3ed57SPetar Penkov 	ipv6_set_dsfield(ip6h, dsfield);
20550b3ed57SPetar Penkov 
20650b3ed57SPetar Penkov 	memcpy(&ip6h->saddr, &src->sin6_addr, sizeof(ip6h->saddr));
20750b3ed57SPetar Penkov 	memcpy(&ip6h->daddr, &dst->sin6_addr, sizeof(ip6h->daddr));
20850b3ed57SPetar Penkov }
20950b3ed57SPetar Penkov 
build_udp_v4_csum(const struct iphdr * iph,const struct udphdr * udph,int num_words)21050b3ed57SPetar Penkov static uint16_t build_udp_v4_csum(const struct iphdr *iph,
21150b3ed57SPetar Penkov 				  const struct udphdr *udph,
21250b3ed57SPetar Penkov 				  int num_words)
21350b3ed57SPetar Penkov {
21450b3ed57SPetar Penkov 	unsigned long pseudo_sum;
21550b3ed57SPetar Penkov 	int num_u16 = sizeof(iph->saddr);	/* halfwords: twice byte len */
21650b3ed57SPetar Penkov 
21750b3ed57SPetar Penkov 	pseudo_sum = add_csum_hword((void *) &iph->saddr, num_u16);
21850b3ed57SPetar Penkov 	pseudo_sum += htons(IPPROTO_UDP);
21950b3ed57SPetar Penkov 	pseudo_sum += udph->len;
22050b3ed57SPetar Penkov 	return build_ip_csum((void *) udph, num_words, pseudo_sum);
22150b3ed57SPetar Penkov }
22250b3ed57SPetar Penkov 
build_udp_v6_csum(const struct ipv6hdr * ip6h,const struct udphdr * udph,int num_words)22350b3ed57SPetar Penkov static uint16_t build_udp_v6_csum(const struct ipv6hdr *ip6h,
22450b3ed57SPetar Penkov 				  const struct udphdr *udph,
22550b3ed57SPetar Penkov 				  int num_words)
22650b3ed57SPetar Penkov {
22750b3ed57SPetar Penkov 	unsigned long pseudo_sum;
22850b3ed57SPetar Penkov 	int num_u16 = sizeof(ip6h->saddr);	/* halfwords: twice byte len */
22950b3ed57SPetar Penkov 
23050b3ed57SPetar Penkov 	pseudo_sum = add_csum_hword((void *) &ip6h->saddr, num_u16);
23150b3ed57SPetar Penkov 	pseudo_sum += htons(ip6h->nexthdr);
23250b3ed57SPetar Penkov 	pseudo_sum += ip6h->payload_len;
23350b3ed57SPetar Penkov 	return build_ip_csum((void *) udph, num_words, pseudo_sum);
23450b3ed57SPetar Penkov }
23550b3ed57SPetar Penkov 
build_udp_header(void * header,int payload_len,uint16_t dport,int family)23650b3ed57SPetar Penkov static void build_udp_header(void *header, int payload_len,
23750b3ed57SPetar Penkov 			     uint16_t dport, int family)
23850b3ed57SPetar Penkov {
23950b3ed57SPetar Penkov 	struct udphdr *udph = header;
24050b3ed57SPetar Penkov 	int len = sizeof(*udph) + payload_len;
24150b3ed57SPetar Penkov 
24250b3ed57SPetar Penkov 	udph->source = htons(cfg_src_port);
24350b3ed57SPetar Penkov 	udph->dest = htons(dport);
24450b3ed57SPetar Penkov 	udph->len = htons(len);
24550b3ed57SPetar Penkov 	udph->check = 0;
24650b3ed57SPetar Penkov 	if (family == AF_INET)
24750b3ed57SPetar Penkov 		udph->check = build_udp_v4_csum(header - sizeof(struct iphdr),
24850b3ed57SPetar Penkov 						udph, len >> 1);
24950b3ed57SPetar Penkov 	else
25050b3ed57SPetar Penkov 		udph->check = build_udp_v6_csum(header - sizeof(struct ipv6hdr),
25150b3ed57SPetar Penkov 						udph, len >> 1);
25250b3ed57SPetar Penkov }
25350b3ed57SPetar Penkov 
build_gue_header(void * header,uint8_t proto)25450b3ed57SPetar Penkov static void build_gue_header(void *header, uint8_t proto)
25550b3ed57SPetar Penkov {
25650b3ed57SPetar Penkov 	struct guehdr *gueh = header;
25750b3ed57SPetar Penkov 
25850b3ed57SPetar Penkov 	gueh->proto_ctype = proto;
25950b3ed57SPetar Penkov }
26050b3ed57SPetar Penkov 
build_gre_header(void * header,uint16_t proto)26150b3ed57SPetar Penkov static void build_gre_header(void *header, uint16_t proto)
26250b3ed57SPetar Penkov {
26350b3ed57SPetar Penkov 	struct grehdr *greh = header;
26450b3ed57SPetar Penkov 
26550b3ed57SPetar Penkov 	greh->protocol = htons(proto);
26650b3ed57SPetar Penkov }
26750b3ed57SPetar Penkov 
l3_length(int family)26850b3ed57SPetar Penkov static int l3_length(int family)
26950b3ed57SPetar Penkov {
27050b3ed57SPetar Penkov 	if (family == AF_INET)
27150b3ed57SPetar Penkov 		return sizeof(struct iphdr);
27250b3ed57SPetar Penkov 	else
27350b3ed57SPetar Penkov 		return sizeof(struct ipv6hdr);
27450b3ed57SPetar Penkov }
27550b3ed57SPetar Penkov 
build_packet(void)27650b3ed57SPetar Penkov static int build_packet(void)
27750b3ed57SPetar Penkov {
27850b3ed57SPetar Penkov 	int ol3_len = 0, ol4_len = 0, il3_len = 0, il4_len = 0;
27950b3ed57SPetar Penkov 	int el3_len = 0;
28050b3ed57SPetar Penkov 
28150b3ed57SPetar Penkov 	if (cfg_l3_extra)
28250b3ed57SPetar Penkov 		el3_len = l3_length(cfg_l3_extra);
28350b3ed57SPetar Penkov 
28450b3ed57SPetar Penkov 	/* calculate header offsets */
28550b3ed57SPetar Penkov 	if (cfg_encap_proto) {
28650b3ed57SPetar Penkov 		ol3_len = l3_length(cfg_l3_outer);
28750b3ed57SPetar Penkov 
28850b3ed57SPetar Penkov 		if (cfg_encap_proto == IPPROTO_GRE)
28950b3ed57SPetar Penkov 			ol4_len = sizeof(struct grehdr);
29050b3ed57SPetar Penkov 		else if (cfg_encap_proto == IPPROTO_UDP)
29150b3ed57SPetar Penkov 			ol4_len = sizeof(struct udphdr) + sizeof(struct guehdr);
29250b3ed57SPetar Penkov 	}
29350b3ed57SPetar Penkov 
29450b3ed57SPetar Penkov 	il3_len = l3_length(cfg_l3_inner);
29550b3ed57SPetar Penkov 	il4_len = sizeof(struct udphdr);
29650b3ed57SPetar Penkov 
29750b3ed57SPetar Penkov 	if (el3_len + ol3_len + ol4_len + il3_len + il4_len + cfg_payload_len >=
29850b3ed57SPetar Penkov 	    sizeof(buf))
29950b3ed57SPetar Penkov 		error(1, 0, "packet too large\n");
30050b3ed57SPetar Penkov 
30150b3ed57SPetar Penkov 	/*
30250b3ed57SPetar Penkov 	 * Fill packet from inside out, to calculate correct checksums.
30350b3ed57SPetar Penkov 	 * But create ip before udp headers, as udp uses ip for pseudo-sum.
30450b3ed57SPetar Penkov 	 */
30550b3ed57SPetar Penkov 	memset(buf + el3_len + ol3_len + ol4_len + il3_len + il4_len,
30650b3ed57SPetar Penkov 	       cfg_payload_char, cfg_payload_len);
30750b3ed57SPetar Penkov 
30850b3ed57SPetar Penkov 	/* add zero byte for udp csum padding */
30950b3ed57SPetar Penkov 	buf[el3_len + ol3_len + ol4_len + il3_len + il4_len + cfg_payload_len] = 0;
31050b3ed57SPetar Penkov 
31150b3ed57SPetar Penkov 	switch (cfg_l3_inner) {
31250b3ed57SPetar Penkov 	case PF_INET:
31350b3ed57SPetar Penkov 		build_ipv4_header(buf + el3_len + ol3_len + ol4_len,
31450b3ed57SPetar Penkov 				  IPPROTO_UDP,
31550b3ed57SPetar Penkov 				  in_saddr4.sin_addr.s_addr,
31650b3ed57SPetar Penkov 				  in_daddr4.sin_addr.s_addr,
31750b3ed57SPetar Penkov 				  il4_len + cfg_payload_len,
31850b3ed57SPetar Penkov 				  cfg_dsfield_inner);
31950b3ed57SPetar Penkov 		break;
32050b3ed57SPetar Penkov 	case PF_INET6:
32150b3ed57SPetar Penkov 		build_ipv6_header(buf + el3_len + ol3_len + ol4_len,
32250b3ed57SPetar Penkov 				  IPPROTO_UDP,
32350b3ed57SPetar Penkov 				  &in_saddr6, &in_daddr6,
32450b3ed57SPetar Penkov 				  il4_len + cfg_payload_len,
32550b3ed57SPetar Penkov 				  cfg_dsfield_inner);
32650b3ed57SPetar Penkov 		break;
32750b3ed57SPetar Penkov 	}
32850b3ed57SPetar Penkov 
32950b3ed57SPetar Penkov 	build_udp_header(buf + el3_len + ol3_len + ol4_len + il3_len,
33050b3ed57SPetar Penkov 			 cfg_payload_len, CFG_PORT_INNER, cfg_l3_inner);
33150b3ed57SPetar Penkov 
33250b3ed57SPetar Penkov 	if (!cfg_encap_proto)
33350b3ed57SPetar Penkov 		return il3_len + il4_len + cfg_payload_len;
33450b3ed57SPetar Penkov 
33550b3ed57SPetar Penkov 	switch (cfg_l3_outer) {
33650b3ed57SPetar Penkov 	case PF_INET:
33750b3ed57SPetar Penkov 		build_ipv4_header(buf + el3_len, cfg_encap_proto,
33850b3ed57SPetar Penkov 				  out_saddr4.sin_addr.s_addr,
33950b3ed57SPetar Penkov 				  out_daddr4.sin_addr.s_addr,
34050b3ed57SPetar Penkov 				  ol4_len + il3_len + il4_len + cfg_payload_len,
34150b3ed57SPetar Penkov 				  cfg_dsfield_outer);
34250b3ed57SPetar Penkov 		break;
34350b3ed57SPetar Penkov 	case PF_INET6:
34450b3ed57SPetar Penkov 		build_ipv6_header(buf + el3_len, cfg_encap_proto,
34550b3ed57SPetar Penkov 				  &out_saddr6, &out_daddr6,
34650b3ed57SPetar Penkov 				  ol4_len + il3_len + il4_len + cfg_payload_len,
34750b3ed57SPetar Penkov 				  cfg_dsfield_outer);
34850b3ed57SPetar Penkov 		break;
34950b3ed57SPetar Penkov 	}
35050b3ed57SPetar Penkov 
35150b3ed57SPetar Penkov 	switch (cfg_encap_proto) {
35250b3ed57SPetar Penkov 	case IPPROTO_UDP:
35350b3ed57SPetar Penkov 		build_gue_header(buf + el3_len + ol3_len + ol4_len -
35450b3ed57SPetar Penkov 				 sizeof(struct guehdr),
35550b3ed57SPetar Penkov 				 cfg_l3_inner == PF_INET ? IPPROTO_IPIP
35650b3ed57SPetar Penkov 							 : IPPROTO_IPV6);
35750b3ed57SPetar Penkov 		build_udp_header(buf + el3_len + ol3_len,
35850b3ed57SPetar Penkov 				 sizeof(struct guehdr) + il3_len + il4_len +
35950b3ed57SPetar Penkov 				 cfg_payload_len,
36050b3ed57SPetar Penkov 				 cfg_port_gue, cfg_l3_outer);
36150b3ed57SPetar Penkov 		break;
36250b3ed57SPetar Penkov 	case IPPROTO_GRE:
36350b3ed57SPetar Penkov 		build_gre_header(buf + el3_len + ol3_len,
36450b3ed57SPetar Penkov 				 cfg_l3_inner == PF_INET ? ETH_P_IP
36550b3ed57SPetar Penkov 							 : ETH_P_IPV6);
36650b3ed57SPetar Penkov 		break;
36750b3ed57SPetar Penkov 	}
36850b3ed57SPetar Penkov 
36950b3ed57SPetar Penkov 	switch (cfg_l3_extra) {
37050b3ed57SPetar Penkov 	case PF_INET:
37150b3ed57SPetar Penkov 		build_ipv4_header(buf,
37250b3ed57SPetar Penkov 				  cfg_l3_outer == PF_INET ? IPPROTO_IPIP
37350b3ed57SPetar Penkov 							  : IPPROTO_IPV6,
37450b3ed57SPetar Penkov 				  extra_saddr4.sin_addr.s_addr,
37550b3ed57SPetar Penkov 				  extra_daddr4.sin_addr.s_addr,
37650b3ed57SPetar Penkov 				  ol3_len + ol4_len + il3_len + il4_len +
37750b3ed57SPetar Penkov 				  cfg_payload_len, 0);
37850b3ed57SPetar Penkov 		break;
37950b3ed57SPetar Penkov 	case PF_INET6:
38050b3ed57SPetar Penkov 		build_ipv6_header(buf,
38150b3ed57SPetar Penkov 				  cfg_l3_outer == PF_INET ? IPPROTO_IPIP
38250b3ed57SPetar Penkov 							  : IPPROTO_IPV6,
38350b3ed57SPetar Penkov 				  &extra_saddr6, &extra_daddr6,
38450b3ed57SPetar Penkov 				  ol3_len + ol4_len + il3_len + il4_len +
38550b3ed57SPetar Penkov 				  cfg_payload_len, 0);
38650b3ed57SPetar Penkov 		break;
38750b3ed57SPetar Penkov 	}
38850b3ed57SPetar Penkov 
38950b3ed57SPetar Penkov 	return el3_len + ol3_len + ol4_len + il3_len + il4_len +
39050b3ed57SPetar Penkov 	       cfg_payload_len;
39150b3ed57SPetar Penkov }
39250b3ed57SPetar Penkov 
39350b3ed57SPetar Penkov /* sender transmits encapsulated over RAW or unencap'd over UDP */
setup_tx(void)39450b3ed57SPetar Penkov static int setup_tx(void)
39550b3ed57SPetar Penkov {
39650b3ed57SPetar Penkov 	int family, fd, ret;
39750b3ed57SPetar Penkov 
39850b3ed57SPetar Penkov 	if (cfg_l3_extra)
39950b3ed57SPetar Penkov 		family = cfg_l3_extra;
40050b3ed57SPetar Penkov 	else if (cfg_l3_outer)
40150b3ed57SPetar Penkov 		family = cfg_l3_outer;
40250b3ed57SPetar Penkov 	else
40350b3ed57SPetar Penkov 		family = cfg_l3_inner;
40450b3ed57SPetar Penkov 
40550b3ed57SPetar Penkov 	fd = socket(family, SOCK_RAW, IPPROTO_RAW);
40650b3ed57SPetar Penkov 	if (fd == -1)
40750b3ed57SPetar Penkov 		error(1, errno, "socket tx");
40850b3ed57SPetar Penkov 
40950b3ed57SPetar Penkov 	if (cfg_l3_extra) {
41050b3ed57SPetar Penkov 		if (cfg_l3_extra == PF_INET)
41150b3ed57SPetar Penkov 			ret = connect(fd, (void *) &extra_daddr4,
41250b3ed57SPetar Penkov 				      sizeof(extra_daddr4));
41350b3ed57SPetar Penkov 		else
41450b3ed57SPetar Penkov 			ret = connect(fd, (void *) &extra_daddr6,
41550b3ed57SPetar Penkov 				      sizeof(extra_daddr6));
41650b3ed57SPetar Penkov 		if (ret)
41750b3ed57SPetar Penkov 			error(1, errno, "connect tx");
41850b3ed57SPetar Penkov 	} else if (cfg_l3_outer) {
41950b3ed57SPetar Penkov 		/* connect to destination if not encapsulated */
42050b3ed57SPetar Penkov 		if (cfg_l3_outer == PF_INET)
42150b3ed57SPetar Penkov 			ret = connect(fd, (void *) &out_daddr4,
42250b3ed57SPetar Penkov 				      sizeof(out_daddr4));
42350b3ed57SPetar Penkov 		else
42450b3ed57SPetar Penkov 			ret = connect(fd, (void *) &out_daddr6,
42550b3ed57SPetar Penkov 				      sizeof(out_daddr6));
42650b3ed57SPetar Penkov 		if (ret)
42750b3ed57SPetar Penkov 			error(1, errno, "connect tx");
42850b3ed57SPetar Penkov 	} else {
42950b3ed57SPetar Penkov 		/* otherwise using loopback */
43050b3ed57SPetar Penkov 		if (cfg_l3_inner == PF_INET)
43150b3ed57SPetar Penkov 			ret = connect(fd, (void *) &in_daddr4,
43250b3ed57SPetar Penkov 				      sizeof(in_daddr4));
43350b3ed57SPetar Penkov 		else
43450b3ed57SPetar Penkov 			ret = connect(fd, (void *) &in_daddr6,
43550b3ed57SPetar Penkov 				      sizeof(in_daddr6));
43650b3ed57SPetar Penkov 		if (ret)
43750b3ed57SPetar Penkov 			error(1, errno, "connect tx");
43850b3ed57SPetar Penkov 	}
43950b3ed57SPetar Penkov 
44050b3ed57SPetar Penkov 	return fd;
44150b3ed57SPetar Penkov }
44250b3ed57SPetar Penkov 
44350b3ed57SPetar Penkov /* receiver reads unencapsulated UDP */
setup_rx(void)44450b3ed57SPetar Penkov static int setup_rx(void)
44550b3ed57SPetar Penkov {
44650b3ed57SPetar Penkov 	int fd, ret;
44750b3ed57SPetar Penkov 
44850b3ed57SPetar Penkov 	fd = socket(cfg_l3_inner, SOCK_DGRAM, 0);
44950b3ed57SPetar Penkov 	if (fd == -1)
45050b3ed57SPetar Penkov 		error(1, errno, "socket rx");
45150b3ed57SPetar Penkov 
45250b3ed57SPetar Penkov 	if (cfg_l3_inner == PF_INET)
45350b3ed57SPetar Penkov 		ret = bind(fd, (void *) &in_daddr4, sizeof(in_daddr4));
45450b3ed57SPetar Penkov 	else
45550b3ed57SPetar Penkov 		ret = bind(fd, (void *) &in_daddr6, sizeof(in_daddr6));
45650b3ed57SPetar Penkov 	if (ret)
45750b3ed57SPetar Penkov 		error(1, errno, "bind rx");
45850b3ed57SPetar Penkov 
45950b3ed57SPetar Penkov 	return fd;
46050b3ed57SPetar Penkov }
46150b3ed57SPetar Penkov 
do_tx(int fd,const char * pkt,int len)46250b3ed57SPetar Penkov static int do_tx(int fd, const char *pkt, int len)
46350b3ed57SPetar Penkov {
46450b3ed57SPetar Penkov 	int ret;
46550b3ed57SPetar Penkov 
46650b3ed57SPetar Penkov 	ret = write(fd, pkt, len);
46750b3ed57SPetar Penkov 	if (ret == -1)
46850b3ed57SPetar Penkov 		error(1, errno, "send");
46950b3ed57SPetar Penkov 	if (ret != len)
47050b3ed57SPetar Penkov 		error(1, errno, "send: len (%d < %d)\n", ret, len);
47150b3ed57SPetar Penkov 
47250b3ed57SPetar Penkov 	return 1;
47350b3ed57SPetar Penkov }
47450b3ed57SPetar Penkov 
do_poll(int fd,short events,int timeout)47550b3ed57SPetar Penkov static int do_poll(int fd, short events, int timeout)
47650b3ed57SPetar Penkov {
47750b3ed57SPetar Penkov 	struct pollfd pfd;
47850b3ed57SPetar Penkov 	int ret;
47950b3ed57SPetar Penkov 
48050b3ed57SPetar Penkov 	pfd.fd = fd;
48150b3ed57SPetar Penkov 	pfd.events = events;
48250b3ed57SPetar Penkov 
48350b3ed57SPetar Penkov 	ret = poll(&pfd, 1, timeout);
48450b3ed57SPetar Penkov 	if (ret == -1)
48550b3ed57SPetar Penkov 		error(1, errno, "poll");
48650b3ed57SPetar Penkov 	if (ret && !(pfd.revents & POLLIN))
48750b3ed57SPetar Penkov 		error(1, errno, "poll: unexpected event 0x%x\n", pfd.revents);
48850b3ed57SPetar Penkov 
48950b3ed57SPetar Penkov 	return ret;
49050b3ed57SPetar Penkov }
49150b3ed57SPetar Penkov 
do_rx(int fd)49250b3ed57SPetar Penkov static int do_rx(int fd)
49350b3ed57SPetar Penkov {
49450b3ed57SPetar Penkov 	char rbuf;
49550b3ed57SPetar Penkov 	int ret, num = 0;
49650b3ed57SPetar Penkov 
49750b3ed57SPetar Penkov 	while (1) {
49850b3ed57SPetar Penkov 		ret = recv(fd, &rbuf, 1, MSG_DONTWAIT);
49950b3ed57SPetar Penkov 		if (ret == -1 && errno == EAGAIN)
50050b3ed57SPetar Penkov 			break;
50150b3ed57SPetar Penkov 		if (ret == -1)
50250b3ed57SPetar Penkov 			error(1, errno, "recv");
50350b3ed57SPetar Penkov 		if (rbuf != cfg_payload_char)
50450b3ed57SPetar Penkov 			error(1, 0, "recv: payload mismatch");
50550b3ed57SPetar Penkov 		num++;
506*11da9f0cSYang Li 	}
50750b3ed57SPetar Penkov 
50850b3ed57SPetar Penkov 	return num;
50950b3ed57SPetar Penkov }
51050b3ed57SPetar Penkov 
do_main(void)51150b3ed57SPetar Penkov static int do_main(void)
51250b3ed57SPetar Penkov {
51350b3ed57SPetar Penkov 	unsigned long tstop, treport, tcur;
51450b3ed57SPetar Penkov 	int fdt = -1, fdr = -1, len, tx = 0, rx = 0;
51550b3ed57SPetar Penkov 
51650b3ed57SPetar Penkov 	if (!cfg_only_tx)
51750b3ed57SPetar Penkov 		fdr = setup_rx();
51850b3ed57SPetar Penkov 	if (!cfg_only_rx)
51950b3ed57SPetar Penkov 		fdt = setup_tx();
52050b3ed57SPetar Penkov 
52150b3ed57SPetar Penkov 	len = build_packet();
52250b3ed57SPetar Penkov 
52350b3ed57SPetar Penkov 	tcur = util_gettime();
52450b3ed57SPetar Penkov 	treport = tcur + 1000;
52550b3ed57SPetar Penkov 	tstop = tcur + (cfg_num_secs * 1000);
52650b3ed57SPetar Penkov 
52750b3ed57SPetar Penkov 	while (1) {
52850b3ed57SPetar Penkov 		if (!cfg_only_rx)
52950b3ed57SPetar Penkov 			tx += do_tx(fdt, buf, len);
53050b3ed57SPetar Penkov 
53150b3ed57SPetar Penkov 		if (!cfg_only_tx)
53250b3ed57SPetar Penkov 			rx += do_rx(fdr);
53350b3ed57SPetar Penkov 
53450b3ed57SPetar Penkov 		if (cfg_num_secs) {
53550b3ed57SPetar Penkov 			tcur = util_gettime();
53650b3ed57SPetar Penkov 			if (tcur >= tstop)
53750b3ed57SPetar Penkov 				break;
53850b3ed57SPetar Penkov 			if (tcur >= treport) {
53950b3ed57SPetar Penkov 				fprintf(stderr, "pkts: tx=%u rx=%u\n", tx, rx);
54050b3ed57SPetar Penkov 				tx = 0;
54150b3ed57SPetar Penkov 				rx = 0;
54250b3ed57SPetar Penkov 				treport = tcur + 1000;
54350b3ed57SPetar Penkov 			}
54450b3ed57SPetar Penkov 		} else {
54550b3ed57SPetar Penkov 			if (tx == cfg_num_pkt)
54650b3ed57SPetar Penkov 				break;
54750b3ed57SPetar Penkov 		}
54850b3ed57SPetar Penkov 	}
54950b3ed57SPetar Penkov 
55050b3ed57SPetar Penkov 	/* read straggler packets, if any */
55150b3ed57SPetar Penkov 	if (rx < tx) {
55250b3ed57SPetar Penkov 		tstop = util_gettime() + 100;
55350b3ed57SPetar Penkov 		while (rx < tx) {
55450b3ed57SPetar Penkov 			tcur = util_gettime();
55550b3ed57SPetar Penkov 			if (tcur >= tstop)
55650b3ed57SPetar Penkov 				break;
55750b3ed57SPetar Penkov 
55850b3ed57SPetar Penkov 			do_poll(fdr, POLLIN, tstop - tcur);
55950b3ed57SPetar Penkov 			rx += do_rx(fdr);
56050b3ed57SPetar Penkov 		}
56150b3ed57SPetar Penkov 	}
56250b3ed57SPetar Penkov 
56350b3ed57SPetar Penkov 	fprintf(stderr, "pkts: tx=%u rx=%u\n", tx, rx);
56450b3ed57SPetar Penkov 
56550b3ed57SPetar Penkov 	if (fdr != -1 && close(fdr))
56650b3ed57SPetar Penkov 		error(1, errno, "close rx");
56750b3ed57SPetar Penkov 	if (fdt != -1 && close(fdt))
56850b3ed57SPetar Penkov 		error(1, errno, "close tx");
56950b3ed57SPetar Penkov 
57050b3ed57SPetar Penkov 	/*
57150b3ed57SPetar Penkov 	 * success (== 0) only if received all packets
57250b3ed57SPetar Penkov 	 * unless failure is expected, in which case none must arrive.
57350b3ed57SPetar Penkov 	 */
57450b3ed57SPetar Penkov 	if (cfg_expect_failure)
57550b3ed57SPetar Penkov 		return rx != 0;
57650b3ed57SPetar Penkov 	else
57750b3ed57SPetar Penkov 		return rx != tx;
57850b3ed57SPetar Penkov }
57950b3ed57SPetar Penkov 
58050b3ed57SPetar Penkov 
usage(const char * filepath)58150b3ed57SPetar Penkov static void __attribute__((noreturn)) usage(const char *filepath)
58250b3ed57SPetar Penkov {
58350b3ed57SPetar Penkov 	fprintf(stderr, "Usage: %s [-e gre|gue|bare|none] [-i 4|6] [-l len] "
58450b3ed57SPetar Penkov 			"[-O 4|6] [-o 4|6] [-n num] [-t secs] [-R] [-T] "
58550b3ed57SPetar Penkov 			"[-s <osrc> [-d <odst>] [-S <isrc>] [-D <idst>] "
58650b3ed57SPetar Penkov 			"[-x <otos>] [-X <itos>] [-f <isport>] [-F]\n",
58750b3ed57SPetar Penkov 		filepath);
58850b3ed57SPetar Penkov 	exit(1);
58950b3ed57SPetar Penkov }
59050b3ed57SPetar Penkov 
parse_addr(int family,void * addr,const char * optarg)59150b3ed57SPetar Penkov static void parse_addr(int family, void *addr, const char *optarg)
59250b3ed57SPetar Penkov {
59350b3ed57SPetar Penkov 	int ret;
59450b3ed57SPetar Penkov 
59550b3ed57SPetar Penkov 	ret = inet_pton(family, optarg, addr);
59650b3ed57SPetar Penkov 	if (ret == -1)
59750b3ed57SPetar Penkov 		error(1, errno, "inet_pton");
59850b3ed57SPetar Penkov 	if (ret == 0)
59950b3ed57SPetar Penkov 		error(1, 0, "inet_pton: bad string");
60050b3ed57SPetar Penkov }
60150b3ed57SPetar Penkov 
parse_addr4(struct sockaddr_in * addr,const char * optarg)60250b3ed57SPetar Penkov static void parse_addr4(struct sockaddr_in *addr, const char *optarg)
60350b3ed57SPetar Penkov {
60450b3ed57SPetar Penkov 	parse_addr(AF_INET, &addr->sin_addr, optarg);
60550b3ed57SPetar Penkov }
60650b3ed57SPetar Penkov 
parse_addr6(struct sockaddr_in6 * addr,const char * optarg)60750b3ed57SPetar Penkov static void parse_addr6(struct sockaddr_in6 *addr, const char *optarg)
60850b3ed57SPetar Penkov {
60950b3ed57SPetar Penkov 	parse_addr(AF_INET6, &addr->sin6_addr, optarg);
61050b3ed57SPetar Penkov }
61150b3ed57SPetar Penkov 
parse_protocol_family(const char * filepath,const char * optarg)61250b3ed57SPetar Penkov static int parse_protocol_family(const char *filepath, const char *optarg)
61350b3ed57SPetar Penkov {
61450b3ed57SPetar Penkov 	if (!strcmp(optarg, "4"))
61550b3ed57SPetar Penkov 		return PF_INET;
61650b3ed57SPetar Penkov 	if (!strcmp(optarg, "6"))
61750b3ed57SPetar Penkov 		return PF_INET6;
61850b3ed57SPetar Penkov 
61950b3ed57SPetar Penkov 	usage(filepath);
62050b3ed57SPetar Penkov }
62150b3ed57SPetar Penkov 
parse_opts(int argc,char ** argv)62250b3ed57SPetar Penkov static void parse_opts(int argc, char **argv)
62350b3ed57SPetar Penkov {
62450b3ed57SPetar Penkov 	int c;
62550b3ed57SPetar Penkov 
62650b3ed57SPetar Penkov 	while ((c = getopt(argc, argv, "d:D:e:f:Fhi:l:n:o:O:Rs:S:t:Tx:X:")) != -1) {
62750b3ed57SPetar Penkov 		switch (c) {
62850b3ed57SPetar Penkov 		case 'd':
62950b3ed57SPetar Penkov 			if (cfg_l3_outer == AF_UNSPEC)
63050b3ed57SPetar Penkov 				error(1, 0, "-d must be preceded by -o");
63150b3ed57SPetar Penkov 			if (cfg_l3_outer == AF_INET)
63250b3ed57SPetar Penkov 				parse_addr4(&out_daddr4, optarg);
63350b3ed57SPetar Penkov 			else
63450b3ed57SPetar Penkov 				parse_addr6(&out_daddr6, optarg);
63550b3ed57SPetar Penkov 			break;
63650b3ed57SPetar Penkov 		case 'D':
63750b3ed57SPetar Penkov 			if (cfg_l3_inner == AF_UNSPEC)
63850b3ed57SPetar Penkov 				error(1, 0, "-D must be preceded by -i");
63950b3ed57SPetar Penkov 			if (cfg_l3_inner == AF_INET)
64050b3ed57SPetar Penkov 				parse_addr4(&in_daddr4, optarg);
64150b3ed57SPetar Penkov 			else
64250b3ed57SPetar Penkov 				parse_addr6(&in_daddr6, optarg);
64350b3ed57SPetar Penkov 			break;
64450b3ed57SPetar Penkov 		case 'e':
64550b3ed57SPetar Penkov 			if (!strcmp(optarg, "gre"))
64650b3ed57SPetar Penkov 				cfg_encap_proto = IPPROTO_GRE;
64750b3ed57SPetar Penkov 			else if (!strcmp(optarg, "gue"))
64850b3ed57SPetar Penkov 				cfg_encap_proto = IPPROTO_UDP;
64950b3ed57SPetar Penkov 			else if (!strcmp(optarg, "bare"))
65050b3ed57SPetar Penkov 				cfg_encap_proto = IPPROTO_IPIP;
65150b3ed57SPetar Penkov 			else if (!strcmp(optarg, "none"))
65250b3ed57SPetar Penkov 				cfg_encap_proto = IPPROTO_IP;	/* == 0 */
65350b3ed57SPetar Penkov 			else
65450b3ed57SPetar Penkov 				usage(argv[0]);
65550b3ed57SPetar Penkov 			break;
65650b3ed57SPetar Penkov 		case 'f':
65750b3ed57SPetar Penkov 			cfg_src_port = strtol(optarg, NULL, 0);
65850b3ed57SPetar Penkov 			break;
65950b3ed57SPetar Penkov 		case 'F':
66050b3ed57SPetar Penkov 			cfg_expect_failure = true;
66150b3ed57SPetar Penkov 			break;
66250b3ed57SPetar Penkov 		case 'h':
66350b3ed57SPetar Penkov 			usage(argv[0]);
66450b3ed57SPetar Penkov 			break;
66550b3ed57SPetar Penkov 		case 'i':
66650b3ed57SPetar Penkov 			if (!strcmp(optarg, "4"))
66750b3ed57SPetar Penkov 				cfg_l3_inner = PF_INET;
66850b3ed57SPetar Penkov 			else if (!strcmp(optarg, "6"))
66950b3ed57SPetar Penkov 				cfg_l3_inner = PF_INET6;
67050b3ed57SPetar Penkov 			else
67150b3ed57SPetar Penkov 				usage(argv[0]);
67250b3ed57SPetar Penkov 			break;
67350b3ed57SPetar Penkov 		case 'l':
67450b3ed57SPetar Penkov 			cfg_payload_len = strtol(optarg, NULL, 0);
67550b3ed57SPetar Penkov 			break;
67650b3ed57SPetar Penkov 		case 'n':
67750b3ed57SPetar Penkov 			cfg_num_pkt = strtol(optarg, NULL, 0);
67850b3ed57SPetar Penkov 			break;
67950b3ed57SPetar Penkov 		case 'o':
68050b3ed57SPetar Penkov 			cfg_l3_outer = parse_protocol_family(argv[0], optarg);
68150b3ed57SPetar Penkov 			break;
68250b3ed57SPetar Penkov 		case 'O':
68350b3ed57SPetar Penkov 			cfg_l3_extra = parse_protocol_family(argv[0], optarg);
68450b3ed57SPetar Penkov 			break;
68550b3ed57SPetar Penkov 		case 'R':
68650b3ed57SPetar Penkov 			cfg_only_rx = true;
68750b3ed57SPetar Penkov 			break;
68850b3ed57SPetar Penkov 		case 's':
68950b3ed57SPetar Penkov 			if (cfg_l3_outer == AF_INET)
69050b3ed57SPetar Penkov 				parse_addr4(&out_saddr4, optarg);
69150b3ed57SPetar Penkov 			else
69250b3ed57SPetar Penkov 				parse_addr6(&out_saddr6, optarg);
69350b3ed57SPetar Penkov 			break;
69450b3ed57SPetar Penkov 		case 'S':
69550b3ed57SPetar Penkov 			if (cfg_l3_inner == AF_INET)
69650b3ed57SPetar Penkov 				parse_addr4(&in_saddr4, optarg);
69750b3ed57SPetar Penkov 			else
69850b3ed57SPetar Penkov 				parse_addr6(&in_saddr6, optarg);
69950b3ed57SPetar Penkov 			break;
70050b3ed57SPetar Penkov 		case 't':
70150b3ed57SPetar Penkov 			cfg_num_secs = strtol(optarg, NULL, 0);
70250b3ed57SPetar Penkov 			break;
70350b3ed57SPetar Penkov 		case 'T':
70450b3ed57SPetar Penkov 			cfg_only_tx = true;
70550b3ed57SPetar Penkov 			break;
70650b3ed57SPetar Penkov 		case 'x':
70750b3ed57SPetar Penkov 			cfg_dsfield_outer = strtol(optarg, NULL, 0);
70850b3ed57SPetar Penkov 			break;
70950b3ed57SPetar Penkov 		case 'X':
71050b3ed57SPetar Penkov 			cfg_dsfield_inner = strtol(optarg, NULL, 0);
71150b3ed57SPetar Penkov 			break;
71250b3ed57SPetar Penkov 		}
71350b3ed57SPetar Penkov 	}
71450b3ed57SPetar Penkov 
71550b3ed57SPetar Penkov 	if (cfg_only_rx && cfg_only_tx)
71650b3ed57SPetar Penkov 		error(1, 0, "options: cannot combine rx-only and tx-only");
71750b3ed57SPetar Penkov 
71850b3ed57SPetar Penkov 	if (cfg_encap_proto && cfg_l3_outer == AF_UNSPEC)
71950b3ed57SPetar Penkov 		error(1, 0, "options: must specify outer with encap");
72050b3ed57SPetar Penkov 	else if ((!cfg_encap_proto) && cfg_l3_outer != AF_UNSPEC)
72150b3ed57SPetar Penkov 		error(1, 0, "options: cannot combine no-encap and outer");
72250b3ed57SPetar Penkov 	else if ((!cfg_encap_proto) && cfg_l3_extra != AF_UNSPEC)
72350b3ed57SPetar Penkov 		error(1, 0, "options: cannot combine no-encap and extra");
72450b3ed57SPetar Penkov 
72550b3ed57SPetar Penkov 	if (cfg_l3_inner == AF_UNSPEC)
72650b3ed57SPetar Penkov 		cfg_l3_inner = AF_INET6;
72750b3ed57SPetar Penkov 	if (cfg_l3_inner == AF_INET6 && cfg_encap_proto == IPPROTO_IPIP)
72850b3ed57SPetar Penkov 		cfg_encap_proto = IPPROTO_IPV6;
72950b3ed57SPetar Penkov 
73050b3ed57SPetar Penkov 	/* RFC 6040 4.2:
73150b3ed57SPetar Penkov 	 *   on decap, if outer encountered congestion (CE == 0x3),
73250b3ed57SPetar Penkov 	 *   but inner cannot encode ECN (NoECT == 0x0), then drop packet.
73350b3ed57SPetar Penkov 	 */
73450b3ed57SPetar Penkov 	if (((cfg_dsfield_outer & 0x3) == 0x3) &&
73550b3ed57SPetar Penkov 	    ((cfg_dsfield_inner & 0x3) == 0x0))
73650b3ed57SPetar Penkov 		cfg_expect_failure = true;
73750b3ed57SPetar Penkov }
73850b3ed57SPetar Penkov 
print_opts(void)73950b3ed57SPetar Penkov static void print_opts(void)
74050b3ed57SPetar Penkov {
74150b3ed57SPetar Penkov 	if (cfg_l3_inner == PF_INET6) {
74250b3ed57SPetar Penkov 		util_printaddr("inner.dest6", (void *) &in_daddr6);
74350b3ed57SPetar Penkov 		util_printaddr("inner.source6", (void *) &in_saddr6);
74450b3ed57SPetar Penkov 	} else {
74550b3ed57SPetar Penkov 		util_printaddr("inner.dest4", (void *) &in_daddr4);
74650b3ed57SPetar Penkov 		util_printaddr("inner.source4", (void *) &in_saddr4);
74750b3ed57SPetar Penkov 	}
74850b3ed57SPetar Penkov 
74950b3ed57SPetar Penkov 	if (!cfg_l3_outer)
75050b3ed57SPetar Penkov 		return;
75150b3ed57SPetar Penkov 
75250b3ed57SPetar Penkov 	fprintf(stderr, "encap proto:   %u\n", cfg_encap_proto);
75350b3ed57SPetar Penkov 
75450b3ed57SPetar Penkov 	if (cfg_l3_outer == PF_INET6) {
75550b3ed57SPetar Penkov 		util_printaddr("outer.dest6", (void *) &out_daddr6);
75650b3ed57SPetar Penkov 		util_printaddr("outer.source6", (void *) &out_saddr6);
75750b3ed57SPetar Penkov 	} else {
75850b3ed57SPetar Penkov 		util_printaddr("outer.dest4", (void *) &out_daddr4);
75950b3ed57SPetar Penkov 		util_printaddr("outer.source4", (void *) &out_saddr4);
76050b3ed57SPetar Penkov 	}
76150b3ed57SPetar Penkov 
76250b3ed57SPetar Penkov 	if (!cfg_l3_extra)
76350b3ed57SPetar Penkov 		return;
76450b3ed57SPetar Penkov 
76550b3ed57SPetar Penkov 	if (cfg_l3_outer == PF_INET6) {
76650b3ed57SPetar Penkov 		util_printaddr("extra.dest6", (void *) &extra_daddr6);
76750b3ed57SPetar Penkov 		util_printaddr("extra.source6", (void *) &extra_saddr6);
76850b3ed57SPetar Penkov 	} else {
76950b3ed57SPetar Penkov 		util_printaddr("extra.dest4", (void *) &extra_daddr4);
77050b3ed57SPetar Penkov 		util_printaddr("extra.source4", (void *) &extra_saddr4);
77150b3ed57SPetar Penkov 	}
77250b3ed57SPetar Penkov 
77350b3ed57SPetar Penkov }
77450b3ed57SPetar Penkov 
main(int argc,char ** argv)77550b3ed57SPetar Penkov int main(int argc, char **argv)
77650b3ed57SPetar Penkov {
77750b3ed57SPetar Penkov 	parse_opts(argc, argv);
77850b3ed57SPetar Penkov 	print_opts();
77950b3ed57SPetar Penkov 	return do_main();
78050b3ed57SPetar Penkov }
781