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
+       }
+}

Reply via email to