781 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Inject packets with all sorts of encapsulation into the kernel.4 *5 * IPv4/IPv6 outer layer 36 * GRE/GUE/BARE outer layer 4, where bare is IPIP/SIT/IPv4-in-IPv6/..7 * IPv4/IPv6 inner layer 38 */9 10#define _GNU_SOURCE11 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 800036 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#else56#error "Please fix <asm/byteorder.h>"57#endif58 __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 else250 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 else273 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_IPIP356 : 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_IP365 : 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_IPIP373 : 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_IPIP382 : 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 else403 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 else414 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 else424 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 else434 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 else455 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 packets572 * unless failure is expected, in which case none must arrive.573 */574 if (cfg_expect_failure)575 return rx != 0;576 else577 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 else634 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 else642 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 else654 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 else671 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 else692 parse_addr6(&out_saddr6, optarg);693 break;694 case 'S':695 if (cfg_l3_inner == AF_INET)696 parse_addr4(&in_saddr4, optarg);697 else698 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