]> The Tcpdump Group git mirrors - tcpdump/blob - print-nhrp.c
54dc56b1d07ad7cb6a17440bcbbaa4345165f0cd
[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 * I-D draft-detienne-dmvpn-01 (expired)
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include "netdissect-stdinc.h"
31
32 #define ND_LONGJMP_FROM_TCHECK
33 #include "netdissect.h"
34 #include "addrtoname.h"
35 #include "af.h"
36 #include "ethertype.h"
37 #include "interface.h"
38 #include "extract.h"
39
40 #define NHRP_VER_RFC2332 1
41
42 #define NHRP_PKT_RESOLUTION_REQUEST 1
43 #define NHRP_PKT_RESOLUTION_REPLY 2
44 #define NHRP_PKT_REGISTRATION_REQUEST 3
45 #define NHRP_PKT_REGISTRATION_REPLY 4
46 #define NHRP_PKT_PURGE_REQUEST 5
47 #define NHRP_PKT_PURGE_REPLY 6
48 #define NHRP_PKT_ERROR_INDICATION 7
49 #define NHRP_PKT_TRAFFIC_INDICATION 8 /* draft-detienne-dmvpn-01 */
50
51 static const struct tok pkt_types[] = {
52 { NHRP_PKT_RESOLUTION_REQUEST, "res request" },
53 { NHRP_PKT_RESOLUTION_REPLY, "res reply" },
54 { NHRP_PKT_REGISTRATION_REQUEST, "reg request" },
55 { NHRP_PKT_REGISTRATION_REPLY, "reg reply" },
56 { NHRP_PKT_PURGE_REQUEST, "purge request" },
57 { NHRP_PKT_PURGE_REPLY, "purge reply" },
58 { NHRP_PKT_ERROR_INDICATION, "error indication" },
59 { NHRP_PKT_TRAFFIC_INDICATION, "traffic indication" },
60 { 0, NULL }
61 };
62
63 /*
64 * Fixed header part.
65 */
66 struct nhrp_fixed_header {
67 nd_uint16_t afn; /* link layer address */
68 nd_uint16_t pro_type; /* protocol type (short form) */
69 nd_uint8_t pro_snap[5]; /* protocol type (long form) */
70 nd_uint8_t hopcnt; /* hop count */
71 nd_uint16_t pktsz; /* length of the NHRP packet (octets) */
72 nd_uint16_t chksum; /* IP checksum over the entier packet */
73 nd_uint16_t extoff; /* extension offset */
74 nd_uint8_t op_version; /* version of address mapping and
75 management protocol */
76 nd_uint8_t op_type; /* NHRP packet type */
77 nd_uint8_t shtl; /* type and length of src NBMA addr */
78 nd_uint8_t sstl; /* type and length of src NBMA
79 subaddress */
80 };
81
82 /*
83 * Mandatory header part. This is the beginning of the mandatory
84 * header; it's followed by addresses and client information entries.
85 *
86 * The mandatory header part formats are similar for
87 * all NHRP packets; the only difference is that NHRP_PKT_ERROR_INDICATION
88 * has a 16-bit error code and a 16-bit error packet offset, and
89 * NHRP_PKT_TRAFFIC_INDICATION has a 16-bit traffic code and a 16-bit unused
90 * field, rather than a 32-bit request ID.
91 */
92 struct nhrp_mand_header {
93 nd_uint8_t spl; /* src proto len */
94 nd_uint8_t dpl; /* dst proto len */
95 nd_uint16_t flags; /* flags */
96 union {
97 nd_uint32_t id; /* request id */
98 struct { /* error code */
99 nd_uint16_t code;
100 nd_uint16_t offset;
101 } err;
102 struct { /* error code */
103 nd_uint16_t traffic_code;
104 nd_uint16_t unused;
105 } tind;
106 } u;
107 };
108
109 static const struct tok err_code_types[] = {
110 { 1, "unrecognized extension" },
111 { 3, "NHRP loop detected" },
112 { 6, "protocol address unreachable" },
113 { 7, "protocol error" },
114 { 8, "NHRP SDU size exceeded" },
115 { 9, "invalid extension" },
116 { 10, "invalid NHRP resolution reply received" },
117 { 11, "authentication failure" },
118 { 15, "hop count exceeded" },
119 { 0, NULL }
120 };
121
122 static const struct tok traffic_code_types[] = {
123 { 0, "NHRP traffic redirect/indirection" },
124 { 0, NULL }
125 };
126
127 #define NHRP_FIXED_HEADER_LEN 20
128
129 struct nhrp_cie {
130 /* client information entry */
131 nd_uint8_t code;
132 nd_uint8_t plen;
133 nd_uint16_t unused;
134 nd_uint16_t mtu;
135 nd_uint16_t htime;
136 nd_uint8_t cli_addr_tl;
137 nd_uint8_t cli_saddr_tl;
138 nd_uint8_t cli_proto_tl;
139 nd_uint8_t pref;
140 };
141
142 static u_int nhrp_print_cie(netdissect_options *ndo, const u_char *, uint16_t, uint16_t, uint16_t);
143
144 /*
145 * Get string for IPv4 address pointed to by addr if addrlen is 4;
146 * otherwise, get it as a string for the sequence of hex bytes.
147 */
148 static const char *
149 nhrp_ipv4_addr_string(netdissect_options *ndo, const u_char *addr, u_int addrlen)
150 {
151 if (addrlen == 4)
152 return (GET_IPADDR_STRING(addr));
153 else
154 return (GET_LINKADDR_STRING(addr, LINKADDR_OTHER, addrlen));
155 }
156 #define NHRP_IPv4_ADDR_STRING(addr, addrlen) \
157 nhrp_ipv4_addr_string(ndo, (addr), (addrlen))
158
159 /*
160 * Get string for IPv6 address pointed to by addr if addrlen is 16;
161 * otherwise, get it as a string for the sequence of hex bytes.
162 */
163 static const char *
164 nhrp_ipv6_addr_string(netdissect_options *ndo, const u_char *addr, u_int addrlen)
165 {
166 if (addrlen == 16)
167 return (GET_IP6ADDR_STRING(addr));
168 else
169 return (GET_LINKADDR_STRING(addr, LINKADDR_OTHER, addrlen));
170 }
171 #define NHRP_IPv6_ADDR_STRING(addr, addrlen) \
172 nhrp_ipv6_addr_string(ndo, (addr), (addrlen))
173
174 /*
175 * Get string for MAC address pointed to by addr if addrlen is 6;
176 * otherwise, get it as a string for the sequence of hex bytes.
177 */
178 static const char *
179 nhrp_mac_addr_string(netdissect_options *ndo, const u_char *addr, u_int addrlen)
180 {
181 if (addrlen == 6)
182 return (GET_ETHERADDR_STRING(addr));
183 else
184 return (GET_LINKADDR_STRING(addr, LINKADDR_OTHER, addrlen));
185 }
186 #define NHRP_MAC_ADDR_STRING(addr, addrlen) \
187 nhrp_mac_addr_string(ndo, (addr), (addrlen))
188
189 void
190 nhrp_print(netdissect_options *ndo, const u_char *bp, u_int length)
191 {
192 const struct nhrp_fixed_header *fixed_hdr;
193 uint16_t afn;
194 uint16_t pro_type;
195 uint16_t pktsz;
196 uint16_t extoff;
197 uint8_t op_version;
198 uint8_t op_type;
199 uint8_t shtl, sstl;
200 const struct nhrp_mand_header *mand_hdr;
201 uint16_t mand_part_len;
202 uint8_t spl, dpl;
203
204 ndo->ndo_protocol = "nhrp";
205 nd_print_protocol_caps(ndo);
206 ND_PRINT(": ");
207
208 fixed_hdr = (const struct nhrp_fixed_header *)bp;
209
210 ND_ICHECK_ZU(length, <, sizeof(*fixed_hdr));
211 op_version = GET_U_1(fixed_hdr->op_version);
212 if (op_version != NHRP_VER_RFC2332) {
213 ND_PRINT("unknown-version-%02x", op_version);
214 return;
215 }
216
217 afn = GET_BE_U_2(fixed_hdr->afn);
218 pro_type = GET_BE_U_2(fixed_hdr->pro_type);
219
220 pktsz = GET_BE_U_2(fixed_hdr->pktsz);
221 ND_ICHECKMSG_ZU("pktsz", pktsz, <, sizeof(*fixed_hdr));
222 extoff = GET_BE_U_2(fixed_hdr->extoff);
223
224 op_type = GET_U_1(fixed_hdr->op_type);
225 ND_PRINT("%s", tok2str(pkt_types, "unknown-op-type-%04x", op_type));
226
227 /*
228 * Mandatory part length.
229 * We already know that pktsz is large enough for the fixed
230 * header and the fixed part of the mandatory header.
231 */
232 if (extoff == 0) {
233 mand_part_len = pktsz - sizeof(*fixed_hdr);
234 } else {
235 ND_ICHECKMSG_U("extoff", extoff, >, pktsz);
236 ND_ICHECKMSG_ZU("extoff", extoff, <, sizeof(*fixed_hdr));
237 mand_part_len = extoff - sizeof(*fixed_hdr);
238 }
239 length -= sizeof(*fixed_hdr);
240 if (mand_part_len > length)
241 mand_part_len = (uint16_t)length;
242
243 /* We start looking at the mandatory header here. */
244 ND_TCHECK_LEN(bp, sizeof(*fixed_hdr));
245 bp += sizeof(*fixed_hdr);
246 length -= sizeof(*fixed_hdr);
247 ND_ICHECK_ZU(mand_part_len, <, sizeof(*mand_hdr));
248 ND_TCHECK_LEN(bp, sizeof(*mand_hdr));
249 mand_hdr = (const struct nhrp_mand_header *)bp;
250
251 switch (op_type) {
252 case NHRP_PKT_RESOLUTION_REQUEST:
253 case NHRP_PKT_RESOLUTION_REPLY:
254 case NHRP_PKT_REGISTRATION_REQUEST:
255 case NHRP_PKT_REGISTRATION_REPLY:
256 case NHRP_PKT_PURGE_REQUEST:
257 case NHRP_PKT_PURGE_REPLY:
258 ND_PRINT(", id %u", GET_BE_U_4(mand_hdr->u.id));
259 break;
260 case NHRP_PKT_ERROR_INDICATION:
261 ND_PRINT(", error <%s>", tok2str(err_code_types, "unknown-err-code-%u", GET_BE_U_2(mand_hdr->u.err.code)));
262 break;
263 case NHRP_PKT_TRAFFIC_INDICATION:
264 ND_PRINT(", code <%s>", tok2str(traffic_code_types, "unknown-traffic-code-%u", GET_BE_U_2(mand_hdr->u.tind.traffic_code)));
265 break;
266 }
267
268 shtl = GET_U_1(fixed_hdr->shtl);
269 sstl = GET_U_1(fixed_hdr->sstl);
270
271 if (ndo->ndo_vflag) {
272 ND_PRINT(", hopcnt %u", GET_U_1(fixed_hdr->hopcnt));
273
274 /* most significant bit must be 0 */
275 if (shtl & 0x80)
276 ND_PRINT(" (shtl bit 7 set)");
277
278 /* check 2nd most significant bit */
279 if (shtl & 0x40)
280 ND_PRINT(" (nbma E.154)");
281 }
282
283 /* Mandatory header part */
284 spl = GET_U_1(mand_hdr->spl);
285 dpl = GET_U_1(mand_hdr->dpl);
286 bp += sizeof(*mand_hdr); /* Skip to the addresses */
287 mand_part_len -= sizeof(*mand_hdr);
288
289 /* Source NBMA Address, if any. */
290 if (shtl != 0) {
291 ND_ICHECK_U(mand_part_len, <, shtl);
292 switch (afn) {
293 case AFNUM_IP:
294 ND_PRINT(", src nbma %s", NHRP_IPv4_ADDR_STRING(bp, shtl));
295 break;
296 case AFNUM_IP6:
297 ND_PRINT(", src nbma %s", NHRP_IPv6_ADDR_STRING(bp, shtl));
298 break;
299 case AFNUM_802:
300 ND_PRINT(", src nbma %s", NHRP_MAC_ADDR_STRING(bp, shtl));
301 break;
302 default:
303 ND_PRINT(", unknown-nbma-addr-family-%04x (%s)",
304 afn, GET_LINKADDR_STRING(bp, LINKADDR_OTHER, shtl));
305 break;
306 }
307 bp += shtl;
308 mand_part_len -= shtl;
309 }
310
311 /* Skip the Source NBMA SubAddress, if any */
312 if (sstl != 0) {
313 ND_ICHECK_U(mand_part_len, <, sstl);
314 ND_TCHECK_LEN(bp, sstl);
315 bp += sstl;
316 mand_part_len -= sstl;
317 }
318
319 ND_PRINT(", ");
320 /* Source Protocol Address */
321 if (spl != 0) {
322 ND_ICHECK_U(mand_part_len, <, spl);
323 switch (pro_type) {
324 case ETHERTYPE_IP:
325 ND_PRINT("%s ", NHRP_IPv4_ADDR_STRING(bp, spl));
326 break;
327 case ETHERTYPE_IPV6:
328 ND_PRINT("%s ", NHRP_IPv6_ADDR_STRING(bp, spl));
329 break;
330 default:
331 ND_PRINT("proto type %04x ", pro_type);
332 ND_PRINT("%s ", GET_LINKADDR_STRING(bp, LINKADDR_OTHER, spl));
333 break;
334 }
335 bp += spl;
336 mand_part_len -= spl;
337 }
338 ND_PRINT("->");
339 /* Destination Protocol Address */
340 if (dpl != 0) {
341 ND_ICHECK_U(mand_part_len, <, dpl);
342 switch (pro_type) {
343 case ETHERTYPE_IP:
344 ND_PRINT(" %s", NHRP_IPv4_ADDR_STRING(bp, dpl));
345 break;
346 case ETHERTYPE_IPV6:
347 ND_PRINT(" %s", NHRP_IPv6_ADDR_STRING(bp, dpl));
348 break;
349 default:
350 ND_PRINT(" %s", GET_LINKADDR_STRING(bp, LINKADDR_OTHER, dpl));
351 break;
352 }
353 bp += dpl;
354 mand_part_len -= dpl;
355 }
356
357 switch (op_type) {
358 case NHRP_PKT_RESOLUTION_REQUEST:
359 case NHRP_PKT_RESOLUTION_REPLY:
360 case NHRP_PKT_REGISTRATION_REQUEST:
361 case NHRP_PKT_REGISTRATION_REPLY:
362 case NHRP_PKT_PURGE_REQUEST:
363 case NHRP_PKT_PURGE_REPLY:
364 /* Client Information Entries */
365 while (mand_part_len != 0) {
366 u_int cie_len;
367
368 /*
369 * cie_len is guaranteed by nhrp_print_cie()
370 * to be <= mand_part_len.
371 */
372 cie_len = nhrp_print_cie(ndo, bp, mand_part_len,
373 afn, pro_type);
374 bp += cie_len;
375 mand_part_len -= (uint16_t)cie_len;
376 }
377 break;
378 case NHRP_PKT_ERROR_INDICATION:
379 /* Contents of NHRP Packet in error */
380 break;
381 default:
382 break;
383 }
384 return;
385
386 invalid:
387 nd_print_invalid(ndo);
388 }
389
390 static u_int
391 nhrp_print_cie(netdissect_options *ndo, const u_char *data, uint16_t mand_part_len,
392 uint16_t afn, uint16_t pro_type)
393 {
394 const struct nhrp_cie *cie;
395 u_int cie_len;
396 uint8_t cli_addr_tl;
397 uint8_t cli_saddr_tl;
398 uint8_t cli_proto_tl;
399
400 cie = (const struct nhrp_cie *)data;
401 cie_len = 0;
402 ND_ICHECKMSG_ZU("remaining mandatory part length",
403 mand_part_len, <, sizeof(*cie));
404
405 ND_PRINT(" (code %d", GET_U_1(cie->code));
406 if (ndo->ndo_vflag)
407 ND_PRINT(", pl %d, mtu %d, htime %d, pref %d",
408 GET_U_1(cie->plen),
409 GET_BE_U_2(cie->mtu),
410 GET_BE_U_2(cie->htime),
411 GET_U_1(cie->pref));
412
413 cli_addr_tl = GET_U_1(cie->cli_addr_tl);
414 cli_saddr_tl = GET_U_1(cie->cli_saddr_tl);
415 cli_proto_tl = GET_U_1(cie->cli_proto_tl);
416
417 /* check 2nd most significant bit */
418 if (cli_addr_tl & 0x40)
419 ND_PRINT(", nbma E.154");
420
421 data += sizeof(*cie);
422 cie_len += sizeof(*cie);
423 mand_part_len -= sizeof(*cie);
424
425 if (cli_addr_tl) {
426 ND_ICHECKMSG_U("remaining mandatory part length",
427 mand_part_len, <, cli_addr_tl);
428 switch (afn) {
429 case AFNUM_IP:
430 ND_PRINT(", nbma %s", NHRP_IPv4_ADDR_STRING(data, cli_addr_tl));
431 break;
432 case AFNUM_IP6:
433 ND_PRINT(", nbma %s", NHRP_IPv6_ADDR_STRING(data, cli_addr_tl));
434 break;
435 case AFNUM_802:
436 ND_PRINT(", nbma %s", NHRP_MAC_ADDR_STRING(data, cli_addr_tl));
437 break;
438 default:
439 ND_PRINT(", unknown-nbma-addr-family-%04x (%s)",
440 afn, GET_LINKADDR_STRING(data, LINKADDR_OTHER, cli_addr_tl));
441 break;
442 }
443 data += cli_addr_tl;
444 cie_len += cli_addr_tl;
445 mand_part_len -= cli_addr_tl;
446 }
447
448 if (cli_saddr_tl) {
449 ND_ICHECKMSG_U("remaining mandatory part length",
450 mand_part_len, <, cli_addr_tl);
451 ND_PRINT(", unknown-nbma-saddr-family");
452 ND_TCHECK_LEN(data, cli_saddr_tl);
453 data += cli_saddr_tl;
454 cie_len += cli_saddr_tl;
455 mand_part_len -= cli_saddr_tl;
456 }
457
458 if (cli_proto_tl) {
459 ND_ICHECKMSG_U("remaining mandatory part length",
460 mand_part_len, <, cli_proto_tl);
461 switch (pro_type) {
462 case ETHERTYPE_IP:
463 ND_PRINT(", proto %s", NHRP_IPv4_ADDR_STRING(data, cli_proto_tl));
464 break;
465 case ETHERTYPE_IPV6:
466 ND_PRINT(", proto %s", NHRP_IPv6_ADDR_STRING(data, cli_proto_tl));
467 break;
468 default:
469 ND_PRINT(", unknown-proto-family-%04x (%s)",
470 pro_type, GET_LINKADDR_STRING(data, LINKADDR_OTHER, cli_proto_tl));
471 break;
472 }
473 cie_len += cli_proto_tl;
474 mand_part_len -= cli_proto_tl;
475 }
476
477 ND_PRINT(")");
478
479 return (cie_len);
480
481 invalid:
482 nd_print_invalid(ndo);
483
484 /*
485 * We get here because this CIE goes past the remaining length,
486 * of the mandatory part. We've reported that error; we now
487 * assign the insufficiently-large remaining piece of the
488 * mandatory part to this CIE, so that this CIE finishes up
489 * the mandatory part, and the loop processing the CIEs
490 * terminates. There cannot be any CIEs after this one.
491 */
492 cie_len += mand_part_len;
493 return (cie_len);
494 }