1
2
3
4
5
6
7
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
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;
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);
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);
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
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
303
304
305 memset(buf + el3_len + ol3_len + ol4_len + il3_len + il4_len,
306 cfg_payload_char, cfg_payload_len);
307
308
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
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
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
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
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
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
572
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;
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
731
732
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