1 /* $OpenBSD: print-gre.c,v 1.30 2020/01/24 22:46:36 procter Exp $ */ 2 3 /* 4 * Copyright (c) 2002 Jason L. Wright (jason@thought.net) 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * tcpdump filter for GRE - Generic Routing Encapsulation 31 * RFC1701 (GRE), RFC1702 (GRE IPv4), and RFC2637 (Enhanced GRE) 32 */ 33 34 #include <sys/time.h> 35 #include <sys/uio.h> 36 #include <sys/socket.h> 37 38 #include <netinet/in.h> 39 #include <netinet/ip.h> 40 #include <arpa/inet.h> 41 42 #include <net/ethertypes.h> 43 44 #include <stdio.h> 45 #include <string.h> 46 47 #include "interface.h" 48 #include "addrtoname.h" 49 #include "extract.h" 50 51 #define GRE_CP 0x8000 /* checksum present */ 52 #define GRE_RP 0x4000 /* routing present */ 53 #define GRE_KP 0x2000 /* key present */ 54 #define GRE_SP 0x1000 /* sequence# present */ 55 #define GRE_sP 0x0800 /* source routing */ 56 #define GRE_RECRS 0x0700 /* recursion count */ 57 #define GRE_AP 0x0080 /* acknowledgment# present */ 58 #define GRE_VERS 0x0007 /* protocol version */ 59 60 /* source route entry types */ 61 #define GRESRE_IP 0x0800 /* IP */ 62 #define GRESRE_ASN 0xfffe /* ASN */ 63 64 #define NVGRE_VSID_MASK 0xffffff00U 65 #define NVGRE_VSID_SHIFT 8 66 #define NVGRE_FLOWID_MASK 0x000000ffU 67 #define NVGRE_FLOWID_SHIFT 0 68 69 #define GRE_WCCP 0x883e 70 #define ERSPAN_II 0x88be 71 #define ERSPAN_III 0x22eb 72 73 struct wccp_redirect { 74 uint8_t flags; 75 #define WCCP_D (1 << 7) 76 #define WCCP_A (1 << 6) 77 uint8_t ServiceId; 78 uint8_t AltBucket; 79 uint8_t PriBucket; 80 }; 81 82 void gre_print_0(const u_char *, u_int); 83 void gre_print_1(const u_char *, u_int); 84 void gre_print_pptp(const u_char *, u_int, uint16_t); 85 void gre_print_eoip(const u_char *, u_int, uint16_t); 86 void gre_print_erspan(uint16_t, const u_char *, u_int); 87 void gre_print_erspan3(const u_char *, u_int); 88 void gre_sre_print(u_int16_t, u_int8_t, u_int8_t, const u_char *, u_int); 89 void gre_sre_ip_print(u_int8_t, u_int8_t, const u_char *, u_int); 90 void gre_sre_asn_print(u_int8_t, u_int8_t, const u_char *, u_int); 91 92 void 93 gre_print(const u_char *p, u_int length) 94 { 95 uint16_t vers; 96 int l; 97 98 l = snapend - p; 99 100 if (l < sizeof(vers)) { 101 printf("[|gre]"); 102 return; 103 } 104 vers = EXTRACT_16BITS(p) & GRE_VERS; 105 106 switch (vers) { 107 case 0: 108 gre_print_0(p, length); 109 break; 110 case 1: 111 gre_print_1(p, length); 112 break; 113 default: 114 printf("gre-unknown-version=%u", vers); 115 break; 116 } 117 } 118 119 void 120 gre_print_0(const u_char *p, u_int length) 121 { 122 uint16_t flags, proto; 123 u_int l; 124 125 l = snapend - p; 126 127 flags = EXTRACT_16BITS(p); 128 p += sizeof(flags); 129 l -= sizeof(flags); 130 length -= sizeof(flags); 131 132 printf("gre"); 133 134 if (vflag) { 135 printf(" [%s%s%s%s%s]", 136 (flags & GRE_CP) ? "C" : "", 137 (flags & GRE_RP) ? "R" : "", 138 (flags & GRE_KP) ? "K" : "", 139 (flags & GRE_SP) ? "S" : "", 140 (flags & GRE_sP) ? "s" : ""); 141 } 142 143 if (l < sizeof(proto)) 144 goto trunc; 145 proto = EXTRACT_16BITS(p); 146 p += sizeof(proto); 147 l -= sizeof(proto); 148 length -= sizeof(proto); 149 150 if (vflag) 151 printf(" %04x", proto); 152 153 if ((flags & GRE_CP) | (flags & GRE_RP)) { 154 if (l < 2) 155 goto trunc; 156 if ((flags & GRE_CP) && vflag) 157 printf(" sum 0x%x", EXTRACT_16BITS(p)); 158 p += 2; 159 l -= 2; 160 length -= 2; 161 162 if (l < 2) 163 goto trunc; 164 if (flags & GRE_RP) 165 printf(" off 0x%x", EXTRACT_16BITS(p)); 166 p += 2; 167 l -= 2; 168 length -= 2; 169 } 170 171 if (flags & GRE_KP) { 172 uint32_t key, vsid; 173 174 if (l < sizeof(key)) 175 goto trunc; 176 key = EXTRACT_32BITS(p); 177 p += sizeof(key); 178 l -= sizeof(key); 179 length -= sizeof(key); 180 181 /* maybe NVGRE, or key entropy? */ 182 vsid = (key & NVGRE_VSID_MASK) >> NVGRE_VSID_SHIFT; 183 printf(" key=%u|%u+%02x", key, vsid, 184 (key & NVGRE_FLOWID_MASK) >> NVGRE_FLOWID_SHIFT); 185 } 186 187 if (flags & GRE_SP) { 188 if (l < 4) 189 goto trunc; 190 printf(" seq %u", EXTRACT_32BITS(p)); 191 p += 4; 192 l -= 4; 193 length -= 4; 194 } 195 196 if (flags & GRE_RP) { 197 for (;;) { 198 u_int16_t af; 199 u_int8_t sreoff; 200 u_int8_t srelen; 201 202 if (l < 4) 203 goto trunc; 204 af = EXTRACT_16BITS(p); 205 sreoff = *(p + 2); 206 srelen = *(p + 3); 207 p += 4; 208 l -= 4; 209 length -= 4; 210 211 if (af == 0 && srelen == 0) 212 break; 213 214 gre_sre_print(af, sreoff, srelen, p, l); 215 216 if (l < srelen) 217 goto trunc; 218 p += srelen; 219 l -= srelen; 220 length -= srelen; 221 } 222 } 223 224 printf(" "); 225 226 switch (packettype) { 227 case PT_ERSPAN: 228 gre_print_erspan(flags, p, length); 229 return; 230 default: 231 break; 232 } 233 234 switch (proto) { 235 case 0: 236 printf("keep-alive"); 237 break; 238 case GRE_WCCP: { 239 printf("wccp "); 240 241 if (l == 0) 242 return; 243 244 if (*p >> 4 != 4) { 245 struct wccp_redirect *wccp; 246 247 if (l < sizeof(*wccp)) { 248 printf("[|wccp]"); 249 return; 250 } 251 252 wccp = (struct wccp_redirect *)p; 253 254 printf("D:%c A:%c SId:%u Alt:%u Pri:%u", 255 (wccp->flags & WCCP_D) ? '1' : '0', 256 (wccp->flags & WCCP_A) ? '1' : '0', 257 wccp->ServiceId, wccp->AltBucket, wccp->PriBucket); 258 259 p += sizeof(*wccp); 260 l -= sizeof(*wccp); 261 262 printf(": "); 263 } 264 265 /* FALLTHROUGH */ 266 } 267 case ETHERTYPE_IP: 268 ip_print(p, length); 269 break; 270 case ETHERTYPE_IPV6: 271 ip6_print(p, length); 272 break; 273 case ETHERTYPE_MPLS: 274 case ETHERTYPE_MPLS_MCAST: 275 mpls_print(p, length); 276 break; 277 case ETHERTYPE_TRANSETHER: 278 ether_tryprint(p, length, 0); 279 break; 280 #ifndef ETHERTYPE_NSH 281 #define ETHERTYPE_NSH 0x894f 282 #endif 283 case ETHERTYPE_NSH: 284 nsh_print(p, length); 285 break; 286 case ERSPAN_II: 287 gre_print_erspan(flags, p, length); 288 break; 289 case 0x2000: 290 cdp_print(p, length, l, 0); 291 break; 292 default: 293 printf("unknown-proto-%04x", proto); 294 } 295 return; 296 297 trunc: 298 printf("[|gre]"); 299 } 300 301 void 302 gre_print_1(const u_char *p, u_int length) 303 { 304 uint16_t flags, proto; 305 int l; 306 307 l = snapend - p; 308 309 flags = EXTRACT_16BITS(p); 310 p += sizeof(flags); 311 l -= sizeof(flags); 312 length -= sizeof(flags); 313 314 if (l < sizeof(proto)) 315 goto trunc; 316 317 proto = EXTRACT_16BITS(p); 318 p += sizeof(proto); 319 l -= sizeof(proto); 320 length -= sizeof(proto); 321 322 switch (proto) { 323 case ETHERTYPE_PPP: 324 gre_print_pptp(p, length, flags); 325 break; 326 case 0x6400: 327 /* MikroTik RouterBoard Ethernet over IP (EoIP) */ 328 gre_print_eoip(p, length, flags); 329 break; 330 default: 331 printf("unknown-gre1-proto-%04x", proto); 332 break; 333 } 334 335 return; 336 337 trunc: 338 printf("[|gre1]"); 339 } 340 341 void 342 gre_print_pptp(const u_char *p, u_int length, uint16_t flags) 343 { 344 uint16_t len; 345 int l; 346 347 l = snapend - p; 348 349 printf("pptp"); 350 351 if (vflag) { 352 printf(" [%s%s%s%s%s%s]", 353 (flags & GRE_CP) ? "C" : "", 354 (flags & GRE_RP) ? "R" : "", 355 (flags & GRE_KP) ? "K" : "", 356 (flags & GRE_SP) ? "S" : "", 357 (flags & GRE_sP) ? "s" : "", 358 (flags & GRE_AP) ? "A" : ""); 359 } 360 361 if (flags & GRE_CP) { 362 printf(" cpset!"); 363 return; 364 } 365 if (flags & GRE_RP) { 366 printf(" rpset!"); 367 return; 368 } 369 if ((flags & GRE_KP) == 0) { 370 printf(" kpunset!"); 371 return; 372 } 373 if (flags & GRE_sP) { 374 printf(" spset!"); 375 return; 376 } 377 378 /* GRE_KP */ 379 if (l < sizeof(len)) 380 goto trunc; 381 len = EXTRACT_16BITS(p); 382 p += sizeof(len); 383 l -= sizeof(len); 384 length -= sizeof(len); 385 386 if (vflag) 387 printf(" len %u", EXTRACT_16BITS(p)); 388 389 if (l < 2) 390 goto trunc; 391 printf(" callid %u", EXTRACT_16BITS(p)); 392 p += 2; 393 l -= 2; 394 length -= 2; 395 396 if (flags & GRE_SP) { 397 if (l < 4) 398 goto trunc; 399 printf(" seq %u", EXTRACT_32BITS(p)); 400 p += 4; 401 l -= 4; 402 length -= 4; 403 } 404 405 if (flags & GRE_AP) { 406 if (l < 4) 407 goto trunc; 408 printf(" ack %u", EXTRACT_32BITS(p)); 409 p += 4; 410 l -= 4; 411 length -= 4; 412 } 413 414 if ((flags & GRE_SP) == 0) 415 return; 416 417 if (length < len) { 418 printf(" truncated-pptp - %d bytes missing!", 419 len - length); 420 len = length; 421 } 422 423 printf(": "); 424 425 ppp_hdlc_print(p, len); 426 return; 427 428 trunc: 429 printf("[|pptp]"); 430 } 431 432 void 433 gre_print_eoip(const u_char *p, u_int length, uint16_t flags) 434 { 435 uint16_t len, id; 436 int l; 437 438 l = snapend - p; 439 440 printf("eoip"); 441 442 flags &= ~GRE_VERS; 443 if (flags != GRE_KP) { 444 printf(" unknown-eoip-flags-%04x!", flags); 445 return; 446 } 447 448 if (l < sizeof(len)) 449 goto trunc; 450 451 len = EXTRACT_16BITS(p); 452 p += sizeof(len); 453 l -= sizeof(len); 454 length -= sizeof(len); 455 456 if (l < sizeof(id)) 457 goto trunc; 458 459 id = EXTRACT_LE_16BITS(p); 460 p += sizeof(id); 461 l -= sizeof(id); 462 length -= sizeof(id); 463 464 if (vflag) 465 printf(" len=%u tunnel-id=%u", len, id); 466 else 467 printf(" %u", id); 468 469 if (length < len) { 470 printf(" truncated-eoip - %d bytes missing!", 471 len - length); 472 len = length; 473 } 474 475 printf(": "); 476 477 if (len == 0) 478 printf("keepalive"); 479 else 480 ether_tryprint(p, len, 0); 481 482 return; 483 484 trunc: 485 printf("[|eoip]"); 486 } 487 488 #define ERSPAN2_VER_SHIFT 28 489 #define ERSPAN2_VER_MASK (0xfU << ERSPAN2_VER_SHIFT) 490 #define ERSPAN2_VER (0x1U << ERSPAN2_VER_SHIFT) 491 #define ERSPAN2_VLAN_SHIFT 16 492 #define ERSPAN2_VLAN_MASK (0xfffU << ERSPAN2_VLAN_SHIFT) 493 #define ERSPAN2_COS_SHIFT 13 494 #define ERSPAN2_COS_MASK (0x7U << ERSPAN2_COS_SHIFT) 495 #define ERSPAN2_EN_SHIFT 11 496 #define ERSPAN2_EN_MASK (0x3U << ERSPAN2_EN_SHIFT) 497 #define ERSPAN2_EN_NONE (0x0U << ERSPAN2_EN_SHIFT) 498 #define ERSPAN2_EN_ISL (0x1U << ERSPAN2_EN_SHIFT) 499 #define ERSPAN2_EN_DOT1Q (0x2U << ERSPAN2_EN_SHIFT) 500 #define ERSPAN2_EN_VLAN (0x3U << ERSPAN2_EN_SHIFT) 501 #define ERSPAN2_T_SHIFT 10 502 #define ERSPAN2_T_MASK (0x1U << ERSPAN2_T_SHIFT) 503 #define ERSPAN2_SID_SHIFT 0 504 #define ERSPAN2_SID_MASK (0x3ffU << ERSPAN2_SID_SHIFT) 505 506 #define ERSPAN2_INDEX_SHIFT 0 507 #define ERSPAN2_INDEX_MASK (0xfffffU << ERSPAN2_INDEX_SHIFT) 508 509 void 510 gre_print_erspan(uint16_t flags, const u_char *bp, u_int len) 511 { 512 uint32_t hdr, ver, vlan, cos, en, sid, index; 513 u_int l; 514 515 printf("erspan"); 516 517 if (!(flags & GRE_SP)) { 518 printf(" I: "); 519 ether_tryprint(bp, len, 0); 520 return; 521 } 522 523 l = snapend - bp; 524 if (l < sizeof(hdr)) 525 goto trunc; 526 527 hdr = EXTRACT_32BITS(bp); 528 bp += sizeof(hdr); 529 l -= sizeof(hdr); 530 len -= sizeof(hdr); 531 532 ver = hdr & ERSPAN2_VER_MASK; 533 if (ver != ERSPAN2_VER) { 534 ver >>= ERSPAN2_VER_SHIFT; 535 printf(" erspan-unknown-version-%x", ver); 536 return; 537 } 538 539 if (vflag) 540 printf(" II"); 541 542 sid = (hdr & ERSPAN2_SID_MASK) >> ERSPAN2_SID_SHIFT; 543 printf(" session %u", sid); 544 545 en = hdr & ERSPAN2_EN_MASK; 546 vlan = (hdr & ERSPAN2_VLAN_MASK) >> ERSPAN2_VLAN_SHIFT; 547 switch (en) { 548 case ERSPAN2_EN_NONE: 549 break; 550 case ERSPAN2_EN_ISL: 551 printf(" isl %u", vlan); 552 break; 553 case ERSPAN2_EN_DOT1Q: 554 printf(" vlan %u", vlan); 555 break; 556 case ERSPAN2_EN_VLAN: 557 printf(" vlan payload"); 558 break; 559 } 560 561 if (vflag) { 562 cos = (hdr & ERSPAN2_COS_MASK) >> ERSPAN2_COS_SHIFT; 563 printf(" cos %u", cos); 564 565 if (hdr & ERSPAN2_T_MASK) 566 printf(" truncated"); 567 } 568 569 if (l < sizeof(hdr)) 570 goto trunc; 571 572 hdr = EXTRACT_32BITS(bp); 573 bp += sizeof(hdr); 574 l -= sizeof(hdr); 575 len -= sizeof(hdr); 576 577 if (vflag) { 578 index = (hdr & ERSPAN2_INDEX_MASK) >> ERSPAN2_INDEX_SHIFT; 579 printf(" index %u", index); 580 } 581 582 printf(": "); 583 ether_tryprint(bp, len, 0); 584 return; 585 586 trunc: 587 printf(" [|erspan]"); 588 } 589 590 void 591 gre_sre_print(u_int16_t af, u_int8_t sreoff, u_int8_t srelen, 592 const u_char *bp, u_int len) 593 { 594 switch (af) { 595 case GRESRE_IP: 596 printf(" (rtaf=ip"); 597 gre_sre_ip_print(sreoff, srelen, bp, len); 598 printf(")"); 599 break; 600 case GRESRE_ASN: 601 printf(" (rtaf=asn"); 602 gre_sre_asn_print(sreoff, srelen, bp, len); 603 printf(")"); 604 break; 605 default: 606 printf(" (rtaf=0x%x)", af); 607 } 608 } 609 void 610 gre_sre_ip_print(u_int8_t sreoff, u_int8_t srelen, const u_char *bp, u_int len) 611 { 612 struct in_addr a; 613 const u_char *up = bp; 614 615 if (sreoff & 3) { 616 printf(" badoffset=%u", sreoff); 617 return; 618 } 619 if (srelen & 3) { 620 printf(" badlength=%u", srelen); 621 return; 622 } 623 if (sreoff >= srelen) { 624 printf(" badoff/len=%u/%u", sreoff, srelen); 625 return; 626 } 627 628 for (;;) { 629 if (len < 4 || srelen == 0) 630 return; 631 632 memcpy(&a, bp, sizeof(a)); 633 printf(" %s%s", 634 ((bp - up) == sreoff) ? "*" : "", 635 inet_ntoa(a)); 636 637 bp += 4; 638 len -= 4; 639 srelen -= 4; 640 } 641 } 642 643 void 644 gre_sre_asn_print(u_int8_t sreoff, u_int8_t srelen, const u_char *bp, u_int len) 645 { 646 const u_char *up = bp; 647 648 if (sreoff & 1) { 649 printf(" badoffset=%u", sreoff); 650 return; 651 } 652 if (srelen & 1) { 653 printf(" badlength=%u", srelen); 654 return; 655 } 656 if (sreoff >= srelen) { 657 printf(" badoff/len=%u/%u", sreoff, srelen); 658 return; 659 } 660 661 for (;;) { 662 if (len < 2 || srelen == 0) 663 return; 664 665 printf(" %s%x", 666 ((bp - up) == sreoff) ? "*" : "", 667 EXTRACT_16BITS(bp)); 668 669 bp += 2; 670 len -= 2; 671 srelen -= 2; 672 } 673 } 674 675 /* 676 * - RFC 7348 Virtual eXtensible Local Area Network (VXLAN) 677 * - draft-ietf-nvo3-vxlan-gpe-08 Generic Protocol Extension for VXLAN 678 */ 679 680 struct vxlan_header { 681 uint16_t flags; 682 #define VXLAN_VER 0x3000 /* GPE */ 683 #define VXLAN_VER_0 0x0000 684 #define VXLAN_I 0x0800 /* Instance Bit */ 685 #define VXLAN_P 0x0400 /* GPE Next Protocol */ 686 #define VXLAN_B 0x0200 /* GPE BUM Traffic */ 687 #define VXLAN_O 0x0100 /* GPE OAM Flag */ 688 uint8_t reserved; 689 uint8_t next_proto; /* GPE */ 690 #define VXLAN_PROTO_RESERVED 0x00 691 #define VXLAN_PROTO_IPV4 0x01 692 #define VXLAN_PROTO_IPV6 0x02 693 #define VXLAN_PROTO_ETHERNET 0x03 694 #define VXLAN_PROTO_NSH 0x04 695 #define VXLAN_PROTO_MPLS 0x05 696 #define VXLAN_PROTO_VBNG 0x07 697 #define VXLAN_PROTO_GBP 0x80 698 #define VXLAN_PROTO_IOAM 0x82 699 uint32_t vni; 700 #define VXLAN_VNI_SHIFT 8 701 #define VXLAN_VNI_MASK (0xffffffU << VXLAN_VNI_SHIFT) 702 #define VXLAN_VNI_RESERVED (~VXLAN_VNI_MASK) 703 }; 704 705 void 706 vxlan_print(const u_char *p, u_int length) 707 { 708 const struct vxlan_header *vh; 709 uint16_t flags, ver; 710 uint8_t proto = VXLAN_PROTO_ETHERNET; 711 int l = snapend - p; 712 713 printf("VXLAN"); 714 715 if (l < sizeof(*vh)) 716 goto trunc; 717 if (length < sizeof(*vh)) { 718 printf(" ip truncated"); 719 return; 720 } 721 722 vh = (const struct vxlan_header *)p; 723 724 p += sizeof(*vh); 725 length -= sizeof(*vh); 726 727 flags = ntohs(vh->flags); 728 ver = flags & VXLAN_VER; 729 if (ver != VXLAN_VER_0) { 730 printf(" unknown version %u", ver >> 12); 731 return; 732 } 733 734 if (flags & VXLAN_I) { 735 uint32_t vni = (htonl(vh->vni) & VXLAN_VNI_MASK) >> 736 VXLAN_VNI_SHIFT; 737 printf(" vni %u", vni >> VXLAN_VNI_SHIFT); 738 } 739 740 if (flags & VXLAN_P) 741 proto = vh->next_proto; 742 743 if (flags & VXLAN_B) 744 printf(" BUM"); 745 746 if (flags & VXLAN_O) { 747 printf(" OAM (proto 0x%x, len %u)", proto, length); 748 return; 749 } 750 751 printf(": "); 752 753 switch (proto) { 754 case VXLAN_PROTO_RESERVED: 755 printf("Reserved"); 756 break; 757 case VXLAN_PROTO_IPV4: 758 ip_print(p, length); 759 break; 760 case VXLAN_PROTO_IPV6: 761 ip6_print(p, length); 762 break; 763 case VXLAN_PROTO_ETHERNET: 764 ether_tryprint(p, length, 0); 765 break; 766 case VXLAN_PROTO_NSH: 767 nsh_print(p, length); 768 break; 769 case VXLAN_PROTO_MPLS: 770 mpls_print(p, length); 771 break; 772 773 default: 774 printf("Unassigned proto 0x%x", proto); 775 break; 776 } 777 778 return; 779 trunc: 780 printf(" [|vxlan]"); 781 } 782