1 /* packet-dvmrp.c   2001 Ronnie Sahlberg <See AUTHORS for email>
2  * Routines for IGMP/DVMRP packet disassembly
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  */
10 /*
11 
12 
13 			DVMRP	DVMRP
14 	code		v1	v3
15 
16 	0x01		*	*
17 	0x02		*	*
18 	0x03		x
19 	0x04		x
20 	0x07			x
21 	0x08			x
22 	0x09			x
23 
24 
25 	* V3 has len>=8 and byte[6]==0xff and byte[7]==0x03
26 
27 
28 	DVMRP is defined in the following RFCs
29 	RFC1075 Version 1
30 	draft-ietf-idmr-dvmrp-v3-10.txt Version 3
31 
32 	V1 and V3 can be distinguished by looking at bytes 6 and 7 in the
33 	IGMP/DVMRP header.
34 	If header[6]==0xff and header[7]==0x03 we have version 3.
35 
36 
37 	RFC1075 has typos in 3.12.2 and 3.12.4, see if you can spot them.
38 */
39 
40 #include "config.h"
41 
42 #include <epan/packet.h>
43 #include <epan/prefs.h>
44 #include <epan/expert.h>
45 #include "packet-igmp.h"
46 
47 void proto_register_dvmrp(void);
48 void proto_reg_handoff_dvmrp(void);
49 
50 static int proto_dvmrp = -1;
51 static int hf_version = -1;
52 static int hf_type = -1;
53 static int hf_code_v1 = -1;
54 static int hf_checksum = -1;
55 static int hf_checksum_status = -1;
56 static int hf_reserved = -1;
57 static int hf_commands = -1;
58 static int hf_command = -1;
59 static int hf_count = -1;
60 static int hf_afi = -1;
61 static int hf_netmask = -1;
62 static int hf_metric = -1;
63 static int hf_dest_unr = -1;
64 static int hf_split_horiz = -1;
65 static int hf_infinity = -1;
66 static int hf_daddr = -1;
67 static int hf_maddr = -1;
68 static int hf_hold = -1;
69 static int hf_code_v3 = -1;
70 static int hf_capabilities = -1;
71 static int hf_cap_leaf = -1;
72 static int hf_cap_prune = -1;
73 static int hf_cap_genid = -1;
74 static int hf_cap_mtrace = -1;
75 static int hf_cap_snmp = -1;
76 static int hf_cap_netmask = -1;
77 static int hf_min_ver = -1;
78 static int hf_maj_ver = -1;
79 static int hf_genid = -1;
80 static int hf_route = -1;
81 static int hf_saddr = -1;
82 static int hf_life = -1;
83 static int hf_local = -1;
84 static int hf_threshold = -1;
85 static int hf_flags = -1;
86 static int hf_flag_tunnel = -1;
87 static int hf_flag_srcroute = -1;
88 static int hf_flag_down = -1;
89 static int hf_flag_disabled = -1;
90 static int hf_flag_querier = -1;
91 static int hf_flag_leaf = -1;
92 static int hf_ncount = -1;
93 static int hf_neighbor = -1;
94 
95 static int ett_dvmrp = -1;
96 static int ett_commands = -1;
97 static int ett_capabilities = -1;
98 static int ett_flags = -1;
99 static int ett_route = -1;
100 
101 static expert_field ei_checksum = EI_INIT;
102 
103 static int strict_v3 = FALSE;
104 
105 #define DVMRP_TYPE				0x13
106 static const value_string dvmrp_type[] = {
107 	{DVMRP_TYPE,	"DVMRP"	},
108 	{0,		NULL}
109 };
110 
111 #define DVMRP_V1_RESPONSE			1
112 #define DVMRP_V1_REQUEST			2
113 #define DVMRP_V1_NON_MEMBERSHIP_REPORT		3
114 #define DVMRP_V1_NON_MEMBERSHIP_CANCELLATION	4
115 static const value_string code_v1[] = {
116 	{DVMRP_V1_RESPONSE,			"Response"			},
117 	{DVMRP_V1_REQUEST,			"Request"			},
118 	{DVMRP_V1_NON_MEMBERSHIP_REPORT,	"Non-membership report"		},
119 	{DVMRP_V1_NON_MEMBERSHIP_CANCELLATION,	"Non-membership cancellation"	},
120 	{0,					NULL}
121 };
122 
123 #define DVMRP_V3_PROBE				0x1
124 #define DVMRP_V3_REPORT				0x2
125 #define DVMRP_V3_ASK_NEIGHBORS			0x3
126 #define DVMRP_V3_NEIGHBORS			0x4
127 #define DVMRP_V3_ASK_NEIGHBORS_2		0x5
128 #define DVMRP_V3_NEIGHBORS_2			0x6
129 #define DVMRP_V3_PRUNE				0x7
130 #define DVMRP_V3_GRAFT				0x8
131 #define DVMRP_V3_GRAFT_ACK			0x9
132 static const value_string code_v3[] = {
133 	{DVMRP_V3_PROBE,		"Probe"},
134 	{DVMRP_V3_REPORT,		"Report"},
135 	{DVMRP_V3_ASK_NEIGHBORS,	"Ask Neighbors"},
136 	{DVMRP_V3_NEIGHBORS,		"Neighbors"},
137 	{DVMRP_V3_ASK_NEIGHBORS_2,	"Ask Neighbors 2"},
138 	{DVMRP_V3_NEIGHBORS_2,		"Neighbors 2"},
139 	{DVMRP_V3_PRUNE,		"Prune"},
140 	{DVMRP_V3_GRAFT,		"Graft"},
141 	{DVMRP_V3_GRAFT_ACK,		"Graft ACK"},
142 	{0,				NULL}
143 };
144 
145 #define DVMRP_V3_CAP_LEAF	0x01
146 #define DVMRP_V3_CAP_PRUNE	0x02
147 #define DVMRP_V3_CAP_GENID	0x04
148 #define DVMRP_V3_CAP_MTRACE	0x08
149 #define DVMRP_V3_CAP_SNMP	0x10
150 #define DVMRP_V3_CAP_NETMASK	0x20
151 
152 #define DVMRP_V3_FLAG_TUNNEL	0x01
153 #define DVMRP_V3_FLAG_SRCROUTE	0x02
154 #define DVMRP_V3_FLAG_DOWN	0x10
155 #define DVMRP_V3_FLAG_DISABLED	0x20
156 #define DVMRP_V3_FLAG_QUERIER	0x40
157 #define DVMRP_V3_FLAG_LEAF	0x80
158 
159 
160 #define V1_COMMAND_NULL		0
161 #define V1_COMMAND_AFI		2
162 #define V1_COMMAND_SUBNETMASK	3
163 #define V1_COMMAND_METRIC	4
164 #define V1_COMMAND_FLAGS0	5
165 #define V1_COMMAND_INFINITY	6
166 #define V1_COMMAND_DA		7
167 #define V1_COMMAND_RDA		8
168 #define V1_COMMAND_NMR		9
169 #define V1_COMMAND_NMR_CANCEL	10
170 static const value_string command[] = {
171 	{V1_COMMAND_NULL,	"NULL"	},
172 	{V1_COMMAND_AFI,	"Address Family Indicator"},
173 	{V1_COMMAND_SUBNETMASK,	"Subnetmask"},
174 	{V1_COMMAND_METRIC,	"Metric"},
175 	{V1_COMMAND_FLAGS0,	"Flags0"},
176 	{V1_COMMAND_INFINITY,	"Infinity"},
177 	{V1_COMMAND_DA,		"Destination Address"},
178 	{V1_COMMAND_RDA,	"Requested Destination Address"},
179 	{V1_COMMAND_NMR,	"Non-Membership Report"},
180 	{V1_COMMAND_NMR_CANCEL,	"Non-Membership Report Cancel"},
181 	{0,			NULL}
182 };
183 
184 #define V1_AFI_IP		2
185 static const value_string afi[] = {
186 	{V1_AFI_IP,	"IP v4 Family"},
187 	{0,		NULL}
188 };
189 
190 static const true_false_string tfs_dest_unreach = {
191 	"Destination Unreachable",
192 	"NOT Destination Unreachable"
193 };
194 
195 static const true_false_string tfs_split_horiz = {
196 	"Split Horizon concealed route",
197 	"NOT Split Horizon concealed route"
198 };
199 
200 static const true_false_string tfs_cap_leaf = {
201 	"Leaf",
202 	"NOT Leaf"
203 };
204 static const true_false_string tfs_cap_prune = {
205 	"Prune capable",
206 	"NOT Prune capable"
207 };
208 static const true_false_string tfs_cap_genid = {
209 	"Genid capable",
210 	"NOT Genid capable"
211 };
212 static const true_false_string tfs_cap_mtrace = {
213 	"Multicast Traceroute capable",
214 	"NOT Multicast Traceroute capable"
215 };
216 static const true_false_string tfs_cap_snmp = {
217 	"SNMP capable",
218 	"NOT SNMP capable"
219 };
220 static const true_false_string tfs_cap_netmask = {
221 	"Netmask capable",
222 	"NOT Netmask capable"
223 };
224 
225 static int
dissect_v3_report(tvbuff_t * tvb,proto_tree * parent_tree,int offset)226 dissect_v3_report(tvbuff_t *tvb, proto_tree *parent_tree, int offset)
227 {
228 	guint8 m0,m1,m2,m3;
229 	guint8 s0,s1,s2,s3;
230 	guint8 metric;
231 	guint32 ip;
232 
233 	while (tvb_reported_length_remaining(tvb, offset) > 0) {
234 		proto_tree *tree;
235 		proto_item *item;
236 		int old_offset_a = offset;
237 
238 		item = proto_tree_add_item(parent_tree, hf_route,
239 				tvb, offset, -1, ENC_NA);
240 		tree = proto_item_add_subtree(item, ett_route);
241 
242 		m0 = 0xff;
243 		/* read the mask */
244 		m1 = tvb_get_guint8(tvb, offset);
245 		m2 = tvb_get_guint8(tvb, offset+1);
246 		m3 = tvb_get_guint8(tvb, offset+2);
247 
248 		ip = m3;
249 		ip = (ip<<8)|m2;
250 		ip = (ip<<8)|m1;
251 		ip = (ip<<8)|m0;
252 		proto_tree_add_ipv4(tree, hf_netmask, tvb, offset, 3, ip);
253 
254 		offset += 3;
255 
256 		/* read every srcnet, metric  pairs */
257 		do {
258 			int old_offset_b = offset;
259 			m0 = 0xff;
260 
261 			s1 = 0;
262 			s2 = 0;
263 			s3 = 0;
264 
265 			s0 = tvb_get_guint8(tvb, offset);
266 			offset += 1;
267 			if (m1) {
268 				s1 = tvb_get_guint8(tvb, offset);
269 				offset += 1;
270 			}
271 			if (m2) {
272 				s2 = tvb_get_guint8(tvb, offset);
273 				offset += 1;
274 			}
275 			if (m3) {
276 				s3 = tvb_get_guint8(tvb, offset);
277 				offset += 1;
278 			}
279 
280 			/* handle special case for default route V3/3.4.3 */
281 			if ((!m1)&&(!m2)&&(!m3)&&(!s0)) {
282 				m0 = 0;
283 			}
284 
285 			ip = s3;
286 			ip = (ip<<8)|s2;
287 			ip = (ip<<8)|s1;
288 			ip = (ip<<8)|s0;
289 			proto_tree_add_ipv4_format(tree, hf_saddr, tvb,
290 				old_offset_b, offset-old_offset_b, ip,
291 				"%s %d.%d.%d.%d (netmask %d.%d.%d.%d)",
292 				m0?"Source Network":"Default Route",
293 				s0,s1,s2,s3,m0,m1,m2,m3);
294 
295 			metric = tvb_get_guint8(tvb, offset);
296 			proto_tree_add_uint(tree, hf_metric, tvb,
297 				offset, 1, metric&0x7f);
298 			offset += 1;
299 
300 
301 		} while (!(metric&0x80));
302 
303 		proto_item_set_len(item, offset-old_offset_a);
304 	}
305 
306 	return offset;
307 }
308 
309 static int
dissect_dvmrp_v3(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,int offset)310 dissect_dvmrp_v3(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, int offset)
311 {
312 	guint8 code;
313 
314 	/* version */
315 	proto_tree_add_uint(parent_tree, hf_version, tvb, 0, 0, 3);
316 
317 	/* type of command */
318 	proto_tree_add_uint(parent_tree, hf_type, tvb, offset, 1, 0x13);
319 	offset += 1;
320 
321 	/* code */
322 	code = tvb_get_guint8(tvb, offset);
323 	proto_tree_add_uint(parent_tree, hf_code_v3, tvb, offset, 1, code);
324 	offset += 1;
325 	col_add_fstr(pinfo->cinfo, COL_INFO,
326 			"V%d %s",3 ,val_to_str(code, code_v3,
327 				"Unknown Type:0x%02x"));
328 
329 	/* checksum */
330 	igmp_checksum(parent_tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 0);
331 	offset += 2;
332 
333 	/* skip unused byte */
334 	proto_tree_add_item(parent_tree, hf_reserved, tvb, offset, 2, ENC_BIG_ENDIAN);
335 	offset += 1;
336 
337 	/* PROBE and NEIGHBORS 2 packets have capabilities flags, unused
338 	   for other packets */
339 	if (code==DVMRP_V3_PROBE || code==DVMRP_V3_NEIGHBORS_2) {
340 		static int * const capabilities[] = {
341 			&hf_cap_netmask,
342 			&hf_cap_snmp,
343 			&hf_cap_mtrace,
344 			&hf_cap_genid,
345 			&hf_cap_prune,
346 			&hf_cap_leaf,
347 			NULL
348 		};
349 
350 		proto_tree_add_bitmask(parent_tree, tvb, offset, hf_capabilities,
351 			       ett_capabilities, capabilities, ENC_NA);
352 	}
353 	offset += 1;
354 
355 	/* minor version */
356 	proto_tree_add_item(parent_tree, hf_min_ver, tvb, offset, 1, ENC_BIG_ENDIAN);
357 	offset += 1;
358 
359 	/* major version */
360 	proto_tree_add_item(parent_tree, hf_maj_ver, tvb, offset, 1, ENC_BIG_ENDIAN);
361 	offset += 1;
362 
363 	switch (code) {
364 	case DVMRP_V3_PROBE:
365 		/* generation id */
366 		proto_tree_add_item(parent_tree, hf_genid, tvb,
367 			offset, 4, ENC_BIG_ENDIAN);
368 		offset += 4;
369 		while (tvb_reported_length_remaining(tvb, offset)>=4) {
370 			proto_tree_add_item(parent_tree, hf_neighbor,
371 				tvb, offset, 4, ENC_BIG_ENDIAN);
372 			offset += 4;
373 		}
374 		break;
375 	case DVMRP_V3_REPORT:
376 		offset = dissect_v3_report(tvb, parent_tree, offset);
377 		break;
378 	case DVMRP_V3_PRUNE:
379 		/* source address */
380 		proto_tree_add_item(parent_tree, hf_saddr,
381 			tvb, offset, 4, ENC_BIG_ENDIAN);
382 		offset += 4;
383 		/* group address */
384 		proto_tree_add_item(parent_tree, hf_maddr,
385 			tvb, offset, 4, ENC_BIG_ENDIAN);
386 		offset += 4;
387 		/* prune lifetime */
388 		proto_tree_add_item(parent_tree, hf_life,
389 			tvb, offset, 4, ENC_BIG_ENDIAN);
390 		offset += 4;
391 		/* source netmask */
392 		if (tvb_reported_length_remaining(tvb, offset)>=4) {
393 			proto_tree_add_item(parent_tree, hf_netmask,
394 				tvb, offset, 4, ENC_BIG_ENDIAN);
395 			offset += 4;
396 		}
397 		break;
398 	case DVMRP_V3_GRAFT:
399 		/* source address */
400 		proto_tree_add_item(parent_tree, hf_saddr,
401 			tvb, offset, 4, ENC_BIG_ENDIAN);
402 		offset += 4;
403 		/* group address */
404 		proto_tree_add_item(parent_tree, hf_maddr,
405 			tvb, offset, 4, ENC_BIG_ENDIAN);
406 		offset += 4;
407 		/* source netmask */
408 		if (tvb_reported_length_remaining(tvb, offset)>=4) {
409 			proto_tree_add_item(parent_tree, hf_netmask,
410 				tvb, offset, 4, ENC_BIG_ENDIAN);
411 			offset += 4;
412 		}
413 		break;
414 	case DVMRP_V3_GRAFT_ACK:
415 		/* source address */
416 		proto_tree_add_item(parent_tree, hf_saddr,
417 			tvb, offset, 4, ENC_BIG_ENDIAN);
418 		offset += 4;
419 		/* group address */
420 		proto_tree_add_item(parent_tree, hf_maddr,
421 			tvb, offset, 4, ENC_BIG_ENDIAN);
422 		offset += 4;
423 		/* source netmask */
424 		if (tvb_reported_length_remaining(tvb, offset)>=4) {
425 			proto_tree_add_item(parent_tree, hf_netmask,
426 				tvb, offset, 4, ENC_BIG_ENDIAN);
427 			offset += 4;
428 		}
429 		break;
430 	case DVMRP_V3_ASK_NEIGHBORS:
431 	case DVMRP_V3_NEIGHBORS:
432 		/* XXX - obsolete, and the draft doesn't describe them */
433 		break;
434 	case DVMRP_V3_ASK_NEIGHBORS_2:
435 		/* No data */
436 		break;
437 	case DVMRP_V3_NEIGHBORS_2:
438 		while (tvb_reported_length_remaining(tvb, offset)>=12) {
439 			guint8 neighbor_count;
440 
441 			/* local address */
442 			proto_tree_add_item(parent_tree, hf_local,
443 				tvb, offset, 4, ENC_BIG_ENDIAN);
444 			offset += 4;
445 			/* Metric */
446 			proto_tree_add_item(parent_tree, hf_metric,
447 				tvb, offset, 1, ENC_BIG_ENDIAN);
448 			offset += 1;
449 			/* Threshold */
450 			proto_tree_add_item(parent_tree, hf_threshold,
451 				tvb, offset, 1, ENC_BIG_ENDIAN);
452 			offset += 1;
453 			/* Flags */
454 			{
455 				proto_tree *tree;
456 				proto_item *item;
457 
458 				item = proto_tree_add_item(parent_tree, hf_flags,
459 					tvb, offset, 1, ENC_NA);
460 				tree = proto_item_add_subtree(item, ett_flags);
461 
462 				proto_tree_add_item(tree, hf_flag_tunnel, tvb,
463 					offset, 1, ENC_BIG_ENDIAN);
464 				proto_tree_add_item(tree, hf_flag_srcroute, tvb,
465 					offset, 1, ENC_BIG_ENDIAN);
466 				proto_tree_add_item(tree, hf_flag_down, tvb,
467 					offset, 1, ENC_BIG_ENDIAN);
468 				proto_tree_add_item(tree, hf_flag_disabled, tvb,
469 					offset, 1, ENC_BIG_ENDIAN);
470 				proto_tree_add_item(tree, hf_flag_querier, tvb,
471 					offset, 1, ENC_BIG_ENDIAN);
472 				proto_tree_add_item(tree, hf_flag_leaf, tvb,
473 					offset, 1, ENC_BIG_ENDIAN);
474 			}
475 			offset += 1;
476 			/* Neighbor count */
477 			neighbor_count = tvb_get_guint8(tvb, offset);
478 			proto_tree_add_item(parent_tree, hf_ncount,
479 				tvb, offset, 1, ENC_BIG_ENDIAN);
480 			offset += 1;
481 
482 			while ((tvb_reported_length_remaining(tvb, offset)>=4)
483 				&& (neighbor_count>0)) {
484 				proto_tree_add_item(parent_tree, hf_neighbor,
485 					tvb, offset, 4, ENC_BIG_ENDIAN);
486 				offset += 4;
487 				neighbor_count--;
488 			}
489 		}
490 		break;
491 	}
492 
493 	return offset;
494 }
495 
496 
497 static int
dissect_dvmrp_v1(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,int offset)498 dissect_dvmrp_v1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, int offset)
499 {
500 	guint8 code;
501 	guint8 af=2; /* default */
502 
503 	/* version */
504 	proto_tree_add_uint(parent_tree, hf_version, tvb, 0, 0, 1);
505 
506 	/* type of command */
507 	proto_tree_add_uint(parent_tree, hf_type, tvb, offset, 1, 0x13);
508 	offset += 1;
509 
510 	/* code */
511 	code = tvb_get_guint8(tvb, offset);
512 	proto_tree_add_uint(parent_tree, hf_code_v1, tvb, offset, 1, code);
513 	offset += 1;
514 	col_add_fstr(pinfo->cinfo, COL_INFO,
515 			"V%d %s",1 ,val_to_str(code, code_v1,
516 				"Unknown Type:0x%02x"));
517 
518 	/* checksum */
519 	igmp_checksum(parent_tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 0);
520 	offset += 2;
521 
522 	/* decode all the v1 commands */
523 	while (tvb_reported_length_remaining(tvb, offset) > 0) {
524 		proto_tree *tree;
525 		proto_item *item;
526 		guint8 cmd,count;
527 		int old_offset = offset;
528 
529 		item = proto_tree_add_item(parent_tree, hf_commands,
530 				tvb, offset, -1, ENC_NA);
531 		tree = proto_item_add_subtree(item, ett_commands);
532 
533 		cmd = tvb_get_guint8(tvb, offset);
534 		proto_tree_add_uint(tree, hf_command, tvb,
535 			offset, 1, cmd);
536 		offset += 1;
537 
538 		switch (cmd){
539 		case V1_COMMAND_NULL:
540 			offset += 1; /* skip ignored/pad byte*/
541 			if (item) {
542 				proto_item_set_text(item, "Command: NULL");
543 			}
544 			break;
545 		case V1_COMMAND_AFI:
546 			af = tvb_get_guint8(tvb, offset);
547 			proto_tree_add_uint(tree, hf_afi, tvb,
548 				offset, 1, af);
549 			offset += 1;
550 			if (item) {
551 				proto_item_set_text(item, "%s: %s",
552 					val_to_str(cmd, command, "Unknown Command:0x%02x"),
553 					val_to_str(af, afi, "Unknown Family:0x%02x")
554 				);
555 			}
556 			break;
557 		case V1_COMMAND_SUBNETMASK:
558 			count = tvb_get_guint8(tvb, offset);
559 			proto_tree_add_uint(tree, hf_count, tvb,
560 				offset, 1, count);
561 			offset += 1;
562 			if (count) { /* must be 0 or 1 */
563 				proto_tree_add_item(tree, hf_netmask,
564 					tvb, offset, 4, ENC_BIG_ENDIAN);
565 				if (item) {
566 					proto_item_set_text(item, "%s: %d.%d.%d.%d",
567 						val_to_str(cmd, command, "Unknown Command:0x%02x"),
568 						tvb_get_guint8(tvb, offset),
569 						tvb_get_guint8(tvb, offset+1),
570 						tvb_get_guint8(tvb, offset+2),
571 						tvb_get_guint8(tvb, offset+3));
572 				}
573 				offset += 4;
574 			} else {
575 				if (item) {
576 					proto_item_set_text(item, "%s: <no mask supplied>",
577 						val_to_str(cmd, command, "Unknown Command:0x%02x"));
578 				}
579 			}
580 			break;
581 		case V1_COMMAND_METRIC:
582 			proto_tree_add_item(tree, hf_metric, tvb,
583 				offset, 1, ENC_BIG_ENDIAN);
584 			if (item) {
585 				proto_item_set_text(item, "%s: %d",
586 					val_to_str(cmd, command, "Unknown Command:0x%02x"),
587 					tvb_get_guint8(tvb, offset));
588 			}
589 			offset += 1;
590 			break;
591 		case V1_COMMAND_FLAGS0:
592 			count = tvb_get_guint8(tvb, offset);
593 			proto_tree_add_boolean(tree, hf_dest_unr, tvb, offset, 1, count);
594 			proto_tree_add_boolean(tree, hf_split_horiz, tvb, offset, 1, count);
595 			if (item) {
596 				proto_item_set_text(item, "%s: 0x%02x",
597 					val_to_str(cmd, command, "Unknown Command:0x%02x"), count);
598 			}
599 			offset += 1;
600 			break;
601 		case V1_COMMAND_INFINITY:
602 			proto_tree_add_item(tree, hf_infinity, tvb,
603 				offset, 1, ENC_BIG_ENDIAN);
604 			if (item) {
605 				proto_item_set_text(item, "%s: %d",
606 					val_to_str(cmd, command, "Unknown Command:0x%02x"), tvb_get_guint8(tvb, offset));
607 			}
608 			offset += 1;
609 			break;
610 		case V1_COMMAND_DA:
611 		case V1_COMMAND_RDA: /* same as DA */
612 			count = tvb_get_guint8(tvb, offset);
613 			proto_tree_add_uint(tree, hf_count, tvb,
614 				offset, 1, count);
615 			offset += 1;
616 			while (count--) {
617 				proto_tree_add_item(tree, hf_daddr,
618 					tvb, offset, 4, ENC_BIG_ENDIAN);
619 				offset += 4;
620 			}
621 			if (item) {
622 				proto_item_set_text(item, "%s",
623 					val_to_str(cmd, command, "Unknown Command:0x%02x"));
624 			}
625 			break;
626 		case V1_COMMAND_NMR:
627 			count = tvb_get_guint8(tvb, offset);
628 			proto_tree_add_uint(tree, hf_count, tvb,
629 				offset, 1, count);
630 			offset += 1;
631 			while (count--) {
632 				proto_tree_add_item(tree, hf_maddr,
633 					tvb, offset, 4, ENC_BIG_ENDIAN);
634 				offset += 4;
635 				proto_tree_add_item(tree, hf_hold, tvb,
636 					offset, 4, ENC_BIG_ENDIAN);
637 				offset += 4;
638 			}
639 			if (item) {
640 				proto_item_set_text(item, "%s",
641 					val_to_str(cmd, command, "Unknown Command:0x%02x"));
642 			}
643 			break;
644 		case V1_COMMAND_NMR_CANCEL:
645 			count = tvb_get_guint8(tvb, offset);
646 			proto_tree_add_uint(tree, hf_count, tvb,
647 				offset, 1, count);
648 			offset += 1;
649 			while (count--) {
650 				proto_tree_add_item(tree, hf_maddr,
651 					tvb, offset, 4, ENC_BIG_ENDIAN);
652 				offset += 4;
653 			}
654 			if (item) {
655 				proto_item_set_text(item, "%s",
656 					val_to_str(cmd, command, "Unknown Command:0x%02x"));
657 			}
658 			break;
659 		}
660 
661 		proto_item_set_len(item, offset-old_offset);
662 	}
663 
664 	return offset;
665 }
666 
667 /* This function is only called from the IGMP dissector */
668 static int
dissect_dvmrp(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,void * data _U_)669 dissect_dvmrp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
670 {
671 	proto_tree *tree;
672 	proto_item *item;
673 	int offset = 0;
674 
675 	col_set_str(pinfo->cinfo, COL_PROTOCOL, "DVMRP");
676 	col_clear(pinfo->cinfo, COL_INFO);
677 
678 	item = proto_tree_add_item(parent_tree, proto_dvmrp, tvb, offset, -1, ENC_NA);
679 	tree = proto_item_add_subtree(item, ett_dvmrp);
680 
681 	if ((tvb_captured_length_remaining(tvb, offset)>=8)
682 	 && (((tvb_get_guint8(tvb, 6)==0xff)
683 	 && (tvb_get_guint8(tvb, 7)==0x03))
684 	     || !strict_v3)) {
685 		offset = dissect_dvmrp_v3(tvb, pinfo, tree, offset);
686 	} else {
687 		offset = dissect_dvmrp_v1(tvb, pinfo, tree, offset);
688 	}
689 
690 	proto_item_set_len(item, offset);
691 	return offset;
692 }
693 
694 void
proto_register_dvmrp(void)695 proto_register_dvmrp(void)
696 {
697 	static hf_register_info hf[] = {
698 		{ &hf_version,
699 			{ "DVMRP Version", "dvmrp.version", FT_UINT8, BASE_DEC,
700 			  NULL, 0, NULL, HFILL }},
701 
702 		{ &hf_type,
703 			{ "Type", "dvmrp.type", FT_UINT8, BASE_HEX,
704 			  VALS(dvmrp_type), 0, "DVMRP Packet Type", HFILL }},
705 
706 		{ &hf_code_v1,
707 			{ "Code", "dvmrp.v1.code", FT_UINT8, BASE_HEX,
708 			  VALS(code_v1), 0, "DVMRP Packet Code", HFILL }},
709 
710 		{ &hf_checksum,
711 			{ "Checksum", "dvmrp.checksum", FT_UINT16, BASE_HEX,
712 			  NULL, 0, "DVMRP Checksum", HFILL }},
713 
714 		{ &hf_checksum_status,
715 			{ "Checksum Status", "dvmrp.checksum.status", FT_UINT8, BASE_NONE,
716 			  VALS(proto_checksum_vals), 0x0, NULL, HFILL }},
717 
718 		{ &hf_reserved,
719 			{ "Reserved", "dvmrp.reserved", FT_UINT16, BASE_HEX,
720 			  NULL, 0, "DVMRP v3 Reserved", HFILL }},
721 
722 		{ &hf_commands,
723 			{ "Commands", "dvmrp.commands", FT_NONE, BASE_NONE,
724 			  NULL, 0, "DVMRP V1 Commands", HFILL }},
725 
726 		{ &hf_command,
727 			{ "Command", "dvmrp.command", FT_UINT8, BASE_HEX,
728 			  VALS(command), 0, "DVMRP V1 Command", HFILL }},
729 
730 		{ &hf_afi,
731 			{ "Address Family", "dvmrp.afi", FT_UINT8, BASE_HEX,
732 			  VALS(afi), 0, "DVMRP Address Family Indicator", HFILL }},
733 
734 		{ &hf_count,
735 			{ "Count", "dvmrp.count", FT_UINT8, BASE_HEX,
736 			  NULL, 0, NULL, HFILL }},
737 
738 		{ &hf_netmask,
739 			{ "Netmask", "dvmrp.netmask", FT_IPv4, BASE_NETMASK,
740 			  NULL, 0, "DVMRP Netmask", HFILL }},
741 
742 		{ &hf_metric,
743 			{ "Metric", "dvmrp.metric", FT_UINT8, BASE_DEC,
744 			  NULL, 0, "DVMRP Metric", HFILL }},
745 
746 		{&hf_dest_unr,
747 			{ "Destination Unreachable", "dvmrp.dest_unreach", FT_BOOLEAN, 8,
748 			TFS(&tfs_dest_unreach), 0x01, NULL, HFILL }},
749 
750 		{&hf_split_horiz,
751 			{ "Split Horizon", "dvmrp.split_horiz", FT_BOOLEAN, 8,
752 			TFS(&tfs_split_horiz), 0x02, "Split Horizon concealed route", HFILL }},
753 
754 		{ &hf_infinity,
755 			{ "Infinity", "dvmrp.infinity", FT_UINT8, BASE_DEC,
756 			  NULL, 0, "DVMRP Infinity", HFILL }},
757 
758 		{ &hf_daddr,
759 			{ "Dest Addr", "dvmrp.daddr", FT_IPv4, BASE_NONE,
760 			  NULL, 0, "DVMRP Destination Address", HFILL }},
761 
762 		{ &hf_maddr,
763 			{ "Multicast Addr", "dvmrp.maddr", FT_IPv4, BASE_NONE,
764 			  NULL, 0, "DVMRP Multicast Address", HFILL }},
765 
766 		{ &hf_hold,
767 			{ "Hold Time", "dvmrp.hold", FT_UINT32, BASE_DEC,
768 			  NULL, 0, "DVMRP Hold Time in seconds", HFILL }},
769 
770 		{ &hf_code_v3,
771 			{ "Code", "dvmrp.v3.code", FT_UINT8, BASE_HEX,
772 			  VALS(code_v3), 0, "DVMRP Packet Code", HFILL }},
773 
774 		{ &hf_capabilities,
775 			{ "Capabilities", "dvmrp.capabilities", FT_UINT8, BASE_HEX,
776 			  NULL, 0, "DVMRP V3 Capabilities", HFILL }},
777 
778 		{&hf_cap_leaf,
779 			{ "Leaf", "dvmrp.cap.leaf", FT_BOOLEAN, 8,
780 			TFS(&tfs_cap_leaf), DVMRP_V3_CAP_LEAF, NULL, HFILL }},
781 
782 		{&hf_cap_prune,
783 			{ "Prune", "dvmrp.cap.prune", FT_BOOLEAN, 8,
784 			TFS(&tfs_cap_prune), DVMRP_V3_CAP_PRUNE, "Prune capability", HFILL }},
785 
786 		{&hf_cap_genid,
787 			{ "Genid", "dvmrp.cap.genid", FT_BOOLEAN, 8,
788 			TFS(&tfs_cap_genid), DVMRP_V3_CAP_GENID, "Genid capability", HFILL }},
789 
790 		{&hf_cap_mtrace,
791 			{ "Mtrace", "dvmrp.cap.mtrace", FT_BOOLEAN, 8,
792 			TFS(&tfs_cap_mtrace), DVMRP_V3_CAP_MTRACE, "Mtrace capability", HFILL }},
793 
794 		{&hf_cap_snmp,
795 			{ "SNMP", "dvmrp.cap.snmp", FT_BOOLEAN, 8,
796 			TFS(&tfs_cap_snmp), DVMRP_V3_CAP_SNMP, "SNMP capability", HFILL }},
797 
798 		{&hf_cap_netmask,
799 			{ "Netmask", "dvmrp.cap.netmask", FT_BOOLEAN, 8,
800 			TFS(&tfs_cap_netmask), DVMRP_V3_CAP_NETMASK, "Netmask capability", HFILL }},
801 
802 		{ &hf_min_ver,
803 			{ "Minor Version", "dvmrp.min_ver", FT_UINT8, BASE_HEX,
804 			  NULL, 0, "DVMRP Minor Version", HFILL }},
805 
806 		{ &hf_maj_ver,
807 			{ "Major Version", "dvmrp.maj_ver", FT_UINT8, BASE_HEX,
808 			  NULL, 0, "DVMRP Major Version", HFILL }},
809 
810 		{ &hf_genid,
811 			{ "Generation ID", "dvmrp.genid", FT_UINT32, BASE_DEC,
812 			  NULL, 0, "DVMRP Generation ID", HFILL }},
813 
814 		{ &hf_route,
815 			{ "Route", "dvmrp.route", FT_NONE, BASE_NONE,
816 			  NULL, 0, "DVMRP V3 Route Report", HFILL }},
817 
818 		{ &hf_saddr,
819 			{ "Source Addr", "dvmrp.saddr", FT_IPv4, BASE_NONE,
820 			  NULL, 0, "DVMRP Source Address", HFILL }},
821 
822 		{ &hf_life,
823 			{ "Prune lifetime", "dvmrp.lifetime", FT_UINT32, BASE_DEC,
824 			  NULL, 0, "DVMRP Prune Lifetime", HFILL }},
825 
826 		{ &hf_local,
827 			{ "Local Addr", "dvmrp.local", FT_IPv4, BASE_NONE,
828 			  NULL, 0, "DVMRP Local Address", HFILL }},
829 
830 		{ &hf_threshold,
831 			{ "Threshold", "dvmrp.threshold", FT_UINT8, BASE_DEC,
832 			NULL, 0, "DVMRP Interface Threshold", HFILL }},
833 
834 		{ &hf_flags,
835 			{ "Flags", "dvmrp.flags", FT_NONE, BASE_NONE,
836 			NULL, 0, "DVMRP Interface Flags", HFILL }},
837 
838 		{ &hf_flag_tunnel,
839 			{ "Tunnel", "dvmrp.flag.tunnel", FT_BOOLEAN, 8,
840 			NULL, DVMRP_V3_FLAG_TUNNEL, "Neighbor reached via tunnel", HFILL }},
841 
842 		{ &hf_flag_srcroute,
843 			{ "Source Route", "dvmrp.flag.srcroute", FT_BOOLEAN, 8,
844 			NULL, DVMRP_V3_FLAG_SRCROUTE, "Tunnel uses IP source routing", HFILL }},
845 
846 		{ &hf_flag_down,
847 			{ "Down", "dvmrp.flag.down", FT_BOOLEAN, 8,
848 			NULL, DVMRP_V3_FLAG_DOWN, "Operational status down", HFILL }},
849 
850 		{ &hf_flag_disabled,
851 			{ "Disabled", "dvmrp.flag.disabled", FT_BOOLEAN, 8,
852 			NULL, DVMRP_V3_FLAG_DISABLED, "Administrative status down", HFILL }},
853 
854 		{ &hf_flag_querier,
855 			{ "Querier", "dvmrp.flag.querier", FT_BOOLEAN, 8,
856 			NULL, DVMRP_V3_FLAG_QUERIER, "Querier for interface", HFILL }},
857 
858 		{ &hf_flag_leaf,
859 			{ "Leaf", "dvmrp.flag.leaf", FT_BOOLEAN, 8,
860 			NULL, DVMRP_V3_FLAG_LEAF, "No downstream neighbors on interface", HFILL }},
861 
862 		{ &hf_ncount,
863 			{ "Neighbor Count", "dvmrp.ncount", FT_UINT8, BASE_DEC,
864 			NULL, 0, "DVMRP Neighbor Count", HFILL }},
865 
866 		{ &hf_neighbor,
867 			{ "Neighbor Addr", "dvmrp.neighbor", FT_IPv4, BASE_NONE,
868 			  NULL, 0, "DVMRP Neighbor Address", HFILL }}
869 	};
870 	static gint *ett[] = {
871 		&ett_dvmrp,
872 		&ett_commands,
873 		&ett_capabilities,
874 		&ett_flags,
875 		&ett_route
876 	};
877 
878 	static ei_register_info ei[] = {
879 		{ &ei_checksum, { "dvmrp.bad_checksum", PI_CHECKSUM, PI_ERROR, "Bad checksum", EXPFILL }},
880 	};
881 
882 	expert_module_t* expert_dvmrp;
883 
884 	module_t *module_dvmrp;
885 
886 	proto_dvmrp = proto_register_protocol("Distance Vector Multicast Routing Protocol", "DVMRP", "dvmrp");
887 	proto_register_field_array(proto_dvmrp, hf, array_length(hf));
888 	proto_register_subtree_array(ett, array_length(ett));
889 	expert_dvmrp = expert_register_protocol(proto_dvmrp);
890 	expert_register_field_array(expert_dvmrp, ei, array_length(ei));
891 
892 	module_dvmrp = prefs_register_protocol(proto_dvmrp, NULL);
893 
894 	prefs_register_bool_preference(module_dvmrp, "strict_v3", "Allow strict DVMRP V3 only",
895 		"Allow only packets with Major=0x03//Minor=0xFF as DVMRP V3 packets",
896 		&strict_v3);
897 }
898 
899 void
proto_reg_handoff_dvmrp(void)900 proto_reg_handoff_dvmrp(void)
901 {
902 	dissector_handle_t dvmrp_handle;
903 
904 	dvmrp_handle = create_dissector_handle(dissect_dvmrp, proto_dvmrp);
905 	dissector_add_uint("igmp.type", IGMP_DVMRP, dvmrp_handle);
906 }
907 
908 
909 /*
910  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
911  *
912  * Local variables:
913  * c-basic-offset: 8
914  * tab-width: 8
915  * indent-tabs-mode: t
916  * End:
917  *
918  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
919  * :indentSize=8:tabSize=8:noTabs=false:
920  */
921