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 
39 struct grehdr {
40 	uint16_t unused;
41 	uint16_t protocol;
42 } __attribute__((packed));
43 
44 struct 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 
65 static uint8_t	cfg_dsfield_inner;
66 static uint8_t	cfg_dsfield_outer;
67 static uint8_t	cfg_encap_proto;
68 static bool	cfg_expect_failure = false;
69 static int	cfg_l3_extra = AF_UNSPEC;	/* optional SIT prefix */
70 static int	cfg_l3_inner = AF_UNSPEC;
71 static int	cfg_l3_outer = AF_UNSPEC;
72 static int	cfg_num_pkt = 10;
73 static int	cfg_num_secs = 0;
74 static char	cfg_payload_char = 'a';
75 static int	cfg_payload_len = 100;
76 static int	cfg_port_gue = 6080;
77 static bool	cfg_only_rx;
78 static bool	cfg_only_tx;
79 static int	cfg_src_port = 9;
80 
81 static 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 
97 INIT_ADDR4(in_daddr4, INADDR_LOOPBACK, CFG_PORT_INNER)
98 INIT_ADDR4(in_saddr4, INADDR_LOOPBACK + 2, 0)
99 INIT_ADDR4(out_daddr4, INADDR_LOOPBACK, 0)
100 INIT_ADDR4(out_saddr4, INADDR_LOOPBACK + 1, 0)
101 INIT_ADDR4(extra_daddr4, INADDR_LOOPBACK, 0)
102 INIT_ADDR4(extra_saddr4, INADDR_LOOPBACK + 1, 0)
103 
104 INIT_ADDR6(in_daddr6, IN6ADDR_LOOPBACK_INIT, CFG_PORT_INNER)
105 INIT_ADDR6(in_saddr6, IN6ADDR_LOOPBACK_INIT, 0)
106 INIT_ADDR6(out_daddr6, IN6ADDR_LOOPBACK_INIT, 0)
107 INIT_ADDR6(out_saddr6, IN6ADDR_LOOPBACK_INIT, 0)
108 INIT_ADDR6(extra_daddr6, IN6ADDR_LOOPBACK_INIT, 0)
109 INIT_ADDR6(extra_saddr6, IN6ADDR_LOOPBACK_INIT, 0)
110 
111 static 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 
119 static 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 
143 static 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 
154 static 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 
165 static 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 
183 static 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 
193 static 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 
210 static 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 
223 static 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 
236 static 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 
254 static void build_gue_header(void *header, uint8_t proto)
255 {
256 	struct guehdr *gueh = header;
257 
258 	gueh->proto_ctype = proto;
259 }
260 
261 static void build_gre_header(void *header, uint16_t proto)
262 {
263 	struct grehdr *greh = header;
264 
265 	greh->protocol = htons(proto);
266 }
267 
268 static 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 
276 static 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 */
394 static 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 */
444 static 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 
462 static 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 
475 static 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 
492 static 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 
511 static 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 
581 static 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 
591 static 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 
602 static void parse_addr4(struct sockaddr_in *addr, const char *optarg)
603 {
604 	parse_addr(AF_INET, &addr->sin_addr, optarg);
605 }
606 
607 static void parse_addr6(struct sockaddr_in6 *addr, const char *optarg)
608 {
609 	parse_addr(AF_INET6, &addr->sin6_addr, optarg);
610 }
611 
612 static 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 
622 static 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 
739 static 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 
775 int main(int argc, char **argv)
776 {
777 	parse_opts(argc, argv);
778 	print_opts();
779 	return do_main();
780 }
781