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