xref: /openbsd-src/usr.sbin/tcpdump/print-gre.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
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