/* $OpenBSD: print-nhrp.c,v 1.2 2022/12/28 21:30:19 jmc Exp $ */ /* * Copyright (c) 2020 Remi Locherer * * 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. */ /* \summary: NHRP printer */ /* * RFC 2332 NBMA Next Hop Resolution Protocol (NHRP) * I-D draft-detienne-dmvpn-01 (expired) */ #ifdef HAVE_CONFIG_H #include #endif #include "netdissect-stdinc.h" #define ND_LONGJMP_FROM_TCHECK #include "netdissect.h" #include "addrtoname.h" #include "af.h" #include "ethertype.h" #include "interface.h" #include "extract.h" #define NHRP_VER_RFC2332 1 #define NHRP_PKT_RESOLUTION_REQUEST 1 #define NHRP_PKT_RESOLUTION_REPLY 2 #define NHRP_PKT_REGISTRATION_REQUEST 3 #define NHRP_PKT_REGISTRATION_REPLY 4 #define NHRP_PKT_PURGE_REQUEST 5 #define NHRP_PKT_PURGE_REPLY 6 #define NHRP_PKT_ERROR_INDICATION 7 #define NHRP_PKT_TRAFFIC_INDICATION 8 /* draft-detienne-dmvpn-01 */ static const struct tok pkt_types[] = { { NHRP_PKT_RESOLUTION_REQUEST, "res request" }, { NHRP_PKT_RESOLUTION_REPLY, "res reply" }, { NHRP_PKT_REGISTRATION_REQUEST, "reg request" }, { NHRP_PKT_REGISTRATION_REPLY, "reg reply" }, { NHRP_PKT_PURGE_REQUEST, "purge request" }, { NHRP_PKT_PURGE_REPLY, "purge reply" }, { NHRP_PKT_ERROR_INDICATION, "error indication" }, { NHRP_PKT_TRAFFIC_INDICATION, "traffic indication" }, { 0, NULL } }; /* * Fixed header part. */ struct nhrp_fixed_header { nd_uint16_t afn; /* link layer address */ nd_uint16_t pro_type; /* protocol type (short form) */ nd_uint8_t pro_snap[5]; /* protocol type (long form) */ nd_uint8_t hopcnt; /* hop count */ nd_uint16_t pktsz; /* length of the NHRP packet (octets) */ nd_uint16_t chksum; /* IP checksum over the entier packet */ nd_uint16_t extoff; /* extension offset */ nd_uint8_t op_version; /* version of address mapping and management protocol */ nd_uint8_t op_type; /* NHRP packet type */ nd_uint8_t shtl; /* type and length of src NBMA addr */ nd_uint8_t sstl; /* type and length of src NBMA subaddress */ }; /* * Mandatory header part. This is the beginning of the mandatory * header; it's followed by addresses and client information entries. * * The mandatory header part formats are similar for * all NHRP packets; the only difference is that NHRP_PKT_ERROR_INDICATION * has a 16-bit error code and a 16-bit error packet offset, and * NHRP_PKT_TRAFFIC_INDICATION has a 16-bit traffic code and a 16-bit unused * field, rather than a 32-bit request ID. */ struct nhrp_mand_header { nd_uint8_t spl; /* src proto len */ nd_uint8_t dpl; /* dst proto len */ nd_uint16_t flags; /* flags */ union { nd_uint32_t id; /* request id */ struct { /* error code */ nd_uint16_t code; nd_uint16_t offset; } err; struct { /* error code */ nd_uint16_t traffic_code; nd_uint16_t unused; } tind; } u; }; static const struct tok err_code_types[] = { { 1, "unrecognized extension" }, { 3, "NHRP loop detected" }, { 6, "protocol address unreachable" }, { 7, "protocol error" }, { 8, "NHRP SDU size exceeded" }, { 9, "invalid extension" }, { 10, "invalid NHRP resolution reply received" }, { 11, "authentication failure" }, { 15, "hop count exceeded" }, { 0, NULL } }; static const struct tok traffic_code_types[] = { { 0, "NHRP traffic redirect/indirection" }, { 0, NULL } }; #define NHRP_FIXED_HEADER_LEN 20 struct nhrp_cie { /* client information entry */ nd_uint8_t code; nd_uint8_t plen; nd_uint16_t unused; nd_uint16_t mtu; nd_uint16_t htime; nd_uint8_t cli_addr_tl; nd_uint8_t cli_saddr_tl; nd_uint8_t cli_proto_tl; nd_uint8_t pref; }; static u_int nhrp_print_cie(netdissect_options *ndo, const u_char *, uint16_t, uint16_t, uint16_t); /* * Get string for IPv4 address pointed to by addr if addrlen is 4; * otherwise, get it as a string for the sequence of hex bytes. */ static const char * nhrp_ipv4_addr_string(netdissect_options *ndo, const u_char *addr, u_int addrlen) { if (addrlen == 4) return (GET_IPADDR_STRING(addr)); else return (GET_LINKADDR_STRING(addr, LINKADDR_OTHER, addrlen)); } #define NHRP_IPv4_ADDR_STRING(addr, addrlen) \ nhrp_ipv4_addr_string(ndo, (addr), (addrlen)) /* * Get string for IPv6 address pointed to by addr if addrlen is 16; * otherwise, get it as a string for the sequence of hex bytes. */ static const char * nhrp_ipv6_addr_string(netdissect_options *ndo, const u_char *addr, u_int addrlen) { if (addrlen == 16) return (GET_IP6ADDR_STRING(addr)); else return (GET_LINKADDR_STRING(addr, LINKADDR_OTHER, addrlen)); } #define NHRP_IPv6_ADDR_STRING(addr, addrlen) \ nhrp_ipv6_addr_string(ndo, (addr), (addrlen)) /* * Get string for MAC address pointed to by addr if addrlen is 6; * otherwise, get it as a string for the sequence of hex bytes. */ static const char * nhrp_mac_addr_string(netdissect_options *ndo, const u_char *addr, u_int addrlen) { if (addrlen == 6) return (GET_ETHERADDR_STRING(addr)); else return (GET_LINKADDR_STRING(addr, LINKADDR_OTHER, addrlen)); } #define NHRP_MAC_ADDR_STRING(addr, addrlen) \ nhrp_mac_addr_string(ndo, (addr), (addrlen)) void nhrp_print(netdissect_options *ndo, const u_char *bp, u_int length) { const struct nhrp_fixed_header *fixed_hdr; uint16_t afn; uint16_t pro_type; uint16_t pktsz; uint16_t extoff; uint8_t op_version; uint8_t op_type; uint8_t shtl, sstl; const struct nhrp_mand_header *mand_hdr; uint16_t mand_part_len; uint8_t spl, dpl; ndo->ndo_protocol = "nhrp"; nd_print_protocol_caps(ndo); ND_PRINT(": "); fixed_hdr = (const struct nhrp_fixed_header *)bp; ND_ICHECK_ZU(length, <, sizeof(*fixed_hdr)); op_version = GET_U_1(fixed_hdr->op_version); if (op_version != NHRP_VER_RFC2332) { ND_PRINT("unknown-version-%02x", op_version); return; } afn = GET_BE_U_2(fixed_hdr->afn); pro_type = GET_BE_U_2(fixed_hdr->pro_type); pktsz = GET_BE_U_2(fixed_hdr->pktsz); ND_ICHECKMSG_ZU("pktsz", pktsz, <, sizeof(*fixed_hdr)); extoff = GET_BE_U_2(fixed_hdr->extoff); op_type = GET_U_1(fixed_hdr->op_type); ND_PRINT("%s", tok2str(pkt_types, "unknown-op-type-%04x", op_type)); /* * Mandatory part length. * We already know that pktsz is large enough for the fixed * header and the fixed part of the mandatory header. */ if (extoff == 0) { mand_part_len = pktsz - sizeof(*fixed_hdr); } else { ND_ICHECKMSG_U("extoff", extoff, >, pktsz); ND_ICHECKMSG_ZU("extoff", extoff, <, sizeof(*fixed_hdr)); mand_part_len = extoff - sizeof(*fixed_hdr); } length -= sizeof(*fixed_hdr); if (mand_part_len > length) mand_part_len = (uint16_t)length; /* We start looking at the mandatory header here. */ ND_TCHECK_LEN(bp, sizeof(*fixed_hdr)); bp += sizeof(*fixed_hdr); length -= sizeof(*fixed_hdr); ND_ICHECK_ZU(mand_part_len, <, sizeof(*mand_hdr)); ND_TCHECK_LEN(bp, sizeof(*mand_hdr)); mand_hdr = (const struct nhrp_mand_header *)bp; switch (op_type) { case NHRP_PKT_RESOLUTION_REQUEST: case NHRP_PKT_RESOLUTION_REPLY: case NHRP_PKT_REGISTRATION_REQUEST: case NHRP_PKT_REGISTRATION_REPLY: case NHRP_PKT_PURGE_REQUEST: case NHRP_PKT_PURGE_REPLY: ND_PRINT(", id %u", GET_BE_U_4(mand_hdr->u.id)); break; case NHRP_PKT_ERROR_INDICATION: ND_PRINT(", error <%s>", tok2str(err_code_types, "unknown-err-code-%u", GET_BE_U_2(mand_hdr->u.err.code))); break; case NHRP_PKT_TRAFFIC_INDICATION: ND_PRINT(", code <%s>", tok2str(traffic_code_types, "unknown-traffic-code-%u", GET_BE_U_2(mand_hdr->u.tind.traffic_code))); break; } shtl = GET_U_1(fixed_hdr->shtl); sstl = GET_U_1(fixed_hdr->sstl); if (ndo->ndo_vflag) { ND_PRINT(", hopcnt %u", GET_U_1(fixed_hdr->hopcnt)); /* most significant bit must be 0 */ if (shtl & 0x80) ND_PRINT(" (shtl bit 7 set)"); /* check 2nd most significant bit */ if (shtl & 0x40) ND_PRINT(" (nbma E.154)"); } /* Mandatory header part */ spl = GET_U_1(mand_hdr->spl); dpl = GET_U_1(mand_hdr->dpl); bp += sizeof(*mand_hdr); /* Skip to the addresses */ mand_part_len -= sizeof(*mand_hdr); /* Source NBMA Address, if any. */ if (shtl != 0) { ND_ICHECK_U(mand_part_len, <, shtl); switch (afn) { case AFNUM_IP: ND_PRINT(", src nbma %s", NHRP_IPv4_ADDR_STRING(bp, shtl)); break; case AFNUM_IP6: ND_PRINT(", src nbma %s", NHRP_IPv6_ADDR_STRING(bp, shtl)); break; case AFNUM_802: ND_PRINT(", src nbma %s", NHRP_MAC_ADDR_STRING(bp, shtl)); break; default: ND_PRINT(", unknown-nbma-addr-family-%04x (%s)", afn, GET_LINKADDR_STRING(bp, LINKADDR_OTHER, shtl)); break; } bp += shtl; mand_part_len -= shtl; } /* Skip the Source NBMA SubAddress, if any */ if (sstl != 0) { ND_ICHECK_U(mand_part_len, <, sstl); ND_TCHECK_LEN(bp, sstl); bp += sstl; mand_part_len -= sstl; } ND_PRINT(", "); /* Source Protocol Address */ if (spl != 0) { ND_ICHECK_U(mand_part_len, <, spl); switch (pro_type) { case ETHERTYPE_IP: ND_PRINT("%s ", NHRP_IPv4_ADDR_STRING(bp, spl)); break; case ETHERTYPE_IPV6: ND_PRINT("%s ", NHRP_IPv6_ADDR_STRING(bp, spl)); break; default: ND_PRINT("proto type %04x ", pro_type); ND_PRINT("%s ", GET_LINKADDR_STRING(bp, LINKADDR_OTHER, spl)); break; } bp += spl; mand_part_len -= spl; } ND_PRINT("->"); /* Destination Protocol Address */ if (dpl != 0) { ND_ICHECK_U(mand_part_len, <, dpl); switch (pro_type) { case ETHERTYPE_IP: ND_PRINT(" %s", NHRP_IPv4_ADDR_STRING(bp, dpl)); break; case ETHERTYPE_IPV6: ND_PRINT(" %s", NHRP_IPv6_ADDR_STRING(bp, dpl)); break; default: ND_PRINT(" %s", GET_LINKADDR_STRING(bp, LINKADDR_OTHER, dpl)); break; } bp += dpl; mand_part_len -= dpl; } switch (op_type) { case NHRP_PKT_RESOLUTION_REQUEST: case NHRP_PKT_RESOLUTION_REPLY: case NHRP_PKT_REGISTRATION_REQUEST: case NHRP_PKT_REGISTRATION_REPLY: case NHRP_PKT_PURGE_REQUEST: case NHRP_PKT_PURGE_REPLY: /* Client Information Entries */ while (mand_part_len != 0) { u_int cie_len; /* * cie_len is guaranteed by nhrp_print_cie() * to be <= mand_part_len. */ cie_len = nhrp_print_cie(ndo, bp, mand_part_len, afn, pro_type); bp += cie_len; mand_part_len -= (uint16_t)cie_len; } break; case NHRP_PKT_ERROR_INDICATION: /* Contents of NHRP Packet in error */ break; default: break; } return; invalid: nd_print_invalid(ndo); } static u_int nhrp_print_cie(netdissect_options *ndo, const u_char *data, uint16_t mand_part_len, uint16_t afn, uint16_t pro_type) { const struct nhrp_cie *cie; u_int cie_len; uint8_t cli_addr_tl; uint8_t cli_saddr_tl; uint8_t cli_proto_tl; cie = (const struct nhrp_cie *)data; cie_len = 0; ND_ICHECKMSG_ZU("remaining mandatory part length", mand_part_len, <, sizeof(*cie)); ND_PRINT(" (code %d", GET_U_1(cie->code)); if (ndo->ndo_vflag) ND_PRINT(", pl %d, mtu %d, htime %d, pref %d", GET_U_1(cie->plen), GET_BE_U_2(cie->mtu), GET_BE_U_2(cie->htime), GET_U_1(cie->pref)); cli_addr_tl = GET_U_1(cie->cli_addr_tl); cli_saddr_tl = GET_U_1(cie->cli_saddr_tl); cli_proto_tl = GET_U_1(cie->cli_proto_tl); /* check 2nd most significant bit */ if (cli_addr_tl & 0x40) ND_PRINT(", nbma E.154"); data += sizeof(*cie); cie_len += sizeof(*cie); mand_part_len -= sizeof(*cie); if (cli_addr_tl) { ND_ICHECKMSG_U("remaining mandatory part length", mand_part_len, <, cli_addr_tl); switch (afn) { case AFNUM_IP: ND_PRINT(", nbma %s", NHRP_IPv4_ADDR_STRING(data, cli_addr_tl)); break; case AFNUM_IP6: ND_PRINT(", nbma %s", NHRP_IPv6_ADDR_STRING(data, cli_addr_tl)); break; case AFNUM_802: ND_PRINT(", nbma %s", NHRP_MAC_ADDR_STRING(data, cli_addr_tl)); break; default: ND_PRINT(", unknown-nbma-addr-family-%04x (%s)", afn, GET_LINKADDR_STRING(data, LINKADDR_OTHER, cli_addr_tl)); break; } data += cli_addr_tl; cie_len += cli_addr_tl; mand_part_len -= cli_addr_tl; } if (cli_saddr_tl) { ND_ICHECKMSG_U("remaining mandatory part length", mand_part_len, <, cli_addr_tl); ND_PRINT(", unknown-nbma-saddr-family"); ND_TCHECK_LEN(data, cli_saddr_tl); data += cli_saddr_tl; cie_len += cli_saddr_tl; mand_part_len -= cli_saddr_tl; } if (cli_proto_tl) { ND_ICHECKMSG_U("remaining mandatory part length", mand_part_len, <, cli_proto_tl); switch (pro_type) { case ETHERTYPE_IP: ND_PRINT(", proto %s", NHRP_IPv4_ADDR_STRING(data, cli_proto_tl)); break; case ETHERTYPE_IPV6: ND_PRINT(", proto %s", NHRP_IPv6_ADDR_STRING(data, cli_proto_tl)); break; default: ND_PRINT(", unknown-proto-family-%04x (%s)", pro_type, GET_LINKADDR_STRING(data, LINKADDR_OTHER, cli_proto_tl)); break; } cie_len += cli_proto_tl; mand_part_len -= cli_proto_tl; } ND_PRINT(")"); return (cie_len); invalid: nd_print_invalid(ndo); /* * We get here because this CIE goes past the remaining length, * of the mandatory part. We've reported that error; we now * assign the insufficiently-large remaining piece of the * mandatory part to this CIE, so that this CIE finishes up * the mandatory part, and the loop processing the CIEs * terminates. There cannot be any CIEs after this one. */ cie_len += mand_part_len; return (cie_len); }