]> The Tcpdump Group git mirrors - tcpdump/blob - print-nhrp.c
Add support for dissecting RFC 2332 NHRP.
[tcpdump] / print-nhrp.c
1 /* $OpenBSD: print-nhrp.c,v 1.2 2022/12/28 21:30:19 jmc Exp $ */
2
3 /*
4 * Copyright (c) 2020 Remi Locherer <remi@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 /* \summary: NHRP printer */
20
21 /*
22 * RFC 2332 NBMA Next Hop Resolution Protocol (NHRP)
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include "netdissect-stdinc.h"
30
31 #define ND_LONGJMP_FROM_TCHECK
32 #include "netdissect.h"
33 #include "addrtoname.h"
34 #include "af.h"
35 #include "ethertype.h"
36 #include "interface.h"
37 #include "extract.h"
38
39 #define NHRP_VER_RFC2332 1
40
41 #define NHRP_PKT_RESOLUTION_REQUEST 1
42 #define NHRP_PKT_RESOLUTION_REPLY 2
43 #define NHRP_PKT_REGISTRATION_REQUEST 3
44 #define NHRP_PKT_REGISTRATION_REPLY 4
45 #define NHRP_PKT_PURGE_REQUEST 5
46 #define NHRP_PKT_PURGE_REPLY 6
47 #define NHRP_PKT_ERROR_INDICATION 7
48
49 static const struct tok pkt_types[] = {
50 { NHRP_PKT_RESOLUTION_REQUEST, "res request" },
51 { NHRP_PKT_RESOLUTION_REPLY, "res reply" },
52 { NHRP_PKT_REGISTRATION_REQUEST, "reg request" },
53 { NHRP_PKT_REGISTRATION_REPLY, "reg reply" },
54 { NHRP_PKT_PURGE_REQUEST, "purge request" },
55 { NHRP_PKT_PURGE_REPLY, "purge reply" },
56 { NHRP_PKT_ERROR_INDICATION, "error indication" },
57 { 0, NULL }
58 };
59
60 /*
61 * Fixed header part.
62 */
63 struct nhrp_fixed_header {
64 nd_uint16_t afn; /* link layer address */
65 nd_uint16_t pro_type; /* protocol type (short form) */
66 nd_uint8_t pro_snap[5]; /* protocol type (long form) */
67 nd_uint8_t hopcnt; /* hop count */
68 nd_uint16_t pktsz; /* length of the NHRP packet (octets) */
69 nd_uint16_t chksum; /* IP checksum over the entier packet */
70 nd_uint16_t extoff; /* extension offset */
71 nd_uint8_t op_version; /* version of address mapping and
72 management protocol */
73 nd_uint8_t op_type; /* NHRP packet type */
74 nd_uint8_t shtl; /* type and length of src NBMA addr */
75 nd_uint8_t sstl; /* type and length of src NBMA
76 subaddress */
77 };
78
79 /*
80 * Mandatory header part. This is the beginning of the mandatory
81 * header; it's followed by addresses and client information entries.
82 *
83 * The mandatory header part formats are similar for
84 * all NHRP packets; the only difference is that NHRP_PKT_ERROR_INDICATION
85 * has a 16-bit error code and a 16-bit error packet offset rather
86 * than a 32-bit request ID.
87 */
88 struct nhrp_mand_header {
89 nd_uint8_t spl; /* src proto len */
90 nd_uint8_t dpl; /* dst proto len */
91 nd_uint16_t flags; /* flags */
92 union {
93 nd_uint32_t id; /* request id */
94 struct { /* error code */
95 nd_uint16_t code;
96 nd_uint16_t offset;
97 } err;
98 } u;
99 };
100
101 #define NHRP_FIXED_HEADER_LEN 20
102
103 struct nhrp_cie {
104 /* client information entry */
105 nd_uint8_t code;
106 nd_uint8_t plen;
107 nd_uint16_t unused;
108 nd_uint16_t mtu;
109 nd_uint16_t htime;
110 nd_uint8_t cli_addr_tl;
111 nd_uint8_t cli_saddr_tl;
112 nd_uint8_t cli_proto_tl;
113 nd_uint8_t pref;
114 };
115
116 static u_int nhrp_print_cie(netdissect_options *ndo, const u_char *, uint16_t, uint16_t, uint16_t);
117
118 /*
119 * Get string for IPv4 address pointed to by addr if addrlen is 4;
120 * otherwise, get it as a string for the sequence of hex bytes.
121 */
122 static const char *
123 nhrp_ipv4_addr_string(netdissect_options *ndo, const u_char *addr, u_int addrlen)
124 {
125 if (addrlen == 4)
126 return (GET_IPADDR_STRING(addr));
127 else
128 return (GET_LINKADDR_STRING(addr, LINKADDR_OTHER, addrlen));
129 }
130 #define NHRP_IPv4_ADDR_STRING(addr, addrlen) \
131 nhrp_ipv4_addr_string(ndo, (addr), (addrlen))
132
133 /*
134 * Get string for IPv6 address pointed to by addr if addrlen is 16;
135 * otherwise, get it as a string for the sequence of hex bytes.
136 */
137 static const char *
138 nhrp_ipv6_addr_string(netdissect_options *ndo, const u_char *addr, u_int addrlen)
139 {
140 if (addrlen == 16)
141 return (GET_IP6ADDR_STRING(addr));
142 else
143 return (GET_LINKADDR_STRING(addr, LINKADDR_OTHER, addrlen));
144 }
145 #define NHRP_IPv6_ADDR_STRING(addr, addrlen) \
146 nhrp_ipv6_addr_string(ndo, (addr), (addrlen))
147
148 /*
149 * Get string for MAC address pointed to by addr if addrlen is 6;
150 * otherwise, get it as a string for the sequence of hex bytes.
151 */
152 static const char *
153 nhrp_mac_addr_string(netdissect_options *ndo, const u_char *addr, u_int addrlen)
154 {
155 if (addrlen == 6)
156 return (GET_ETHERADDR_STRING(addr));
157 else
158 return (GET_LINKADDR_STRING(addr, LINKADDR_OTHER, addrlen));
159 }
160 #define NHRP_MAC_ADDR_STRING(addr, addrlen) \
161 nhrp_mac_addr_string(ndo, (addr), (addrlen))
162
163 void
164 nhrp_print(netdissect_options *ndo, const u_char *bp, u_int length)
165 {
166 const struct nhrp_fixed_header *fixed_hdr;
167 uint16_t afn;
168 uint16_t pro_type;
169 uint16_t pktsz;
170 uint16_t extoff;
171 uint8_t op_version;
172 uint8_t op_type;
173 uint8_t shtl, sstl;
174 const struct nhrp_mand_header *mand_hdr;
175 uint16_t mand_part_len;
176 uint8_t spl, dpl;
177
178 ndo->ndo_protocol = "nhrp";
179 nd_print_protocol_caps(ndo);
180 ND_PRINT(": ");
181
182 fixed_hdr = (const struct nhrp_fixed_header *)bp;
183
184 ND_ICHECK_ZU(length, <, sizeof(*fixed_hdr));
185 op_version = GET_U_1(fixed_hdr->op_version);
186 if (op_version != NHRP_VER_RFC2332) {
187 ND_PRINT("unknown-version-%02x", op_version);
188 return;
189 }
190
191 afn = GET_BE_U_2(fixed_hdr->afn);
192 pro_type = GET_BE_U_2(fixed_hdr->pro_type);
193
194 pktsz = GET_BE_U_2(fixed_hdr->pktsz);
195 ND_ICHECKMSG_ZU("pktsz", pktsz, <, sizeof(*fixed_hdr));
196 extoff = GET_BE_U_2(fixed_hdr->extoff);
197
198 op_type = GET_U_1(fixed_hdr->op_type);
199 ND_PRINT("%s", tok2str(pkt_types, "unknown-op-type-%04x", op_type));
200
201 /*
202 * Mandatory part length.
203 * We already know that pktsz is large enough for the fixed
204 * header and the fixed part of the mandatory heaer.
205 */
206 if (extoff == 0) {
207 mand_part_len = pktsz - sizeof(*fixed_hdr);
208 } else {
209 ND_ICHECKMSG_U("extoff", extoff, >, pktsz);
210 ND_ICHECKMSG_ZU("extoff", extoff, <, sizeof(*fixed_hdr));
211 mand_part_len = extoff - sizeof(*fixed_hdr);
212 }
213 length -= sizeof(*fixed_hdr);
214 if (mand_part_len > length)
215 mand_part_len = length;
216
217 /* We start looking at the mandatory header here. */
218 ND_TCHECK_LEN(bp, sizeof(*fixed_hdr));
219 bp += sizeof(*fixed_hdr);
220 length -= sizeof(*fixed_hdr);
221 ND_ICHECK_ZU(mand_part_len, <, sizeof(*mand_hdr));
222 ND_TCHECK_LEN(bp, sizeof(*mand_hdr));
223 mand_hdr = (const struct nhrp_mand_header *)bp;
224
225 // nhrpext = p + extoff;
226 // nhrpend = p + pktsz;
227
228 switch (op_type) {
229 case NHRP_PKT_RESOLUTION_REQUEST:
230 case NHRP_PKT_RESOLUTION_REPLY:
231 case NHRP_PKT_REGISTRATION_REQUEST:
232 case NHRP_PKT_REGISTRATION_REPLY:
233 case NHRP_PKT_PURGE_REQUEST:
234 case NHRP_PKT_PURGE_REPLY:
235 ND_PRINT(", id %u", GET_BE_U_4(mand_hdr->u.id));
236 break;
237 case NHRP_PKT_ERROR_INDICATION:
238 ND_PRINT(", error %u", GET_BE_U_2(mand_hdr->u.err.code));
239 return;
240 }
241
242 shtl = GET_U_1(fixed_hdr->shtl);
243 sstl = GET_U_1(fixed_hdr->sstl);
244
245 if (ndo->ndo_vflag) {
246 ND_PRINT(", hopcnt %u", GET_U_1(fixed_hdr->hopcnt));
247
248 /* most significant bit must be 0 */
249 if (shtl & 0x80)
250 ND_PRINT(" (shtl bit 7 set)");
251
252 /* check 2nd most significant bit */
253 if (shtl & 0x40)
254 ND_PRINT(" (nbma E.154)");
255 }
256
257 /* Mandatory header part */
258 spl = GET_U_1(mand_hdr->spl);
259 dpl = GET_U_1(mand_hdr->dpl);
260 bp += sizeof(*mand_hdr); /* Skip to the addresses */
261 mand_part_len -= sizeof(*mand_hdr);
262
263 /* Source NBMA Address, if any. */
264 if (shtl != 0) {
265 ND_ICHECK_U(mand_part_len, <, shtl);
266 switch (afn) {
267 case AFNUM_IP:
268 ND_PRINT(", src nbma %s", NHRP_IPv4_ADDR_STRING(bp, shtl));
269 break;
270 case AFNUM_IP6:
271 ND_PRINT(", src nbma %s", NHRP_IPv6_ADDR_STRING(bp, shtl));
272 break;
273 case AFNUM_802:
274 ND_PRINT(", src nbma %s", NHRP_MAC_ADDR_STRING(bp, shtl));
275 break;
276 default:
277 ND_PRINT(", unknown-nbma-addr-family-%04x (%s)",
278 afn, GET_LINKADDR_STRING(bp, LINKADDR_OTHER, shtl));
279 break;
280 }
281 bp += shtl;
282 mand_part_len -= shtl;
283 }
284
285 /* Skip the Source NBMA SubAddress, if any */
286 if (sstl != 0) {
287 ND_ICHECK_U(mand_part_len, <, sstl);
288 ND_TCHECK_LEN(bp, sstl);
289 bp += sstl;
290 mand_part_len -= sstl;
291 }
292
293 ND_PRINT(", ");
294 /* Source Protocol Address */
295 if (spl != 0) {
296 ND_ICHECK_U(mand_part_len, <, spl);
297 switch (pro_type) {
298 case ETHERTYPE_IP:
299 ND_PRINT("%s ", NHRP_IPv4_ADDR_STRING(bp, spl));
300 break;
301 case ETHERTYPE_IPV6:
302 ND_PRINT("%s ", NHRP_IPv6_ADDR_STRING(bp, spl));
303 break;
304 default:
305 ND_PRINT("proto type %04x ", pro_type);
306 ND_PRINT("%s ", GET_LINKADDR_STRING(bp, LINKADDR_OTHER, spl));
307 break;
308 }
309 bp += spl;
310 mand_part_len -= spl;
311 }
312 ND_PRINT("->");
313 /* Destination Protocol Address */
314 if (dpl != 0) {
315 ND_ICHECK_U(mand_part_len, <, dpl);
316 switch (pro_type) {
317 case ETHERTYPE_IP:
318 ND_PRINT(" %s", NHRP_IPv4_ADDR_STRING(bp, dpl));
319 break;
320 case ETHERTYPE_IPV6:
321 ND_PRINT(" %s", NHRP_IPv6_ADDR_STRING(bp, dpl));
322 break;
323 default:
324 ND_PRINT(" %s", GET_LINKADDR_STRING(bp, LINKADDR_OTHER, dpl));
325 break;
326 }
327 bp += dpl;
328 mand_part_len -= dpl;
329 }
330
331 switch (op_type) {
332 case NHRP_PKT_RESOLUTION_REQUEST:
333 case NHRP_PKT_RESOLUTION_REPLY:
334 case NHRP_PKT_REGISTRATION_REQUEST:
335 case NHRP_PKT_REGISTRATION_REPLY:
336 case NHRP_PKT_PURGE_REQUEST:
337 case NHRP_PKT_PURGE_REPLY:
338 /* Client Information Entries */
339 while (mand_part_len != 0) {
340 u_int cie_len;
341
342 cie_len = nhrp_print_cie(ndo, bp, mand_part_len,
343 afn, pro_type);
344 bp += cie_len;
345 mand_part_len -= cie_len;
346 }
347 break;
348 case NHRP_PKT_ERROR_INDICATION:
349 /* Contents of NHRP Packet in error */
350 break;
351 default:
352 break;
353 }
354 return;
355
356 invalid:
357 nd_print_invalid(ndo);
358 }
359
360 static u_int
361 nhrp_print_cie(netdissect_options *ndo, const u_char *data, uint16_t mand_part_len,
362 uint16_t afn, uint16_t pro_type)
363 {
364 const struct nhrp_cie *cie;
365 u_int cie_len;
366 uint8_t cli_addr_tl;
367 uint8_t cli_saddr_tl;
368 uint8_t cli_proto_tl;
369
370 cie = (const struct nhrp_cie *)data;
371 cie_len = 0;
372 ND_ICHECKMSG_ZU("remaining mandatory part length",
373 mand_part_len, <, sizeof(*cie));
374
375 ND_PRINT(" (code %d", GET_U_1(cie->code));
376 if (ndo->ndo_vflag)
377 ND_PRINT(", pl %d, mtu %d, htime %d, pref %d",
378 GET_U_1(cie->plen),
379 GET_BE_U_2(cie->mtu),
380 GET_BE_U_2(cie->htime),
381 GET_U_1(cie->pref));
382
383 cli_addr_tl = GET_U_1(cie->cli_addr_tl);
384 cli_saddr_tl = GET_U_1(cie->cli_saddr_tl);
385 cli_proto_tl = GET_U_1(cie->cli_proto_tl);
386
387 /* check 2nd most significant bit */
388 if (cli_addr_tl & 0x40)
389 ND_PRINT(", nbma E.154");
390
391 data += sizeof(*cie);
392 cie_len += sizeof(*cie);
393 mand_part_len -= sizeof(*cie);
394
395 if (cli_addr_tl) {
396 ND_ICHECKMSG_U("remaining mandatory part length",
397 mand_part_len, <, cli_addr_tl);
398 switch (afn) {
399 case AFNUM_IP:
400 ND_PRINT(", nbma %s", NHRP_IPv4_ADDR_STRING(data, cli_addr_tl));
401 break;
402 case AFNUM_IP6:
403 ND_PRINT(", nbma %s", NHRP_IPv6_ADDR_STRING(data, cli_addr_tl));
404 break;
405 case AFNUM_802:
406 ND_PRINT(", nbma %s", NHRP_MAC_ADDR_STRING(data, cli_addr_tl));
407 break;
408 default:
409 ND_PRINT(", unknown-nbma-addr-family-%04x (%s)",
410 afn, GET_LINKADDR_STRING(data, LINKADDR_OTHER, cli_addr_tl));
411 break;
412 }
413 data += cli_addr_tl;
414 cie_len += cli_addr_tl;
415 mand_part_len -= cli_addr_tl;
416 }
417
418 if (cli_saddr_tl) {
419 ND_ICHECKMSG_U("remaining mandatory part length",
420 mand_part_len, <, cli_addr_tl);
421 ND_PRINT(", unknown-nbma-saddr-family");
422 ND_TCHECK_LEN(data, cli_saddr_tl);
423 data += cli_saddr_tl;
424 cie_len += cli_saddr_tl;
425 mand_part_len -= cli_saddr_tl;
426 }
427
428 if (cli_proto_tl) {
429 ND_ICHECKMSG_U("remaining mandatory part length",
430 mand_part_len, <, cli_proto_tl);
431 switch (pro_type) {
432 case ETHERTYPE_IP:
433 ND_PRINT(", proto %s", NHRP_IPv4_ADDR_STRING(data, cli_proto_tl));
434 break;
435 case ETHERTYPE_IPV6:
436 ND_PRINT(", proto %s", NHRP_IPv6_ADDR_STRING(data, cli_proto_tl));
437 break;
438 default:
439 ND_PRINT(", unknown-proto-family-%04x (%s)",
440 pro_type, GET_LINKADDR_STRING(data, LINKADDR_OTHER, cli_proto_tl));
441 break;
442 }
443 cie_len += cli_proto_tl;
444 mand_part_len -= cli_proto_tl;
445 }
446
447 ND_PRINT(")");
448
449 return (cie_len);
450
451 invalid:
452 nd_print_invalid(ndo);
453 return (cie_len);
454 }