treewide: remove redundant IS_ERR() before error code check
[linux/fpc-iii.git] / tools / testing / selftests / bpf / test_flow_dissector.c
blob01f0c634d548185dbc1874f8b8a875661c406772
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Inject packets with all sorts of encapsulation into the kernel.
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 */
10 #define _GNU_SOURCE
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>
35 #define CFG_PORT_INNER 8000
37 /* Add some protocol definitions that do not exist in userspace */
39 struct grehdr {
40 uint16_t unused;
41 uint16_t protocol;
42 } __attribute__((packed));
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;
61 __be32 word;
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;
81 static char buf[ETH_DATA_LEN];
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), \
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, \
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)
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)
111 static unsigned long util_gettime(void)
113 struct timeval tv;
115 gettimeofday(&tv, NULL);
116 return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
119 static void util_printaddr(const char *msg, struct sockaddr *addr)
121 unsigned long off = 0;
122 char nbuf[INET6_ADDRSTRLEN];
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);
136 if (!inet_ntop(addr->sa_family, ((void *) addr) + off, nbuf,
137 sizeof(nbuf)))
138 error(1, errno, "inet_ntop");
140 fprintf(stderr, "%s: %s\n", msg, nbuf);
143 static unsigned long add_csum_hword(const uint16_t *start, int num_u16)
145 unsigned long sum = 0;
146 int i;
148 for (i = 0; i < num_u16; i++)
149 sum += start[i];
151 return sum;
154 static uint16_t build_ip_csum(const uint16_t *start, int num_u16,
155 unsigned long sum)
157 sum += add_csum_hword(start, num_u16);
159 while (sum >> 16)
160 sum = (sum & 0xffff) + (sum >> 16);
162 return ~sum;
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)
169 struct iphdr *iph = header;
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);
183 static void ipv6_set_dsfield(struct ipv6hdr *ip6h, uint8_t dsfield)
185 uint16_t val, *ptr = (uint16_t *)ip6h;
187 val = ntohs(*ptr);
188 val &= 0xF00F;
189 val |= ((uint16_t) dsfield) << 4;
190 *ptr = htons(val);
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)
198 struct ipv6hdr *ip6h = header;
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);
206 memcpy(&ip6h->saddr, &src->sin6_addr, sizeof(ip6h->saddr));
207 memcpy(&ip6h->daddr, &dst->sin6_addr, sizeof(ip6h->daddr));
210 static uint16_t build_udp_v4_csum(const struct iphdr *iph,
211 const struct udphdr *udph,
212 int num_words)
214 unsigned long pseudo_sum;
215 int num_u16 = sizeof(iph->saddr); /* halfwords: twice byte len */
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);
223 static uint16_t build_udp_v6_csum(const struct ipv6hdr *ip6h,
224 const struct udphdr *udph,
225 int num_words)
227 unsigned long pseudo_sum;
228 int num_u16 = sizeof(ip6h->saddr); /* halfwords: twice byte len */
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);
236 static void build_udp_header(void *header, int payload_len,
237 uint16_t dport, int family)
239 struct udphdr *udph = header;
240 int len = sizeof(*udph) + payload_len;
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);
254 static void build_gue_header(void *header, uint8_t proto)
256 struct guehdr *gueh = header;
258 gueh->proto_ctype = proto;
261 static void build_gre_header(void *header, uint16_t proto)
263 struct grehdr *greh = header;
265 greh->protocol = htons(proto);
268 static int l3_length(int family)
270 if (family == AF_INET)
271 return sizeof(struct iphdr);
272 else
273 return sizeof(struct ipv6hdr);
276 static int build_packet(void)
278 int ol3_len = 0, ol4_len = 0, il3_len = 0, il4_len = 0;
279 int el3_len = 0;
281 if (cfg_l3_extra)
282 el3_len = l3_length(cfg_l3_extra);
284 /* calculate header offsets */
285 if (cfg_encap_proto) {
286 ol3_len = l3_length(cfg_l3_outer);
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);
294 il3_len = l3_length(cfg_l3_inner);
295 il4_len = sizeof(struct udphdr);
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");
302 * Fill packet from inside out, to calculate correct checksums.
303 * But create ip before udp headers, as udp uses ip for pseudo-sum.
305 memset(buf + el3_len + ol3_len + ol4_len + il3_len + il4_len,
306 cfg_payload_char, cfg_payload_len);
308 /* add zero byte for udp csum padding */
309 buf[el3_len + ol3_len + ol4_len + il3_len + il4_len + cfg_payload_len] = 0;
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;
329 build_udp_header(buf + el3_len + ol3_len + ol4_len + il3_len,
330 cfg_payload_len, CFG_PORT_INNER, cfg_l3_inner);
332 if (!cfg_encap_proto)
333 return il3_len + il4_len + cfg_payload_len;
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;
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;
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;
389 return el3_len + ol3_len + ol4_len + il3_len + il4_len +
390 cfg_payload_len;
393 /* sender transmits encapsulated over RAW or unencap'd over UDP */
394 static int setup_tx(void)
396 int family, fd, ret;
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;
405 fd = socket(family, SOCK_RAW, IPPROTO_RAW);
406 if (fd == -1)
407 error(1, errno, "socket tx");
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");
440 return fd;
443 /* receiver reads unencapsulated UDP */
444 static int setup_rx(void)
446 int fd, ret;
448 fd = socket(cfg_l3_inner, SOCK_DGRAM, 0);
449 if (fd == -1)
450 error(1, errno, "socket rx");
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");
459 return fd;
462 static int do_tx(int fd, const char *pkt, int len)
464 int ret;
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);
472 return 1;
475 static int do_poll(int fd, short events, int timeout)
477 struct pollfd pfd;
478 int ret;
480 pfd.fd = fd;
481 pfd.events = events;
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);
489 return ret;
492 static int do_rx(int fd)
494 char rbuf;
495 int ret, num = 0;
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++;
508 return num;
511 static int do_main(void)
513 unsigned long tstop, treport, tcur;
514 int fdt = -1, fdr = -1, len, tx = 0, rx = 0;
516 if (!cfg_only_tx)
517 fdr = setup_rx();
518 if (!cfg_only_rx)
519 fdt = setup_tx();
521 len = build_packet();
523 tcur = util_gettime();
524 treport = tcur + 1000;
525 tstop = tcur + (cfg_num_secs * 1000);
527 while (1) {
528 if (!cfg_only_rx)
529 tx += do_tx(fdt, buf, len);
531 if (!cfg_only_tx)
532 rx += do_rx(fdr);
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;
544 } else {
545 if (tx == cfg_num_pkt)
546 break;
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;
558 do_poll(fdr, POLLIN, tstop - tcur);
559 rx += do_rx(fdr);
563 fprintf(stderr, "pkts: tx=%u rx=%u\n", tx, rx);
565 if (fdr != -1 && close(fdr))
566 error(1, errno, "close rx");
567 if (fdt != -1 && close(fdt))
568 error(1, errno, "close tx");
571 * success (== 0) only if received all packets
572 * unless failure is expected, in which case none must arrive.
574 if (cfg_expect_failure)
575 return rx != 0;
576 else
577 return rx != tx;
581 static void __attribute__((noreturn)) usage(const char *filepath)
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);
591 static void parse_addr(int family, void *addr, const char *optarg)
593 int ret;
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");
602 static void parse_addr4(struct sockaddr_in *addr, const char *optarg)
604 parse_addr(AF_INET, &addr->sin_addr, optarg);
607 static void parse_addr6(struct sockaddr_in6 *addr, const char *optarg)
609 parse_addr(AF_INET6, &addr->sin6_addr, optarg);
612 static int parse_protocol_family(const char *filepath, const char *optarg)
614 if (!strcmp(optarg, "4"))
615 return PF_INET;
616 if (!strcmp(optarg, "6"))
617 return PF_INET6;
619 usage(filepath);
622 static void parse_opts(int argc, char **argv)
624 int c;
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;
715 if (cfg_only_rx && cfg_only_tx)
716 error(1, 0, "options: cannot combine rx-only and tx-only");
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");
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;
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.
734 if (((cfg_dsfield_outer & 0x3) == 0x3) &&
735 ((cfg_dsfield_inner & 0x3) == 0x0))
736 cfg_expect_failure = true;
739 static void print_opts(void)
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);
749 if (!cfg_l3_outer)
750 return;
752 fprintf(stderr, "encap proto: %u\n", cfg_encap_proto);
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);
762 if (!cfg_l3_extra)
763 return;
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);
775 int main(int argc, char **argv)
777 parse_opts(argc, argv);
778 print_opts();
779 return do_main();