Hi,

I recently looked into NHRP (RFC 2332) and noticed that our tcpdump does
not have a printer for it. So I added support for NHRP to tcpdump.

Initially I was surprised: I expected a simpler protocol! But it is from
the 90's with all the protocols from then in mind (frame relay, ATM, ...).

I tested with public available pcap files and compared the output with
wirshark.
https://packetlife.net/captures/protocol/nhrp/
https://www.networkingwithfish.com/fun-in-the-lab-sniffer-tracing-a-dmvpn-tunnel-startup/

The output looks like this:

08:34:45.647483 172.16.25.2 > 172.16.15.2: gre NHRP: reg request, id 7 [tos 
0xc0]
08:34:45.671422 172.16.15.2 > 172.16.25.2: gre NHRP: reg reply, id 7 [tos 0xc0]

08:47:16.138679 172.16.15.2 > 172.16.25.2: gre NHRP: res request, id 6 [tos 
0xc0]
08:47:16.148863 172.16.25.2 > 172.16.15.2: gre NHRP: res reply, id 6 [tos 0xc0]

With -v set:

08:34:45.647483 172.16.25.2 > 172.16.15.2: gre [] 2001 NHRP: reg request, id 7, 
hopcnt 255, src nbma 172.16.25.2, 192.168.0.2 -> 192.168.0.1 (code 0, pl 255, 
mtu 1514, htime 7200, pref 0) [tos 0xc0] (ttl 254, id 22, len 116)
08:34:45.671422 172.16.15.2 > 172.16.25.2: gre [] 2001 NHRP: reg reply, id 7, 
hopcnt 255, src nbma 172.16.25.2, 192.168.0.2 -> 192.168.0.1 (code 0, pl 255, 
mtu 1514, htime 7200, pref 0) [tos 0xc0] (ttl 255, id 7, len 136)

08:47:16.138679 172.16.15.2 > 172.16.25.2: gre [] 2001 NHRP: res request, id 6, 
hopcnt 254, src nbma 172.16.45.2, 192.168.0.4 -> 192.168.0.2 (code 0, pl 0, mtu 
1514, htime 7200, pref 0) [tos 0xc0] (ttl 254, id 20, len 116)
08:47:16.148863 172.16.25.2 > 172.16.15.2: gre [] 2001 NHRP: res reply, id 6, 
hopcnt 255, src nbma 172.16.45.2, 192.168.0.4 -> 192.168.0.2 (code 0, pl 32, 
mtu 1514, htime 7199, pref 0, nbma 172.16.25.2, proto 192.168.0.2) [tos 0xc0] 
(ttl 255, id 31, len 144)

Extensions are not parsed and printed.

It would be nice to get pcaps with expamles that use address or protocol
combinations other than GRE and IPv4.

Comments, OKs?

Remi


Index: Makefile
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/Makefile,v
retrieving revision 1.64
diff -u -p -r1.64 Makefile
--- Makefile    3 Dec 2019 01:43:33 -0000       1.64
+++ Makefile    28 Mar 2020 17:07:22 -0000
@@ -48,7 +48,7 @@ SRCS= tcpdump.c addrtoname.c privsep.c p
        print-bgp.c print-ospf6.c print-ripng.c print-rt6.c print-stp.c \
        print-etherip.c print-lwres.c print-lldp.c print-cdp.c print-pflog.c \
        print-pfsync.c pf_print_state.c print-ofp.c ofp_map.c \
-       print-udpencap.c print-carp.c \
+       print-udpencap.c print-carp.c print-nhrp.c \
        print-802_11.c print-iapp.c print-mpls.c print-slow.c print-usbpcap.c \
        gmt2local.c savestr.c setsignal.c in_cksum.c
 
