this handles printing of NSH packets as per RFC 8300. i was trying to understand how the protocol was put together, and this helped me a lot. it looks like this:
$ ./obj/tcpdump -nr /home/dlg/tmp/nsh.pcap tcpdump: WARNING: snaplen raised from 116 to 262144 09:13:40.394208 NSH spi 777 si 7 md1: 10.0.8.3.52229 > 10.13.13.13.8000: udp 6 (DF) $ ./obj/tcpdump -nr /home/dlg/tmp/nsh.pcap -v tcpdump: WARNING: snaplen raised from 116 to 262144 09:13:40.394208 NSH spi 777 si 7 md1 context 00000001 00000002 00000003 00000004 10.0.8.3.52229 > 10.13.13.13.8000: [no udp cksum] udp 6 (DF) (ttl 64, id 10308, len 34) $ ./obj/tcpdump -nr /home/dlg/tmp/nsh-over-vxlan-gpe.pcap tcpdump: WARNING: snaplen raised from 116 to 262144 00:19:08.994912 127.0.0.1.4790 > 127.0.0.1.4790: VXLAN vni 65535: NSH spi 16777215 si 255 md2: NSH OAM (proto 0x1, len 40) (DF) $ ./obj/tcpdump -nr /home/dlg/tmp/nsh-over-vxlan-gpe.pcap -v tcpdump: WARNING: snaplen raised from 116 to 262144 00:19:08.994912 127.0.0.1.4790 > 127.0.0.1.4790: [udp sum ok] VXLAN vni 65535: NSH spi 16777215 si 255 md2 md class 1 type 2 12 md class 2 type 3 12 NSH OAM (proto 0x1, len 40) 4500 0020 d431 0000 ff11 6647 c0a8 0001 c0a8 0002 2710 4e20 000c 2178 7465 7374 0000 0000 0000 0000 (DF) (ttl 64, id 16419, len 92) ok? Index: Makefile =================================================================== RCS file: /cvs/src/usr.sbin/tcpdump/Makefile,v retrieving revision 1.63 diff -u -p -r1.63 Makefile --- Makefile 3 Feb 2018 13:39:48 -0000 1.63 +++ Makefile 2 Dec 2019 23:11:06 -0000 @@ -42,7 +42,7 @@ SRCS= tcpdump.c addrtoname.c privsep.c p print-vrrp.c print-wb.c print-decnet.c print-isoclns.c print-ipx.c \ print-atm.c print-dvmrp.c print-krb.c print-pim.c print-netbios.c \ util.c bpf_dump.c parsenfsfh.c version.c print-igrp.c \ - print-gre.c print-radius.c print-enc.c print-cnfp.c \ + print-gre.c print-nsh.c print-radius.c print-enc.c print-cnfp.c \ print-ipsec.c print-ike.c print-raw.c print-l2tp.c print-mobile.c \ print-ip6.c print-ip6opts.c print-icmp6.c print-dhcp6.c print-frag6.c \ print-bgp.c print-ospf6.c print-ripng.c print-rt6.c print-stp.c \ Index: interface.h =================================================================== RCS file: /cvs/src/usr.sbin/tcpdump/interface.h,v retrieving revision 1.82 diff -u -p -r1.82 interface.h --- interface.h 2 Dec 2019 22:07:20 -0000 1.82 +++ interface.h 2 Dec 2019 23:11:06 -0000 @@ -216,6 +216,7 @@ extern void ppp_ether_if_print(u_char *, const u_char *); extern void gre_print(const u_char *, u_int); extern void vxlan_print(const u_char *, u_int); +extern void nsh_print(const u_char *, u_int); extern void icmp_print(const u_char *, u_int, const u_char *); extern void ieee802_11_if_print(u_char *, const struct pcap_pkthdr *, const u_char *); Index: print-ether.c =================================================================== RCS file: /cvs/src/usr.sbin/tcpdump/print-ether.c,v retrieving revision 1.35 diff -u -p -r1.35 print-ether.c --- print-ether.c 18 Nov 2018 08:55:51 -0000 1.35 +++ print-ether.c 2 Dec 2019 23:11:06 -0000 @@ -289,6 +289,13 @@ recurse: } return (1); +#ifndef ETHERTYPE_NSH +#define ETHERTYPE_NSH 0x894f +#endif + case ETHERTYPE_NSH: + nsh_print(p, length); + return (1); + #ifndef ETHERTYPE_PBB #define ETHERTYPE_PBB 0x88e7 #endif Index: print-gre.c =================================================================== RCS file: /cvs/src/usr.sbin/tcpdump/print-gre.c,v retrieving revision 1.28 diff -u -p -r1.28 print-gre.c --- print-gre.c 2 Dec 2019 22:32:01 -0000 1.28 +++ print-gre.c 2 Dec 2019 23:11:06 -0000 @@ -277,6 +277,12 @@ gre_print_0(const u_char *p, u_int lengt case ETHERTYPE_TRANSETHER: ether_tryprint(p, length, 0); break; +#ifndef ETHERTYPE_NSH +#define ETHERTYPE_NSH 0x894f +#endif + case ETHERTYPE_NSH: + nsh_print(p, length); + break; case ERSPAN_II: gre_print_erspan(flags, p, length); break; @@ -758,7 +764,7 @@ vxlan_print(const u_char *p, u_int lengt ether_tryprint(p, length, 0); break; case VXLAN_PROTO_NSH: - printf("NSH"); + nsh_print(p, length); break; case VXLAN_PROTO_MPLS: mpls_print(p, length); Index: print-nsh.c =================================================================== RCS file: print-nsh.c diff -N print-nsh.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ print-nsh.c 2 Dec 2019 23:11:06 -0000 @@ -0,0 +1,308 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2019 David Gwynne <d...@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * RFC 8300 Network Service Header (NSH) + */ + +#include <sys/time.h> +#include <sys/uio.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <netinet/ip.h> +#include <arpa/inet.h> + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "interface.h" +#include "addrtoname.h" +#include "extract.h" + +#ifndef roundup +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) +#endif + +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +struct nsh_header { + uint32_t base; +#define NSH_VER_SHIFT 30 +#define NSH_VER_MASK (0x03 << NSH_VER_SHIFT) +#define NSH_VER_0 0x0 +#define NSH_VER_RESERVED (0x01 << NSH_VER_SHIFT) +#define NSH_OAM_SHIFT 29 +#define NSH_OAM_MASK (0x01 << NSH_OAM_SHIFT) +#define NSH_TTL_SHIFT 22 +#define NSH_TTL_MASK (0x3f << NSH_TTL_SHIFT) +#define NSH_LEN_SHIFT 16 +#define NSH_LEN_MASK (0x3f << NSH_LEN_SHIFT) +#define NSH_LEN_FACTOR 4 +#define NSH_MDTYPE_SHIFT 8 +#define NSH_MDTYPE_MASK (0x0f << NSH_MDTYPE_SHIFT) +#define NSH_PROTO_SHIFT 0 +#define NSH_PROTO_MASK (0xff << NSH_PROTO_SHIFT) + + uint32_t sp; +#define NSH_SPI_SHIFT 8 +#define NSH_SPI_MASK (0xffffff << NSH_SPI_SHIFT) +#define NSH_SI_SHIFT 0 +#define NSH_SI_MASK (0xff << NSH_SI_SHIFT) +}; + +#define NSH_PROTO_IPV4 0x01 +#define NSH_PROTO_IPV6 0x02 +#define NSH_PROTO_ETHERNET 0x03 +#define NSH_PROTO_NSH 0x04 +#define NSH_PROTO_MPLS 0x05 +#define NSH_PROTO_EXP1 0xfe /* Experiment 1 */ +#define NSH_PROTO_EXP2 0xff /* Experiment 2 */ + +#define NSH_MDTYPE_RESERVED 0x0 +#define NSH_MDTYPE_1 0x1 +#define NSH_MDTYPE_2 0x2 +#define NSH_MDTYPE_EXP 0xf /* Experimentation */ + +struct nsh_context_header { + uint32_t ch[4]; +}; + +struct nsh_md_header { + uint16_t class; + uint8_t type; + uint8_t len; +#define NSH_MD_LEN_MASK 0x7f +}; + +static void nsh_print_bytes(const void *, u_int); + +static void nsh_print_mdtype1(const u_char *, u_int); +static void nsh_print_mdtype2(const u_char *, u_int); + +void +nsh_print(const u_char *p, u_int length) +{ + struct nsh_header nsh; + uint32_t field, len, proto; + int l = snapend - p; + + printf("NSH"); + + if (l < sizeof(nsh)) + goto trunc; + if (length < sizeof(nsh)) { + printf(" encapsulation truncated"); + return; + } + + nsh.base = EXTRACT_32BITS(p); + nsh.sp = EXTRACT_32BITS(p + sizeof(nsh.base)); + + field = (nsh.base & NSH_VER_MASK) >> NSH_VER_SHIFT; + switch (field) { + case NSH_VER_0: + break; + case NSH_VER_RESERVED: + printf(" Reserved version"); + return; + default: + printf(" Unknown version %u", field); + return; + } + + field = (nsh.sp & NSH_SPI_MASK) >> NSH_SPI_SHIFT; + printf(" spi %u", field); + field = (nsh.sp & NSH_SI_MASK) >> NSH_SI_SHIFT; + printf(" si %u", field); + + len = ((nsh.base & NSH_LEN_MASK) >> NSH_LEN_SHIFT) * NSH_LEN_FACTOR; + if (vflag > 1) { + field = (nsh.base & NSH_TTL_MASK) >> NSH_TTL_SHIFT; + printf(" (ttl %u, len %u)", field, len); + } + + if (l < len) + goto trunc; + if (length < len) { + printf(" encapsulation truncated"); + return; + } + + p += sizeof(nsh); + l -= sizeof(nsh); + len -= sizeof(nsh); + + field = (nsh.base & NSH_MDTYPE_MASK) >> NSH_MDTYPE_SHIFT; + switch (field) { + case NSH_MDTYPE_RESERVED: + printf(" md-type-reserved"); + break; + case NSH_MDTYPE_1: + printf(" md1"); + if (vflag) + nsh_print_mdtype1(p, len); + break; + case NSH_MDTYPE_2: + printf(" md2"); + if (vflag) + nsh_print_mdtype2(p, len); + break; + case NSH_MDTYPE_EXP: + printf(" mdtype-experimentation"); + break; + default: + printf(" mdtype-unknown-0x%02x", field); + break; + } + + printf("%s", vflag ? "\n " : ": "); + + p += len; + l -= len; + length -= len; + + proto = (nsh.base & NSH_PROTO_MASK) >> NSH_PROTO_SHIFT; + + if (nsh.base & NSH_OAM_MASK) + printf("NSH OAM (proto 0x%0x, len %u)", proto, length); + else { + switch (field) { + case NSH_PROTO_IPV4: + ip_print(p, length); + return; + case NSH_PROTO_IPV6: + ip_print(p, length); + return; + case NSH_PROTO_ETHERNET: + ether_tryprint(p, length, 0); + return; + case NSH_PROTO_NSH: + nsh_print(p, length); + return; + case NSH_PROTO_MPLS: + mpls_print(p, length); + return; + case NSH_PROTO_EXP1: + printf("NSH Experiment 1"); + break; + case NSH_PROTO_EXP2: + printf("NSH Experiment 2"); + break; + default: + printf("nsh-unknown-proto-0x%02x", field); + break; + } + } + + if (vflag) + default_print_unaligned(p, length); + + return; +trunc: + printf(" [|nsh]"); +} + +static void +nsh_print_mdtype1(const u_char *p, u_int len) +{ + const struct nsh_context_header *ctx; + size_t i; + + if (len != sizeof(*ctx)) + printf("nsh-mdtype1-length-%u (not %zu)", len, sizeof(*ctx)); + + printf("\n\tcontext"); + + ctx = (const struct nsh_context_header *)p; + for (i = 0; i < nitems(ctx->ch); i++) { + printf(" "); + nsh_print_bytes(&ctx->ch[i], sizeof(ctx->ch[i])); + } +} + +static void +nsh_print_mdtype2(const u_char *p, u_int l) +{ + if (l == 0) + return; + + do { + struct nsh_md_header h; + uint8_t len; + + if (l < sizeof(h)) + goto trunc; + + memcpy(&h, p, sizeof(h)); + p += sizeof(h); + l -= sizeof(h); + + h.class = ntohs(h.class); + len = h.len & NSH_MD_LEN_MASK; + printf("\n\tmd class %u type %u", h.class, h.type); + if (len > 0) { + printf(" "); + nsh_print_bytes(p, len); + } + + len = roundup(len, 4); + if (l < len) + goto trunc; + + p += len; + l -= len; + } while (l > 0); + + return; +trunc: + printf("[|nsh md]"); +} + +static void +nsh_print_bytes(const void *b, u_int l) +{ + const uint8_t *p = b; + u_int i; + + for (i = 0; i < l; i++) { + int ch = p[i]; +#if 0 + if (isprint(ch) && !isspace(ch)) + putchar(ch); + else { + switch (ch) { + case '\\': + printf("\\\\"); + break; + case '\0': + printf("\\0"); + break; + default: + printf("\\x%02x", ch); + break; + } + } +#else + printf("%02x", ch); +#endif + } +}