test_flow_dissector.c (18626B)
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}