1/*
2 * Copyright (c) 1995, 1996
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
16 * written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22#include <sys/cdefs.h>
23#ifndef lint
24__RCSID("$NetBSD: print-dvmrp.c,v 1.8 2023/08/17 20:19:40 christos Exp $");
25#endif
26
27/* \summary: Distance Vector Multicast Routing Protocol printer */
28
29#ifdef HAVE_CONFIG_H
30#include <config.h>
31#endif
32
33#include "netdissect-stdinc.h"
34
35#include "netdissect.h"
36#include "extract.h"
37#include "addrtoname.h"
38
39/*
40 * See: RFC 1075 and draft-ietf-idmr-dvmrp-v3
41 *
42 * DVMRP message types and flag values shamelessly stolen from
43 * mrouted/dvmrp.h.
44 */
45#define DVMRP_PROBE		1	/* for finding neighbors */
46#define DVMRP_REPORT		2	/* for reporting some or all routes */
47#define DVMRP_ASK_NEIGHBORS	3	/* sent by mapper, asking for a list */
48					/* of this router's neighbors */
49#define DVMRP_NEIGHBORS		4	/* response to such a request */
50#define DVMRP_ASK_NEIGHBORS2	5	/* as above, want new format reply */
51#define DVMRP_NEIGHBORS2	6
52#define DVMRP_PRUNE		7	/* prune message */
53#define DVMRP_GRAFT		8	/* graft message */
54#define DVMRP_GRAFT_ACK		9	/* graft acknowledgement */
55static const struct tok dvmrp_msgtype_str[] = {
56	{ DVMRP_PROBE,          "Probe"              },
57	{ DVMRP_REPORT,         "Report"             },
58	{ DVMRP_ASK_NEIGHBORS,  "Ask-neighbors(old)" },
59	{ DVMRP_NEIGHBORS,      "Neighbors(old)"     },
60	{ DVMRP_ASK_NEIGHBORS2, "Ask-neighbors2"     },
61	{ DVMRP_NEIGHBORS2,     "Neighbors2"         },
62	{ DVMRP_PRUNE,          "Prune"              },
63	{ DVMRP_GRAFT,          "Graft"              },
64	{ DVMRP_GRAFT_ACK,      "Graft-ACK"          },
65	{ 0, NULL }
66};
67
68/*
69 * 'flags' byte values in DVMRP_NEIGHBORS2 reply.
70 */
71#define DVMRP_NF_TUNNEL		0x01	/* neighbors reached via tunnel */
72#define DVMRP_NF_SRCRT		0x02	/* tunnel uses IP source routing */
73#define DVMRP_NF_DOWN		0x10	/* kernel state of interface */
74#define DVMRP_NF_DISABLED	0x20	/* administratively disabled */
75#define DVMRP_NF_QUERIER	0x40	/* I am the subnet's querier */
76
77static void print_probe(netdissect_options *, const u_char *, u_int);
78static void print_report(netdissect_options *, const u_char *, u_int);
79static void print_neighbors(netdissect_options *, const u_char *, u_int);
80static void print_neighbors2(netdissect_options *, const u_char *, u_int, uint8_t, uint8_t);
81
82void
83dvmrp_print(netdissect_options *ndo,
84            const u_char *bp, u_int len)
85{
86	u_char type;
87	uint8_t major_version, minor_version;
88
89	ndo->ndo_protocol = "dvmrp";
90	if (len < 8) {
91		ND_PRINT(" [length %u < 8]", len);
92		goto invalid;
93	}
94
95	type = GET_U_1(bp + 1);
96
97	/* Skip IGMP header */
98	bp += 8;
99	len -= 8;
100
101	ND_PRINT(" %s", tok2str(dvmrp_msgtype_str, "[type %u]", type));
102	switch (type) {
103
104	case DVMRP_PROBE:
105		if (ndo->ndo_vflag) {
106			print_probe(ndo, bp, len);
107		}
108		break;
109
110	case DVMRP_REPORT:
111		if (ndo->ndo_vflag > 1) {
112			print_report(ndo, bp, len);
113		}
114		break;
115
116	case DVMRP_NEIGHBORS:
117		print_neighbors(ndo, bp, len);
118		break;
119
120	case DVMRP_NEIGHBORS2:
121		/*
122		 * extract version from IGMP group address field
123		 */
124		bp -= 4;
125		major_version = GET_U_1(bp + 3);
126		minor_version = GET_U_1(bp + 2);
127		bp += 4;
128		print_neighbors2(ndo, bp, len, major_version, minor_version);
129		break;
130
131	case DVMRP_PRUNE:
132		ND_PRINT(" src %s grp %s", GET_IPADDR_STRING(bp), GET_IPADDR_STRING(bp + 4));
133		ND_PRINT(" timer ");
134		unsigned_relts_print(ndo, GET_BE_U_4(bp + 8));
135		break;
136
137	case DVMRP_GRAFT:
138		ND_PRINT(" src %s grp %s", GET_IPADDR_STRING(bp), GET_IPADDR_STRING(bp + 4));
139		break;
140
141	case DVMRP_GRAFT_ACK:
142		ND_PRINT(" src %s grp %s", GET_IPADDR_STRING(bp), GET_IPADDR_STRING(bp + 4));
143		break;
144	}
145	return;
146
147invalid:
148	nd_print_invalid(ndo);
149}
150
151static void
152print_report(netdissect_options *ndo,
153             const u_char *bp,
154             u_int len)
155{
156	uint32_t mask, origin;
157	u_int metric, done;
158	u_int i, width;
159
160	while (len > 0) {
161		if (len < 3) {
162			ND_PRINT(" [length %u < 3]", len);
163			goto invalid;
164		}
165		mask = (uint32_t)0xff << 24 | GET_U_1(bp) << 16 |
166			GET_U_1(bp + 1) << 8 | GET_U_1(bp + 2);
167		width = 1;
168		if (GET_U_1(bp))
169			width = 2;
170		if (GET_U_1(bp + 1))
171			width = 3;
172		if (GET_U_1(bp + 2))
173			width = 4;
174
175		ND_PRINT("\n\tMask %s", intoa(htonl(mask)));
176		bp += 3;
177		len -= 3;
178		do {
179			if (len < width + 1) {
180				ND_PRINT("\n\t  [Truncated Report]");
181				goto invalid;
182			}
183			origin = 0;
184			for (i = 0; i < width; ++i) {
185				origin = origin << 8 | GET_U_1(bp);
186				bp++;
187			}
188			for ( ; i < 4; ++i)
189				origin <<= 8;
190
191			metric = GET_U_1(bp);
192			bp++;
193			done = metric & 0x80;
194			metric &= 0x7f;
195			ND_PRINT("\n\t  %s metric %u", intoa(htonl(origin)),
196				metric);
197			len -= width + 1;
198		} while (!done);
199	}
200	return;
201
202invalid:
203	nd_print_invalid(ndo);
204}
205
206static void
207print_probe(netdissect_options *ndo,
208            const u_char *bp,
209            u_int len)
210{
211	if (len < 4) {
212		ND_PRINT(" [full length %u < 4]", len);
213		goto invalid;
214	}
215	ND_PRINT(ndo->ndo_vflag > 1 ? "\n\t" : " ");
216	ND_PRINT("genid %u", GET_BE_U_4(bp));
217	if (ndo->ndo_vflag < 2)
218		return;
219
220	bp += 4;
221	len -= 4;
222	while (len > 0) {
223		if (len < 4) {
224			ND_PRINT("[remaining length %u < 4]", len);
225			goto invalid;
226		}
227		ND_PRINT("\n\tneighbor %s", GET_IPADDR_STRING(bp));
228		bp += 4; len -= 4;
229	}
230	return;
231
232invalid:
233	nd_print_invalid(ndo);
234}
235
236static void
237print_neighbors(netdissect_options *ndo,
238                const u_char *bp,
239                u_int len)
240{
241	const u_char *laddr;
242	u_char metric;
243	u_char thresh;
244	int ncount;
245
246	while (len > 0) {
247		if (len < 7) {
248			ND_PRINT(" [length %u < 7]", len);
249			goto invalid;
250		}
251		laddr = bp;
252		bp += 4;
253		metric = GET_U_1(bp);
254		bp++;
255		thresh = GET_U_1(bp);
256		bp++;
257		ncount = GET_U_1(bp);
258		bp++;
259		len -= 7;
260		while (--ncount >= 0) {
261			if (len < 4) {
262				ND_PRINT(" [length %u < 4]", len);
263				goto invalid;
264			}
265			ND_PRINT(" [%s ->", GET_IPADDR_STRING(laddr));
266			ND_PRINT(" %s, (%u/%u)]",
267				   GET_IPADDR_STRING(bp), metric, thresh);
268			bp += 4;
269			len -= 4;
270		}
271	}
272	return;
273
274invalid:
275	nd_print_invalid(ndo);
276}
277
278static void
279print_neighbors2(netdissect_options *ndo,
280                 const u_char *bp,
281                 u_int len, uint8_t major_version,
282                 uint8_t minor_version)
283{
284	const u_char *laddr;
285	u_char metric, thresh, flags;
286	int ncount;
287
288	ND_PRINT(" (v %u.%u):", major_version, minor_version);
289
290	while (len > 0) {
291		if (len < 8) {
292			ND_PRINT(" [length %u < 8]", len);
293			goto invalid;
294		}
295		laddr = bp;
296		bp += 4;
297		metric = GET_U_1(bp);
298		bp++;
299		thresh = GET_U_1(bp);
300		bp++;
301		flags = GET_U_1(bp);
302		bp++;
303		ncount = GET_U_1(bp);
304		bp++;
305		len -= 8;
306		while (--ncount >= 0 && len > 0) {
307			if (len < 4) {
308				ND_PRINT(" [length %u < 4]", len);
309				goto invalid;
310			}
311			ND_PRINT(" [%s -> ", GET_IPADDR_STRING(laddr));
312			ND_PRINT("%s (%u/%u", GET_IPADDR_STRING(bp),
313				     metric, thresh);
314			if (flags & DVMRP_NF_TUNNEL)
315				ND_PRINT("/tunnel");
316			if (flags & DVMRP_NF_SRCRT)
317				ND_PRINT("/srcrt");
318			if (flags & DVMRP_NF_QUERIER)
319				ND_PRINT("/querier");
320			if (flags & DVMRP_NF_DISABLED)
321				ND_PRINT("/disabled");
322			if (flags & DVMRP_NF_DOWN)
323				ND_PRINT("/down");
324			ND_PRINT(")]");
325			bp += 4;
326			len -= 4;
327		}
328		if (ncount != -1) {
329			ND_PRINT(" [invalid ncount]");
330			goto invalid;
331		}
332	}
333	return;
334
335invalid:
336	nd_print_invalid(ndo);
337}
338