Index: interface.h
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/interface.h,v
retrieving revision 1.83
diff -u -p -r1.83 interface.h
--- interface.h 3 Dec 2019 01:43:33 -0000       1.83
+++ interface.h 28 Mar 2020 17:07:22 -0000
@@ -217,6 +217,7 @@ extern void ppp_ether_if_print(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 nhrp_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.37
diff -u -p -r1.37 print-ether.c
--- print-ether.c       24 Jan 2020 22:46:36 -0000      1.37
+++ print-ether.c       28 Mar 2020 17:07:22 -0000
@@ -303,6 +303,13 @@ recurse:
                ether_pbb_print(p, length, caplen);
                return (1);
 
+#ifndef ETHERTYPE_NHRP
+#define ETHERTYPE_NHRP 0x2001
+#endif
+       case ETHERTYPE_NHRP:
+               nhrp_print(p, length);
+               return (1);
+
 #ifdef PPP
        case ETHERTYPE_PPPOEDISC:
        case ETHERTYPE_PPPOE:
Index: print-gre.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-gre.c,v
retrieving revision 1.30
diff -u -p -r1.30 print-gre.c
--- print-gre.c 24 Jan 2020 22:46:36 -0000      1.30
+++ print-gre.c 28 Mar 2020 17:07:22 -0000
@@ -289,6 +289,12 @@ gre_print_0(const u_char *p, u_int lengt
        case 0x2000:
                cdp_print(p, length, l, 0);
                break;
+#ifndef ETHERTYPE_NHRP
+#define ETHERTYPE_NHRP 0x2001
+#endif
+       case ETHERTYPE_NHRP:
+               nhrp_print(p, length);
+               break;
        default:
                printf("unknown-proto-%04x", proto);
        }
Index: print-nhrp.c
===================================================================
RCS file: print-nhrp.c
diff -N print-nhrp.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ print-nhrp.c        13 Apr 2020 08:38:01 -0000
@@ -0,0 +1,286 @@
+/*     $OpenBSD:$ */
+
+/*
+ * Copyright (c) 2020 Remi Locherer <r...@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 2332 NBMA Next Hop Resolution Protocol (NHRP)
+ */
+
+#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 <net/ethertypes.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "addrtoname.h"
+#include "afnum.h"
+#include "interface.h"
+#include "extract.h"
+
+#define NHRP_VER_RFC2332               1
+
+#define NHRP_PKG_RESOLUTION_REQUEST    1
+#define NHRP_PKG_RESOLUTION_REPLY      2
+#define NHRP_PKG_REGISTRATION_REQUEST  3
+#define NHRP_PKG_REGISTRATION_REPLY    4
+#define NHRP_PKG_PURGE_REQUEST         5
+#define NHRP_PKG_PURGE_REPLY           6
+#define NHRP_PKG_ERROR_INDICATION      7
+
+
+struct nhrp_header {
+       /* fixed header part */
+       u_int16_t       afn;            /* link layer address */
+       u_int16_t       pro_type;       /* protocol type (short form) */
+       u_int8_t        pro_snap[5];    /* protocol type (long form) */
+       u_int8_t        hopcnt;         /* hop count */
+       u_int16_t       pktsz;          /* length of the NHRP packet (octets) */
+       u_int16_t       chksum;         /* IP checksum over the entier packet */
+       u_int16_t       extoff;         /* extension offset */
+       u_int8_t        op_version;     /* version of address mapping and
+                                          management protocol */
+       u_int8_t        op_type;        /* NHRP packet type */
+       u_int8_t        shtl;           /* type and length of src NBMA addr */
+       u_int8_t        sstl;           /* type and length of src NBMA
+                                          subaddress */
+       /* mandatory header part */
+       u_int8_t        spl;            /* src proto len */
+       u_int8_t        dpl;            /* dst proto len */
+       u_int16_t       flags;          /* flags */
+        union {
+               u_int32_t       id;     /* request id */
+               struct {                /* error code */
+                       u_int16_t       code;
+                       u_int16_t       offset;
+               } err;
+       } u;
+};
+
+struct nhrp_cie {
+       /* client information entrie */
+       u_int8_t        code;
+       u_int8_t        plen;
+       u_int16_t       unused;
+       u_int16_t       mtu;
+       u_int16_t       htime;
+       u_int8_t        cli_addr_tl;
+       u_int8_t        cli_saddr_tl;
+       u_int8_t        cli_proto_tl;
+       u_int8_t        pref;
+};
+
+static const u_char *  nhrp_print_cie(const u_char *, u_int16_t, u_int16_t);
+
+
+void
+nhrp_print(const u_char *p, u_int length)
+{
+       struct nhrp_header      *hdr;
+       const u_char            *nhrpext, *nhrpend;
+
+       printf("NHRP: ");
+
+       if ((snapend - p) < sizeof(*hdr))
+               goto trunc;
+
+       hdr = (struct nhrp_header *)p;
+
+       if (hdr->op_version != NHRP_VER_RFC2332) {
+               printf("unknown-version-%02x", hdr->op_version);
+               return;
+
+       }
+
+       nhrpext = p + EXTRACT_16BITS(&hdr->extoff);
+       nhrpend = p + EXTRACT_16BITS(&hdr->pktsz);
+
+       switch (hdr->op_type) {
+       case NHRP_PKG_RESOLUTION_REQUEST:
+               printf("res request, ");
+               break;
+       case NHRP_PKG_RESOLUTION_REPLY:
+               printf("res reply, ");
+               break;
+       case NHRP_PKG_REGISTRATION_REQUEST:
+               printf("reg request, ");
+               break;
+       case NHRP_PKG_REGISTRATION_REPLY:
+               printf("reg reply, ");
+               break;
+       case NHRP_PKG_PURGE_REQUEST:
+               printf("purge request, ");
+               break;
+       case NHRP_PKG_PURGE_REPLY:
+               printf("purge reply, ");
+               break;
+       case NHRP_PKG_ERROR_INDICATION:
+               printf("error %u", hdr->u.err.code);
+               return;
+       default:
+               printf("unknown-op-type-%04x, ", hdr->op_type);
+               break;
+       }
+
+       printf("id %u", EXTRACT_32BITS(&hdr->u.id));
+
+       if (vflag) {
+               printf(", hopcnt %u", hdr->hopcnt);
+
+               /* most significat bit must be 0 */
+               if (hdr->shtl & 0x80)
+                       printf(" (shtl bit 7 set)");
+
+               /* check 2nd most significant bit */
+               if (hdr->shtl & 0x40)
+                       printf(" (nbma E.154)");
+
+               if (hdr->shtl == 0)
+                       return;
+
+               p += sizeof(*hdr);
+               if ((snapend - p) < hdr->shtl)
+                       goto trunc;
+
+               switch (EXTRACT_16BITS(&hdr->afn)) {
+               case AFNUM_INET:
+                       printf(", src nbma %s", ipaddr_string(p));
+                       break;
+               case AFNUM_INET6:
+                       printf(", src nbma %s", ip6addr_string(p));
+                       break;
+               case AFNUM_802:
+                       printf(", src nbma %s", etheraddr_string(p));
+                       break;
+               default:
+                       printf(", unknown-nbma-addr-family-%04x",
+                           EXTRACT_16BITS(&hdr->afn));
+                       break;
+               }
+
+               p += hdr->shtl;
+               if ((snapend - p) < (hdr->spl + hdr->dpl))
+                       goto trunc;
+
+               switch (EXTRACT_16BITS(&hdr->pro_type)) {
+               case ETHERTYPE_IP:
+                       printf(", %s -> %s",
+                           ipaddr_string(p),
+                           ipaddr_string(p + hdr->spl));
+                       break;
+               case ETHERTYPE_IPV6:
+                       printf(", %s -> %s",
+                           ip6addr_string(p),
+                           ip6addr_string(p + hdr->spl));
+                       break;
+               default:
+                       printf(", proto type %04x",
+                           EXTRACT_16BITS(&hdr->pro_type));
+                       break;
+               }
+
+               p += hdr->spl + hdr->dpl;
+
+               do {
+                       p = nhrp_print_cie(p, hdr->afn, hdr->pro_type);
+                       if (p == 0)
+                               goto trunc;
+               } while ((hdr->extoff && (p < nhrpext)) ||
+                           ((!hdr->extoff && (p < nhrpend))));
+       }
+
+       return;
+
+trunc:
+       printf(" [|nhrp]");
+
+}
+
+static const u_char *
+nhrp_print_cie(const u_char *data, u_int16_t afn, u_int16_t pro_type)
+{
+       struct nhrp_cie         *cie;
+       int                      family, type;
+
+       if ((snapend - data) < sizeof(*cie))
+               return (0);
+
+       cie = (struct nhrp_cie *)data;
+
+       family = EXTRACT_16BITS(&afn);
+       type = EXTRACT_16BITS(&pro_type);
+
+       printf(" (code %d, pl %d, mtu %d, htime %d, pref %d", cie->code,
+           cie->plen, EXTRACT_16BITS(&cie->mtu), EXTRACT_16BITS(&cie->htime),
+           cie->pref);
+
+       /* check 2nd most significant bit */
+       if (cie->cli_addr_tl & 0x40)
+               printf(", nbma E.154");
+
+       data += sizeof(*cie);
+       if ((snapend - data) < cie->cli_addr_tl)
+               return (0);
+
+       if (cie->cli_addr_tl) {
+               switch (family) {
+               case AFNUM_INET:
+                       printf(", nbma %s", ipaddr_string(data));
+                       break;
+               case AFNUM_INET6:
+                       printf(", nbma %s", ip6addr_string(data));
+                       break;
+               case AFNUM_802:
+                       printf(", nbma %s", etheraddr_string(data));
+                       break;
+               default:
+                       printf(", unknown-nbma-addr-family-%04x", family);
+                       break;
+               }
+       }
+       if (cie->cli_saddr_tl)
+               printf(", unknown-nbma-saddr-family");
+
+       data += cie->cli_addr_tl + cie->cli_saddr_tl;
+       if ((snapend - data) < cie->cli_proto_tl)
+               return (0);
+
+       if (cie->cli_proto_tl) {
+               switch (type) {
+               case ETHERTYPE_IP:
+                       printf(", proto %s", ipaddr_string(data));
+                       break;
+               case ETHERTYPE_IPV6:
+                       printf(", proto %s", ip6addr_string(data));
+                       break;
+               default:
+                       printf(", unknown-proto-family-%04x", type);
+                       break;
+               }
+       }
+
+       printf(")");
+
+       return (data + cie->cli_proto_tl);
+}

Reply via email to