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/if_packet.h> 20 #include <linux/ipv6.h> 21 #include <netinet/ip.h> 22 #include <netinet/in.h> 23 #include <netinet/udp.h> 24 #include <poll.h> 25 #include <stdbool.h> 26 #include <stdlib.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <sys/ioctl.h> 31 #include <sys/socket.h> 32 #include <sys/stat.h> 33 #include <sys/time.h> 34 #include <sys/types.h> 35 #include <unistd.h> 36 37 #define CFG_PORT_INNER 8000 38 39 /* Add some protocol definitions that do not exist in userspace */ 40 41 struct grehdr { 42 uint16_t unused; 43 uint16_t protocol; 44 } __attribute__((packed)); 45 46 struct guehdr { 47 union { 48 struct { 49 #if defined(__LITTLE_ENDIAN_BITFIELD) 50 __u8 hlen:5, 51 control:1, 52 version:2; 53 #elif defined (__BIG_ENDIAN_BITFIELD) 54 __u8 version:2, 55 control:1, 56 hlen:5; 57 #else 58 #error "Please fix <asm/byteorder.h>" 59 #endif 60 __u8 proto_ctype; 61 __be16 flags; 62 }; 63 __be32 word; 64 }; 65 }; 66 67 static uint8_t cfg_dsfield_inner; 68 static uint8_t cfg_dsfield_outer; 69 static uint8_t cfg_encap_proto; 70 static bool cfg_expect_failure = false; 71 static int cfg_l3_extra = AF_UNSPEC; /* optional SIT prefix */ 72 static int cfg_l3_inner = AF_UNSPEC; 73 static int cfg_l3_outer = AF_UNSPEC; 74 static int cfg_num_pkt = 10; 75 static int cfg_num_secs = 0; 76 static char cfg_payload_char = 'a'; 77 static int cfg_payload_len = 100; 78 static int cfg_port_gue = 6080; 79 static bool cfg_only_rx; 80 static bool cfg_only_tx; 81 static int cfg_src_port = 9; 82 83 static char buf[ETH_DATA_LEN]; 84 85 #define INIT_ADDR4(name, addr4, port) \ 86 static struct sockaddr_in name = { \ 87 .sin_family = AF_INET, \ 88 .sin_port = __constant_htons(port), \ 89 .sin_addr.s_addr = __constant_htonl(addr4), \ 90 }; 91 92 #define INIT_ADDR6(name, addr6, port) \ 93 static struct sockaddr_in6 name = { \ 94 .sin6_family = AF_INET6, \ 95 .sin6_port = __constant_htons(port), \ 96 .sin6_addr = addr6, \ 97 }; 98 99 INIT_ADDR4(in_daddr4, INADDR_LOOPBACK, CFG_PORT_INNER) 100 INIT_ADDR4(in_saddr4, INADDR_LOOPBACK + 2, 0) 101 INIT_ADDR4(out_daddr4, INADDR_LOOPBACK, 0) 102 INIT_ADDR4(out_saddr4, INADDR_LOOPBACK + 1, 0) 103 INIT_ADDR4(extra_daddr4, INADDR_LOOPBACK, 0) 104 INIT_ADDR4(extra_saddr4, INADDR_LOOPBACK + 1, 0) 105 106 INIT_ADDR6(in_daddr6, IN6ADDR_LOOPBACK_INIT, CFG_PORT_INNER) 107 INIT_ADDR6(in_saddr6, IN6ADDR_LOOPBACK_INIT, 0) 108 INIT_ADDR6(out_daddr6, IN6ADDR_LOOPBACK_INIT, 0) 109 INIT_ADDR6(out_saddr6, IN6ADDR_LOOPBACK_INIT, 0) 110 INIT_ADDR6(extra_daddr6, IN6ADDR_LOOPBACK_INIT, 0) 111 INIT_ADDR6(extra_saddr6, IN6ADDR_LOOPBACK_INIT, 0) 112 113 static unsigned long util_gettime(void) 114 { 115 struct timeval tv; 116 117 gettimeofday(&tv, NULL); 118 return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); 119 } 120 121 static void util_printaddr(const char *msg, struct sockaddr *addr) 122 { 123 unsigned long off = 0; 124 char nbuf[INET6_ADDRSTRLEN]; 125 126 switch (addr->sa_family) { 127 case PF_INET: 128 off = __builtin_offsetof(struct sockaddr_in, sin_addr); 129 break; 130 case PF_INET6: 131 off = __builtin_offsetof(struct sockaddr_in6, sin6_addr); 132 break; 133 default: 134 error(1, 0, "printaddr: unsupported family %u\n", 135 addr->sa_family); 136 } 137 138 if (!inet_ntop(addr->sa_family, ((void *) addr) + off, nbuf, 139 sizeof(nbuf))) 140 error(1, errno, "inet_ntop"); 141 142 fprintf(stderr, "%s: %s\n", msg, nbuf); 143 } 144 145 static unsigned long add_csum_hword(const uint16_t *start, int num_u16) 146 { 147 unsigned long sum = 0; 148 int i; 149 150 for (i = 0; i < num_u16; i++) 151 sum += start[i]; 152 153 return sum; 154 } 155 156 static uint16_t build_ip_csum(const uint16_t *start, int num_u16, 157 unsigned long sum) 158 { 159 sum += add_csum_hword(start, num_u16); 160 161 while (sum >> 16) 162 sum = (sum & 0xffff) + (sum >> 16); 163 164 return ~sum; 165 } 166 167 static void build_ipv4_header(void *header, uint8_t proto, 168 uint32_t src, uint32_t dst, 169 int payload_len, uint8_t tos) 170 { 171 struct iphdr *iph = header; 172 173 iph->ihl = 5; 174 iph->version = 4; 175 iph->tos = tos; 176 iph->ttl = 8; 177 iph->tot_len = htons(sizeof(*iph) + payload_len); 178 iph->id = htons(1337); 179 iph->protocol = proto; 180 iph->saddr = src; 181 iph->daddr = dst; 182 iph->check = build_ip_csum((void *) iph, iph->ihl << 1, 0); 183 } 184 185 static void ipv6_set_dsfield(struct ipv6hdr *ip6h, uint8_t dsfield) 186 { 187 uint16_t val, *ptr = (uint16_t *)ip6h; 188 189 val = ntohs(*ptr); 190 val &= 0xF00F; 191 val |= ((uint16_t) dsfield) << 4; 192 *ptr = htons(val); 193 } 194 195 static void build_ipv6_header(void *header, uint8_t proto, 196 struct sockaddr_in6 *src, 197 struct sockaddr_in6 *dst, 198 int payload_len, uint8_t dsfield) 199 { 200 struct ipv6hdr *ip6h = header; 201 202 ip6h->version = 6; 203 ip6h->payload_len = htons(payload_len); 204 ip6h->nexthdr = proto; 205 ip6h->hop_limit = 8; 206 ipv6_set_dsfield(ip6h, dsfield); 207 208 memcpy(&ip6h->saddr, &src->sin6_addr, sizeof(ip6h->saddr)); 209 memcpy(&ip6h->daddr, &dst->sin6_addr, sizeof(ip6h->daddr)); 210 } 211 212 static uint16_t build_udp_v4_csum(const struct iphdr *iph, 213 const struct udphdr *udph, 214 int num_words) 215 { 216 unsigned long pseudo_sum; 217 int num_u16 = sizeof(iph->saddr); /* halfwords: twice byte len */ 218 219 pseudo_sum = add_csum_hword((void *) &iph->saddr, num_u16); 220 pseudo_sum += htons(IPPROTO_UDP); 221 pseudo_sum += udph->len; 222 return build_ip_csum((void *) udph, num_words, pseudo_sum); 223 } 224 225 static uint16_t build_udp_v6_csum(const struct ipv6hdr *ip6h, 226 const struct udphdr *udph, 227 int num_words) 228 { 229 unsigned long pseudo_sum; 230 int num_u16 = sizeof(ip6h->saddr); /* halfwords: twice byte len */ 231 232 pseudo_sum = add_csum_hword((void *) &ip6h->saddr, num_u16); 233 pseudo_sum += htons(ip6h->nexthdr); 234 pseudo_sum += ip6h->payload_len; 235 return build_ip_csum((void *) udph, num_words, pseudo_sum); 236 } 237 238 static void build_udp_header(void *header, int payload_len, 239 uint16_t dport, int family) 240 { 241 struct udphdr *udph = header; 242 int len = sizeof(*udph) + payload_len; 243 244 udph->source = htons(cfg_src_port); 245 udph->dest = htons(dport); 246 udph->len = htons(len); 247 udph->check = 0; 248 if (family == AF_INET) 249 udph->check = build_udp_v4_csum(header - sizeof(struct iphdr), 250 udph, len >> 1); 251 else 252 udph->check = build_udp_v6_csum(header - sizeof(struct ipv6hdr), 253 udph, len >> 1); 254 } 255 256 static void build_gue_header(void *header, uint8_t proto) 257 { 258 struct guehdr *gueh = header; 259 260 gueh->proto_ctype = proto; 261 } 262 263 static void build_gre_header(void *header, uint16_t proto) 264 { 265 struct grehdr *greh = header; 266 267 greh->protocol = htons(proto); 268 } 269 270 static int l3_length(int family) 271 { 272 if (family == AF_INET) 273 return sizeof(struct iphdr); 274 else 275 return sizeof(struct ipv6hdr); 276 } 277 278 static int build_packet(void) 279 { 280 int ol3_len = 0, ol4_len = 0, il3_len = 0, il4_len = 0; 281 int el3_len = 0; 282 283 if (cfg_l3_extra) 284 el3_len = l3_length(cfg_l3_extra); 285 286 /* calculate header offsets */ 287 if (cfg_encap_proto) { 288 ol3_len = l3_length(cfg_l3_outer); 289 290 if (cfg_encap_proto == IPPROTO_GRE) 291 ol4_len = sizeof(struct grehdr); 292 else if (cfg_encap_proto == IPPROTO_UDP) 293 ol4_len = sizeof(struct udphdr) + sizeof(struct guehdr); 294 } 295 296 il3_len = l3_length(cfg_l3_inner); 297 il4_len = sizeof(struct udphdr); 298 299 if (el3_len + ol3_len + ol4_len + il3_len + il4_len + cfg_payload_len >= 300 sizeof(buf)) 301 error(1, 0, "packet too large\n"); 302 303 /* 304 * Fill packet from inside out, to calculate correct checksums. 305 * But create ip before udp headers, as udp uses ip for pseudo-sum. 306 */ 307 memset(buf + el3_len + ol3_len + ol4_len + il3_len + il4_len, 308 cfg_payload_char, cfg_payload_len); 309 310 /* add zero byte for udp csum padding */ 311 buf[el3_len + ol3_len + ol4_len + il3_len + il4_len + cfg_payload_len] = 0; 312 313 switch (cfg_l3_inner) { 314 case PF_INET: 315 build_ipv4_header(buf + el3_len + ol3_len + ol4_len, 316 IPPROTO_UDP, 317 in_saddr4.sin_addr.s_addr, 318 in_daddr4.sin_addr.s_addr, 319 il4_len + cfg_payload_len, 320 cfg_dsfield_inner); 321 break; 322 case PF_INET6: 323 build_ipv6_header(buf + el3_len + ol3_len + ol4_len, 324 IPPROTO_UDP, 325 &in_saddr6, &in_daddr6, 326 il4_len + cfg_payload_len, 327 cfg_dsfield_inner); 328 break; 329 } 330 331 build_udp_header(buf + el3_len + ol3_len + ol4_len + il3_len, 332 cfg_payload_len, CFG_PORT_INNER, cfg_l3_inner); 333 334 if (!cfg_encap_proto) 335 return il3_len + il4_len + cfg_payload_len; 336 337 switch (cfg_l3_outer) { 338 case PF_INET: 339 build_ipv4_header(buf + el3_len, cfg_encap_proto, 340 out_saddr4.sin_addr.s_addr, 341 out_daddr4.sin_addr.s_addr, 342 ol4_len + il3_len + il4_len + cfg_payload_len, 343 cfg_dsfield_outer); 344 break; 345 case PF_INET6: 346 build_ipv6_header(buf + el3_len, cfg_encap_proto, 347 &out_saddr6, &out_daddr6, 348 ol4_len + il3_len + il4_len + cfg_payload_len, 349 cfg_dsfield_outer); 350 break; 351 } 352 353 switch (cfg_encap_proto) { 354 case IPPROTO_UDP: 355 build_gue_header(buf + el3_len + ol3_len + ol4_len - 356 sizeof(struct guehdr), 357 cfg_l3_inner == PF_INET ? IPPROTO_IPIP 358 : IPPROTO_IPV6); 359 build_udp_header(buf + el3_len + ol3_len, 360 sizeof(struct guehdr) + il3_len + il4_len + 361 cfg_payload_len, 362 cfg_port_gue, cfg_l3_outer); 363 break; 364 case IPPROTO_GRE: 365 build_gre_header(buf + el3_len + ol3_len, 366 cfg_l3_inner == PF_INET ? ETH_P_IP 367 : ETH_P_IPV6); 368 break; 369 } 370 371 switch (cfg_l3_extra) { 372 case PF_INET: 373 build_ipv4_header(buf, 374 cfg_l3_outer == PF_INET ? IPPROTO_IPIP 375 : IPPROTO_IPV6, 376 extra_saddr4.sin_addr.s_addr, 377 extra_daddr4.sin_addr.s_addr, 378 ol3_len + ol4_len + il3_len + il4_len + 379 cfg_payload_len, 0); 380 break; 381 case PF_INET6: 382 build_ipv6_header(buf, 383 cfg_l3_outer == PF_INET ? IPPROTO_IPIP 384 : IPPROTO_IPV6, 385 &extra_saddr6, &extra_daddr6, 386 ol3_len + ol4_len + il3_len + il4_len + 387 cfg_payload_len, 0); 388 break; 389 } 390 391 return el3_len + ol3_len + ol4_len + il3_len + il4_len + 392 cfg_payload_len; 393 } 394 395 /* sender transmits encapsulated over RAW or unencap'd over UDP */ 396 static int setup_tx(void) 397 { 398 int family, fd, ret; 399 400 if (cfg_l3_extra) 401 family = cfg_l3_extra; 402 else if (cfg_l3_outer) 403 family = cfg_l3_outer; 404 else 405 family = cfg_l3_inner; 406 407 fd = socket(family, SOCK_RAW, IPPROTO_RAW); 408 if (fd == -1) 409 error(1, errno, "socket tx"); 410 411 if (cfg_l3_extra) { 412 if (cfg_l3_extra == PF_INET) 413 ret = connect(fd, (void *) &extra_daddr4, 414 sizeof(extra_daddr4)); 415 else 416 ret = connect(fd, (void *) &extra_daddr6, 417 sizeof(extra_daddr6)); 418 if (ret) 419 error(1, errno, "connect tx"); 420 } else if (cfg_l3_outer) { 421 /* connect to destination if not encapsulated */ 422 if (cfg_l3_outer == PF_INET) 423 ret = connect(fd, (void *) &out_daddr4, 424 sizeof(out_daddr4)); 425 else 426 ret = connect(fd, (void *) &out_daddr6, 427 sizeof(out_daddr6)); 428 if (ret) 429 error(1, errno, "connect tx"); 430 } else { 431 /* otherwise using loopback */ 432 if (cfg_l3_inner == PF_INET) 433 ret = connect(fd, (void *) &in_daddr4, 434 sizeof(in_daddr4)); 435 else 436 ret = connect(fd, (void *) &in_daddr6, 437 sizeof(in_daddr6)); 438 if (ret) 439 error(1, errno, "connect tx"); 440 } 441 442 return fd; 443 } 444 445 /* receiver reads unencapsulated UDP */ 446 static int setup_rx(void) 447 { 448 int fd, ret; 449 450 fd = socket(cfg_l3_inner, SOCK_DGRAM, 0); 451 if (fd == -1) 452 error(1, errno, "socket rx"); 453 454 if (cfg_l3_inner == PF_INET) 455 ret = bind(fd, (void *) &in_daddr4, sizeof(in_daddr4)); 456 else 457 ret = bind(fd, (void *) &in_daddr6, sizeof(in_daddr6)); 458 if (ret) 459 error(1, errno, "bind rx"); 460 461 return fd; 462 } 463 464 static int do_tx(int fd, const char *pkt, int len) 465 { 466 int ret; 467 468 ret = write(fd, pkt, len); 469 if (ret == -1) 470 error(1, errno, "send"); 471 if (ret != len) 472 error(1, errno, "send: len (%d < %d)\n", ret, len); 473 474 return 1; 475 } 476 477 static int do_poll(int fd, short events, int timeout) 478 { 479 struct pollfd pfd; 480 int ret; 481 482 pfd.fd = fd; 483 pfd.events = events; 484 485 ret = poll(&pfd, 1, timeout); 486 if (ret == -1) 487 error(1, errno, "poll"); 488 if (ret && !(pfd.revents & POLLIN)) 489 error(1, errno, "poll: unexpected event 0x%x\n", pfd.revents); 490 491 return ret; 492 } 493 494 static int do_rx(int fd) 495 { 496 char rbuf; 497 int ret, num = 0; 498 499 while (1) { 500 ret = recv(fd, &rbuf, 1, MSG_DONTWAIT); 501 if (ret == -1 && errno == EAGAIN) 502 break; 503 if (ret == -1) 504 error(1, errno, "recv"); 505 if (rbuf != cfg_payload_char) 506 error(1, 0, "recv: payload mismatch"); 507 num++; 508 }; 509 510 return num; 511 } 512 513 static int do_main(void) 514 { 515 unsigned long tstop, treport, tcur; 516 int fdt = -1, fdr = -1, len, tx = 0, rx = 0; 517 518 if (!cfg_only_tx) 519 fdr = setup_rx(); 520 if (!cfg_only_rx) 521 fdt = setup_tx(); 522 523 len = build_packet(); 524 525 tcur = util_gettime(); 526 treport = tcur + 1000; 527 tstop = tcur + (cfg_num_secs * 1000); 528 529 while (1) { 530 if (!cfg_only_rx) 531 tx += do_tx(fdt, buf, len); 532 533 if (!cfg_only_tx) 534 rx += do_rx(fdr); 535 536 if (cfg_num_secs) { 537 tcur = util_gettime(); 538 if (tcur >= tstop) 539 break; 540 if (tcur >= treport) { 541 fprintf(stderr, "pkts: tx=%u rx=%u\n", tx, rx); 542 tx = 0; 543 rx = 0; 544 treport = tcur + 1000; 545 } 546 } else { 547 if (tx == cfg_num_pkt) 548 break; 549 } 550 } 551 552 /* read straggler packets, if any */ 553 if (rx < tx) { 554 tstop = util_gettime() + 100; 555 while (rx < tx) { 556 tcur = util_gettime(); 557 if (tcur >= tstop) 558 break; 559 560 do_poll(fdr, POLLIN, tstop - tcur); 561 rx += do_rx(fdr); 562 } 563 } 564 565 fprintf(stderr, "pkts: tx=%u rx=%u\n", tx, rx); 566 567 if (fdr != -1 && close(fdr)) 568 error(1, errno, "close rx"); 569 if (fdt != -1 && close(fdt)) 570 error(1, errno, "close tx"); 571 572 /* 573 * success (== 0) only if received all packets 574 * unless failure is expected, in which case none must arrive. 575 */ 576 if (cfg_expect_failure) 577 return rx != 0; 578 else 579 return rx != tx; 580 } 581 582 583 static void __attribute__((noreturn)) usage(const char *filepath) 584 { 585 fprintf(stderr, "Usage: %s [-e gre|gue|bare|none] [-i 4|6] [-l len] " 586 "[-O 4|6] [-o 4|6] [-n num] [-t secs] [-R] [-T] " 587 "[-s <osrc> [-d <odst>] [-S <isrc>] [-D <idst>] " 588 "[-x <otos>] [-X <itos>] [-f <isport>] [-F]\n", 589 filepath); 590 exit(1); 591 } 592 593 static void parse_addr(int family, void *addr, const char *optarg) 594 { 595 int ret; 596 597 ret = inet_pton(family, optarg, addr); 598 if (ret == -1) 599 error(1, errno, "inet_pton"); 600 if (ret == 0) 601 error(1, 0, "inet_pton: bad string"); 602 } 603 604 static void parse_addr4(struct sockaddr_in *addr, const char *optarg) 605 { 606 parse_addr(AF_INET, &addr->sin_addr, optarg); 607 } 608 609 static void parse_addr6(struct sockaddr_in6 *addr, const char *optarg) 610 { 611 parse_addr(AF_INET6, &addr->sin6_addr, optarg); 612 } 613 614 static int parse_protocol_family(const char *filepath, const char *optarg) 615 { 616 if (!strcmp(optarg, "4")) 617 return PF_INET; 618 if (!strcmp(optarg, "6")) 619 return PF_INET6; 620 621 usage(filepath); 622 } 623 624 static void parse_opts(int argc, char **argv) 625 { 626 int c; 627 628 while ((c = getopt(argc, argv, "d:D:e:f:Fhi:l:n:o:O:Rs:S:t:Tx:X:")) != -1) { 629 switch (c) { 630 case 'd': 631 if (cfg_l3_outer == AF_UNSPEC) 632 error(1, 0, "-d must be preceded by -o"); 633 if (cfg_l3_outer == AF_INET) 634 parse_addr4(&out_daddr4, optarg); 635 else 636 parse_addr6(&out_daddr6, optarg); 637 break; 638 case 'D': 639 if (cfg_l3_inner == AF_UNSPEC) 640 error(1, 0, "-D must be preceded by -i"); 641 if (cfg_l3_inner == AF_INET) 642 parse_addr4(&in_daddr4, optarg); 643 else 644 parse_addr6(&in_daddr6, optarg); 645 break; 646 case 'e': 647 if (!strcmp(optarg, "gre")) 648 cfg_encap_proto = IPPROTO_GRE; 649 else if (!strcmp(optarg, "gue")) 650 cfg_encap_proto = IPPROTO_UDP; 651 else if (!strcmp(optarg, "bare")) 652 cfg_encap_proto = IPPROTO_IPIP; 653 else if (!strcmp(optarg, "none")) 654 cfg_encap_proto = IPPROTO_IP; /* == 0 */ 655 else 656 usage(argv[0]); 657 break; 658 case 'f': 659 cfg_src_port = strtol(optarg, NULL, 0); 660 break; 661 case 'F': 662 cfg_expect_failure = true; 663 break; 664 case 'h': 665 usage(argv[0]); 666 break; 667 case 'i': 668 if (!strcmp(optarg, "4")) 669 cfg_l3_inner = PF_INET; 670 else if (!strcmp(optarg, "6")) 671 cfg_l3_inner = PF_INET6; 672 else 673 usage(argv[0]); 674 break; 675 case 'l': 676 cfg_payload_len = strtol(optarg, NULL, 0); 677 break; 678 case 'n': 679 cfg_num_pkt = strtol(optarg, NULL, 0); 680 break; 681 case 'o': 682 cfg_l3_outer = parse_protocol_family(argv[0], optarg); 683 break; 684 case 'O': 685 cfg_l3_extra = parse_protocol_family(argv[0], optarg); 686 break; 687 case 'R': 688 cfg_only_rx = true; 689 break; 690 case 's': 691 if (cfg_l3_outer == AF_INET) 692 parse_addr4(&out_saddr4, optarg); 693 else 694 parse_addr6(&out_saddr6, optarg); 695 break; 696 case 'S': 697 if (cfg_l3_inner == AF_INET) 698 parse_addr4(&in_saddr4, optarg); 699 else 700 parse_addr6(&in_saddr6, optarg); 701 break; 702 case 't': 703 cfg_num_secs = strtol(optarg, NULL, 0); 704 break; 705 case 'T': 706 cfg_only_tx = true; 707 break; 708 case 'x': 709 cfg_dsfield_outer = strtol(optarg, NULL, 0); 710 break; 711 case 'X': 712 cfg_dsfield_inner = strtol(optarg, NULL, 0); 713 break; 714 } 715 } 716 717 if (cfg_only_rx && cfg_only_tx) 718 error(1, 0, "options: cannot combine rx-only and tx-only"); 719 720 if (cfg_encap_proto && cfg_l3_outer == AF_UNSPEC) 721 error(1, 0, "options: must specify outer with encap"); 722 else if ((!cfg_encap_proto) && cfg_l3_outer != AF_UNSPEC) 723 error(1, 0, "options: cannot combine no-encap and outer"); 724 else if ((!cfg_encap_proto) && cfg_l3_extra != AF_UNSPEC) 725 error(1, 0, "options: cannot combine no-encap and extra"); 726 727 if (cfg_l3_inner == AF_UNSPEC) 728 cfg_l3_inner = AF_INET6; 729 if (cfg_l3_inner == AF_INET6 && cfg_encap_proto == IPPROTO_IPIP) 730 cfg_encap_proto = IPPROTO_IPV6; 731 732 /* RFC 6040 4.2: 733 * on decap, if outer encountered congestion (CE == 0x3), 734 * but inner cannot encode ECN (NoECT == 0x0), then drop packet. 735 */ 736 if (((cfg_dsfield_outer & 0x3) == 0x3) && 737 ((cfg_dsfield_inner & 0x3) == 0x0)) 738 cfg_expect_failure = true; 739 } 740 741 static void print_opts(void) 742 { 743 if (cfg_l3_inner == PF_INET6) { 744 util_printaddr("inner.dest6", (void *) &in_daddr6); 745 util_printaddr("inner.source6", (void *) &in_saddr6); 746 } else { 747 util_printaddr("inner.dest4", (void *) &in_daddr4); 748 util_printaddr("inner.source4", (void *) &in_saddr4); 749 } 750 751 if (!cfg_l3_outer) 752 return; 753 754 fprintf(stderr, "encap proto: %u\n", cfg_encap_proto); 755 756 if (cfg_l3_outer == PF_INET6) { 757 util_printaddr("outer.dest6", (void *) &out_daddr6); 758 util_printaddr("outer.source6", (void *) &out_saddr6); 759 } else { 760 util_printaddr("outer.dest4", (void *) &out_daddr4); 761 util_printaddr("outer.source4", (void *) &out_saddr4); 762 } 763 764 if (!cfg_l3_extra) 765 return; 766 767 if (cfg_l3_outer == PF_INET6) { 768 util_printaddr("extra.dest6", (void *) &extra_daddr6); 769 util_printaddr("extra.source6", (void *) &extra_saddr6); 770 } else { 771 util_printaddr("extra.dest4", (void *) &extra_daddr4); 772 util_printaddr("extra.source4", (void *) &extra_saddr4); 773 } 774 775 } 776 777 int main(int argc, char **argv) 778 { 779 parse_opts(argc, argv); 780 print_opts(); 781 return do_main(); 782 } 783