1 /* $OpenBSD: print-gre.c,v 1.31 2020/04/15 20:19:25 remi 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 #ifndef ETHERTYPE_NHRP 293 #define ETHERTYPE_NHRP 0x2001 294 #endif 295 case ETHERTYPE_NHRP: 296 nhrp_print(p, length); 297 break; 298 default: 299 printf("unknown-proto-%04x", proto); 300 } 301 return; 302 303 trunc: 304 printf("[|gre]"); 305 } 306 307 void 308 gre_print_1(const u_char *p, u_int length) 309 { 310 uint16_t flags, proto; 311 int l; 312 313 l = snapend - p; 314 315 flags = EXTRACT_16BITS(p); 316 p += sizeof(flags); 317 l -= sizeof(flags); 318 length -= sizeof(flags); 319 320 if (l < sizeof(proto)) 321 goto trunc; 322 323 proto = EXTRACT_16BITS(p); 324 p += sizeof(proto); 325 l -= sizeof(proto); 326 length -= sizeof(proto); 327 328 switch (proto) { 329 case ETHERTYPE_PPP: 330 gre_print_pptp(p, length, flags); 331 break; 332 case 0x6400: 333 /* MikroTik RouterBoard Ethernet over IP (EoIP) */ 334 gre_print_eoip(p, length, flags); 335 break; 336 default: 337 printf("unknown-gre1-proto-%04x", proto); 338 break; 339 } 340 341 return; 342 343 trunc: 344 printf("[|gre1]"); 345 } 346 347 void 348 gre_print_pptp(const u_char *p, u_int length, uint16_t flags) 349 { 350 uint16_t len; 351 int l; 352 353 l = snapend - p; 354 355 printf("pptp"); 356 357 if (vflag) { 358 printf(" [%s%s%s%s%s%s]", 359 (flags & GRE_CP) ? "C" : "", 360 (flags & GRE_RP) ? "R" : "", 361 (flags & GRE_KP) ? "K" : "", 362 (flags & GRE_SP) ? "S" : "", 363 (flags & GRE_sP) ? "s" : "", 364 (flags & GRE_AP) ? "A" : ""); 365 } 366 367 if (flags & GRE_CP) { 368 printf(" cpset!"); 369 return; 370 } 371 if (flags & GRE_RP) { 372 printf(" rpset!"); 373 return; 374 } 375 if ((flags & GRE_KP) == 0) { 376 printf(" kpunset!"); 377 return; 378 } 379 if (flags & GRE_sP) { 380 printf(" spset!"); 381 return; 382 } 383 384 /* GRE_KP */ 385 if (l < sizeof(len)) 386 goto trunc; 387 len = EXTRACT_16BITS(p); 388 p += sizeof(len); 389 l -= sizeof(len); 390 length -= sizeof(len); 391 392 if (vflag) 393 printf(" len %u", EXTRACT_16BITS(p)); 394 395 if (l < 2) 396 goto trunc; 397 printf(" callid %u", EXTRACT_16BITS(p)); 398 p += 2; 399 l -= 2; 400 length -= 2; 401 402 if (flags & GRE_SP) { 403 if (l < 4) 404 goto trunc; 405 printf(" seq %u", EXTRACT_32BITS(p)); 406 p += 4; 407 l -= 4; 408 length -= 4; 409 } 410 411 if (flags & GRE_AP) { 412 if (l < 4) 413 goto trunc; 414 printf(" ack %u", EXTRACT_32BITS(p)); 415 p += 4; 416 l -= 4; 417 length -= 4; 418 } 419 420 if ((flags & GRE_SP) == 0) 421 return; 422 423 if (length < len) { 424 printf(" truncated-pptp - %d bytes missing!", 425 len - length); 426 len = length; 427 } 428 429 printf(": "); 430 431 ppp_hdlc_print(p, len); 432 return; 433 434 trunc: 435 printf("[|pptp]"); 436 } 437 438 void 439 gre_print_eoip(const u_char *p, u_int length, uint16_t flags) 440 { 441 uint16_t len, id; 442 int l; 443 444 l = snapend - p; 445 446 printf("eoip"); 447 448 flags &= ~GRE_VERS; 449 if (flags != GRE_KP) { 450 printf(" unknown-eoip-flags-%04x!", flags); 451 return; 452 } 453 454 if (l < sizeof(len)) 455 goto trunc; 456 457 len = EXTRACT_16BITS(p); 458 p += sizeof(len); 459 l -= sizeof(len); 460 length -= sizeof(len); 461 462 if (l < sizeof(id)) 463 goto trunc; 464 465 id = EXTRACT_LE_16BITS(p); 466 p += sizeof(id); 467 l -= sizeof(id); 468 length -= sizeof(id); 469 470 if (vflag) 471 printf(" len=%u tunnel-id=%u", len, id); 472 else 473 printf(" %u", id); 474 475 if (length < len) { 476 printf(" truncated-eoip - %d bytes missing!", 477 len - length); 478 len = length; 479 } 480 481 printf(": "); 482 483 if (len == 0) 484 printf("keepalive"); 485 else 486 ether_tryprint(p, len, 0); 487 488 return; 489 490 trunc: 491 printf("[|eoip]"); 492 } 493 494 #define ERSPAN2_VER_SHIFT 28 495 #define ERSPAN2_VER_MASK (0xfU << ERSPAN2_VER_SHIFT) 496 #define ERSPAN2_VER (0x1U << ERSPAN2_VER_SHIFT) 497 #define ERSPAN2_VLAN_SHIFT 16 498 #define ERSPAN2_VLAN_MASK (0xfffU << ERSPAN2_VLAN_SHIFT) 499 #define ERSPAN2_COS_SHIFT 13 500 #define ERSPAN2_COS_MASK (0x7U << ERSPAN2_COS_SHIFT) 501 #define ERSPAN2_EN_SHIFT 11 502 #define ERSPAN2_EN_MASK (0x3U << ERSPAN2_EN_SHIFT) 503 #define ERSPAN2_EN_NONE (0x0U << ERSPAN2_EN_SHIFT) 504 #define ERSPAN2_EN_ISL (0x1U << ERSPAN2_EN_SHIFT) 505 #define ERSPAN2_EN_DOT1Q (0x2U << ERSPAN2_EN_SHIFT) 506 #define ERSPAN2_EN_VLAN (0x3U << ERSPAN2_EN_SHIFT) 507 #define ERSPAN2_T_SHIFT 10 508 #define ERSPAN2_T_MASK (0x1U << ERSPAN2_T_SHIFT) 509 #define ERSPAN2_SID_SHIFT 0 510 #define ERSPAN2_SID_MASK (0x3ffU << ERSPAN2_SID_SHIFT) 511 512 #define ERSPAN2_INDEX_SHIFT 0 513 #define ERSPAN2_INDEX_MASK (0xfffffU << ERSPAN2_INDEX_SHIFT) 514 515 void 516 gre_print_erspan(uint16_t flags, const u_char *bp, u_int len) 517 { 518 uint32_t hdr, ver, vlan, cos, en, sid, index; 519 u_int l; 520 521 printf("erspan"); 522 523 if (!(flags & GRE_SP)) { 524 printf(" I: "); 525 ether_tryprint(bp, len, 0); 526 return; 527 } 528 529 l = snapend - bp; 530 if (l < sizeof(hdr)) 531 goto trunc; 532 533 hdr = EXTRACT_32BITS(bp); 534 bp += sizeof(hdr); 535 l -= sizeof(hdr); 536 len -= sizeof(hdr); 537 538 ver = hdr & ERSPAN2_VER_MASK; 539 if (ver != ERSPAN2_VER) { 540 ver >>= ERSPAN2_VER_SHIFT; 541 printf(" erspan-unknown-version-%x", ver); 542 return; 543 } 544 545 if (vflag) 546 printf(" II"); 547 548 sid = (hdr & ERSPAN2_SID_MASK) >> ERSPAN2_SID_SHIFT; 549 printf(" session %u", sid); 550 551 en = hdr & ERSPAN2_EN_MASK; 552 vlan = (hdr & ERSPAN2_VLAN_MASK) >> ERSPAN2_VLAN_SHIFT; 553 switch (en) { 554 case ERSPAN2_EN_NONE: 555 break; 556 case ERSPAN2_EN_ISL: 557 printf(" isl %u", vlan); 558 break; 559 case ERSPAN2_EN_DOT1Q: 560 printf(" vlan %u", vlan); 561 break; 562 case ERSPAN2_EN_VLAN: 563 printf(" vlan payload"); 564 break; 565 } 566 567 if (vflag) { 568 cos = (hdr & ERSPAN2_COS_MASK) >> ERSPAN2_COS_SHIFT; 569 printf(" cos %u", cos); 570 571 if (hdr & ERSPAN2_T_MASK) 572 printf(" truncated"); 573 } 574 575 if (l < sizeof(hdr)) 576 goto trunc; 577 578 hdr = EXTRACT_32BITS(bp); 579 bp += sizeof(hdr); 580 l -= sizeof(hdr); 581 len -= sizeof(hdr); 582 583 if (vflag) { 584 index = (hdr & ERSPAN2_INDEX_MASK) >> ERSPAN2_INDEX_SHIFT; 585 printf(" index %u", index); 586 } 587 588 printf(": "); 589 ether_tryprint(bp, len, 0); 590 return; 591 592 trunc: 593 printf(" [|erspan]"); 594 } 595 596 void 597 gre_sre_print(u_int16_t af, u_int8_t sreoff, u_int8_t srelen, 598 const u_char *bp, u_int len) 599 { 600 switch (af) { 601 case GRESRE_IP: 602 printf(" (rtaf=ip"); 603 gre_sre_ip_print(sreoff, srelen, bp, len); 604 printf(")"); 605 break; 606 case GRESRE_ASN: 607 printf(" (rtaf=asn"); 608 gre_sre_asn_print(sreoff, srelen, bp, len); 609 printf(")"); 610 break; 611 default: 612 printf(" (rtaf=0x%x)", af); 613 } 614 } 615 void 616 gre_sre_ip_print(u_int8_t sreoff, u_int8_t srelen, const u_char *bp, u_int len) 617 { 618 struct in_addr a; 619 const u_char *up = bp; 620 621 if (sreoff & 3) { 622 printf(" badoffset=%u", sreoff); 623 return; 624 } 625 if (srelen & 3) { 626 printf(" badlength=%u", srelen); 627 return; 628 } 629 if (sreoff >= srelen) { 630 printf(" badoff/len=%u/%u", sreoff, srelen); 631 return; 632 } 633 634 for (;;) { 635 if (len < 4 || srelen == 0) 636 return; 637 638 memcpy(&a, bp, sizeof(a)); 639 printf(" %s%s", 640 ((bp - up) == sreoff) ? "*" : "", 641 inet_ntoa(a)); 642 643 bp += 4; 644 len -= 4; 645 srelen -= 4; 646 } 647 } 648 649 void 650 gre_sre_asn_print(u_int8_t sreoff, u_int8_t srelen, const u_char *bp, u_int len) 651 { 652 const u_char *up = bp; 653 654 if (sreoff & 1) { 655 printf(" badoffset=%u", sreoff); 656 return; 657 } 658 if (srelen & 1) { 659 printf(" badlength=%u", srelen); 660 return; 661 } 662 if (sreoff >= srelen) { 663 printf(" badoff/len=%u/%u", sreoff, srelen); 664 return; 665 } 666 667 for (;;) { 668 if (len < 2 || srelen == 0) 669 return; 670 671 printf(" %s%x", 672 ((bp - up) == sreoff) ? "*" : "", 673 EXTRACT_16BITS(bp)); 674 675 bp += 2; 676 len -= 2; 677 srelen -= 2; 678 } 679 } 680 681 /* 682 * - RFC 7348 Virtual eXtensible Local Area Network (VXLAN) 683 * - draft-ietf-nvo3-vxlan-gpe-08 Generic Protocol Extension for VXLAN 684 */ 685 686 struct vxlan_header { 687 uint16_t flags; 688 #define VXLAN_VER 0x3000 /* GPE */ 689 #define VXLAN_VER_0 0x0000 690 #define VXLAN_I 0x0800 /* Instance Bit */ 691 #define VXLAN_P 0x0400 /* GPE Next Protocol */ 692 #define VXLAN_B 0x0200 /* GPE BUM Traffic */ 693 #define VXLAN_O 0x0100 /* GPE OAM Flag */ 694 uint8_t reserved; 695 uint8_t next_proto; /* GPE */ 696 #define VXLAN_PROTO_RESERVED 0x00 697 #define VXLAN_PROTO_IPV4 0x01 698 #define VXLAN_PROTO_IPV6 0x02 699 #define VXLAN_PROTO_ETHERNET 0x03 700 #define VXLAN_PROTO_NSH 0x04 701 #define VXLAN_PROTO_MPLS 0x05 702 #define VXLAN_PROTO_VBNG 0x07 703 #define VXLAN_PROTO_GBP 0x80 704 #define VXLAN_PROTO_IOAM 0x82 705 uint32_t vni; 706 #define VXLAN_VNI_SHIFT 8 707 #define VXLAN_VNI_MASK (0xffffffU << VXLAN_VNI_SHIFT) 708 #define VXLAN_VNI_RESERVED (~VXLAN_VNI_MASK) 709 }; 710 711 void 712 vxlan_print(const u_char *p, u_int length) 713 { 714 const struct vxlan_header *vh; 715 uint16_t flags, ver; 716 uint8_t proto = VXLAN_PROTO_ETHERNET; 717 int l = snapend - p; 718 719 printf("VXLAN"); 720 721 if (l < sizeof(*vh)) 722 goto trunc; 723 if (length < sizeof(*vh)) { 724 printf(" ip truncated"); 725 return; 726 } 727 728 vh = (const struct vxlan_header *)p; 729 730 p += sizeof(*vh); 731 length -= sizeof(*vh); 732 733 flags = ntohs(vh->flags); 734 ver = flags & VXLAN_VER; 735 if (ver != VXLAN_VER_0) { 736 printf(" unknown version %u", ver >> 12); 737 return; 738 } 739 740 if (flags & VXLAN_I) { 741 uint32_t vni = (htonl(vh->vni) & VXLAN_VNI_MASK) >> 742 VXLAN_VNI_SHIFT; 743 printf(" vni %u", vni >> VXLAN_VNI_SHIFT); 744 } 745 746 if (flags & VXLAN_P) 747 proto = vh->next_proto; 748 749 if (flags & VXLAN_B) 750 printf(" BUM"); 751 752 if (flags & VXLAN_O) { 753 printf(" OAM (proto 0x%x, len %u)", proto, length); 754 return; 755 } 756 757 printf(": "); 758 759 switch (proto) { 760 case VXLAN_PROTO_RESERVED: 761 printf("Reserved"); 762 break; 763 case VXLAN_PROTO_IPV4: 764 ip_print(p, length); 765 break; 766 case VXLAN_PROTO_IPV6: 767 ip6_print(p, length); 768 break; 769 case VXLAN_PROTO_ETHERNET: 770 ether_tryprint(p, length, 0); 771 break; 772 case VXLAN_PROTO_NSH: 773 nsh_print(p, length); 774 break; 775 case VXLAN_PROTO_MPLS: 776 mpls_print(p, length); 777 break; 778 779 default: 780 printf("Unassigned proto 0x%x", proto); 781 break; 782 } 783 784 return; 785 trunc: 786 printf(" [|vxlan]"); 787 } 788