1 /* packet-igmp.c
2  * Routines for IGMP packet disassembly
3  * 2001 Ronnie Sahlberg
4  * 2007 Thomas Morin
5  * <See AUTHORS for emails>
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * SPDX-License-Identifier: GPL-2.0-or-later
12  */
13 /*
14 	IGMP is defined in the following RFCs
15 	RFC988	Version 0	Obsolete
16 	RFC1054	Version 1
17 	RFC1112	Version 1	(same as RFC1054 as far as we are concerned)
18 	RFC2236	Version 2
19 	RFC3376	Version 3
20 
21 	Size in bytes for each packet
22 	type	RFC988	RFC1054	RFC2236 RFC3376  DVMRP  MRDISC  MSNIP  IGAP  RGMP
23 	        v0      v1      v2      v3       v1/v3
24 	0x01      20
25 	0x02      20
26 	0x03      20
27 	0x04      20
28 	0x05      20
29 	0x06      20
30 	0x07      20
31 	0x08      20
32 	0x11               8*     8*     >=12
33 	0x12               8*     8*
34 	0x13                                     x
35 	0x16                      8
36 	0x17                      8
37 	0x22                            >=8
38 	0x23                                                    >=8b
39 	0x24                                            >=8a    8b
40 	0x25                                            4a      >=8b
41 	0x26                                            4a
42 	0x40                                                           ??c
43 	0x41                                                           ??c
44 	0x42                                                           ??c
45 	0xfc                                                                  8
46 	0xfd                                                                  8
47 	0xfe                                                                  8
48 	0xff                                                                  8
49 
50    * Differs in second byte of protocol. Always 0 in V1
51 
52 
53 	Multicast traceroute was taken from
54 	draft-ietf-idmr-traceroute-ipm-07.txt
55 
56 	Size in bytes for each packet
57 	type    draft-ietf-idmr-traceroute-ipm-07.ps
58 	0x1e      24 + n*32
59 	0x1f      24 + n*32 (n == 0 for Query)
60 
61    x DVMRP Protocol  see packet-dvmrp.c
62 
63 	DVMRP is defined in the following RFCs
64 	RFC1075 Version 1
65 	draft-ietf-idmr-dvmrp-v3-10.txt Version 3
66 
67 	V1 and V3 can be distinguished by looking at bytes 6 and 7 in the
68 	IGMP header.
69 	If header[6]==0xff and header[7]==0x03 we have version 3.
70 
71    a MRDISC Protocol  see packet-mrdisc.c
72 
73 	MRDISC : IGMP Multicast Router DISCovery
74 	draft-ietf-idmr-igmp-mrdisc-06.txt
75 	TTL == 1 and IP.DST==224.0.0.2 for all packets
76 
77    b MSNIP Protocol  see packet-msnip.c
78 
79 	MSNIP : Multicast Source Notification of Interest Protocol
80 	draft-ietf-idmr-msnip-00.txt
81 	0x23, 0x24 are sent with ip.dst==224.0.0.22
82 	0x25 is sent as unicast.
83 
84    c IGAP Protocol  see packet-igap.c
85 
86         IGAP : Internet Group membership Authentication Protocol
87 	draft-hayashi-igap-03.txt
88 
89    d RGMP Protocol  see packet-rgmp.c
90 
91 	RGMP : Router-port Group Management Protocol
92 	RFC3488
93 	TTL == 1 and IP.DST==224.0.0.25 for all packets
94 
95 */
96 
97 #include "config.h"
98 
99 #include <epan/packet.h>
100 #include <epan/expert.h>
101 #include <epan/range.h>
102 #include <epan/to_str.h>
103 #include <epan/ipproto.h>
104 #include <epan/in_cksum.h>
105 #include <wsutil/str_util.h>
106 #include "packet-igmp.h"
107 
108 void proto_register_igmp(void);
109 void proto_reg_handoff_igmp(void);
110 
111 static int proto_igmp = -1;
112 static int hf_type = -1;
113 static int hf_reserved = -1;
114 static int hf_version = -1;
115 static int hf_group_type = -1;
116 static int hf_reply_code = -1;
117 static int hf_reply_pending = -1;
118 static int hf_checksum = -1;
119 static int hf_checksum_status = -1;
120 static int hf_identifier = -1;
121 static int hf_access_key = -1;
122 static int hf_max_resp = -1;
123 static int hf_max_resp_exp = -1;
124 static int hf_max_resp_mant = -1;
125 static int hf_suppress = -1;
126 static int hf_qrv = -1;
127 static int hf_qqic = -1;
128 static int hf_num_src = -1;
129 static int hf_saddr = -1;
130 static int hf_num_grp_recs = -1;
131 static int hf_record_type = -1;
132 static int hf_aux_data_len = -1;
133 static int hf_maddr = -1;
134 static int hf_aux_data = -1;
135 static int hf_data = -1;
136 static int hf_mtrace_max_hops = -1;
137 static int hf_mtrace_saddr = -1;
138 static int hf_mtrace_raddr = -1;
139 static int hf_mtrace_rspaddr = -1;
140 static int hf_mtrace_resp_ttl = -1;
141 static int hf_mtrace_q_id = -1;
142 static int hf_mtrace_q_arrival = -1;
143 static int hf_mtrace_q_inaddr = -1;
144 static int hf_mtrace_q_outaddr = -1;
145 static int hf_mtrace_q_prevrtr = -1;
146 static int hf_mtrace_q_inpkt = -1;
147 static int hf_mtrace_q_outpkt = -1;
148 static int hf_mtrace_q_total = -1;
149 static int hf_mtrace_q_rtg_proto = -1;
150 static int hf_mtrace_q_fwd_ttl = -1;
151 static int hf_mtrace_q_mbz = -1;
152 static int hf_mtrace_q_s = -1;
153 static int hf_mtrace_q_src_mask = -1;
154 static int hf_mtrace_q_fwd_code = -1;
155 
156 static int ett_igmp = -1;
157 static int ett_group_record = -1;
158 static int ett_max_resp = -1;
159 static int ett_mtrace_block = -1;
160 
161 static expert_field ei_checksum = EI_INIT;
162 
163 static dissector_table_t   subdissector_table;
164 
165 #define IGMP_TRACEROUTE_HDR_LEN           24
166 #define IGMP_TRACEROUTE_RSP_LEN           32
167 
168 static const value_string commands[] = {
169 	{IGMP_V0_CREATE_GROUP_REQUEST,	"Create Group Request"		},
170 	{IGMP_V0_CREATE_GROUP_REPLY,	"Create Group Reply"		},
171 	{IGMP_V0_JOIN_GROUP_REQUEST,	"Join Group Request"		},
172 	{IGMP_V0_JOIN_GROUP_REPLY,	"Join Group Reply"		},
173 	{IGMP_V0_LEAVE_GROUP_REQUEST,	"Leave Group Request"		},
174 	{IGMP_V0_LEAVE_GROUP_REPLY,	"Leave Group Reply"		},
175 	{IGMP_V0_CONFIRM_GROUP_REQUEST,	"Confirm Group Request"		},
176 	{IGMP_V0_CONFIRM_GROUP_REPLY,	"Confirm Group Reply"		},
177 	{IGMP_V1_HOST_MEMBERSHIP_QUERY,	"Membership Query"		},
178 	{IGMP_V1_HOST_MEMBERSHIP_REPORT,"Membership Report"		},
179 	{IGMP_DVMRP,			"DVMRP Protocol"		},
180 	{IGMP_V1_PIM_ROUTING_MESSAGE,	"PIM Routing Message"		},
181 	{IGMP_V2_MEMBERSHIP_REPORT,	"Membership Report"		},
182 	{IGMP_V2_LEAVE_GROUP,		"Leave Group"			},
183 	{IGMP_TRACEROUTE_RESPONSE,	"Traceroute Response"		},
184 	{IGMP_TRACEROUTE_QUERY_REQ,	"Traceroute Query or Request"	},
185 	{IGMP_V3_MEMBERSHIP_REPORT,	"Membership Report"		},
186 	{0,		NULL}
187 };
188 
189 #define IGMP_V3_S		0x08
190 #define IGMP_V3_QRV_MASK	0x07
191 
192 #define IGMP_MAX_RESP_EXP	0x70
193 #define IGMP_MAX_RESP_MANT	0x0f
194 
195 #define IGMP_V0_GROUP_PUBLIC	0x00
196 #define IGMP_V0_GROUP_PRIVATE	0x01
197 
198 static const value_string vs_group_type[] = {
199 	{IGMP_V0_GROUP_PUBLIC,		"Public Group"			},
200 	{IGMP_V0_GROUP_PRIVATE,		"Private Group"			},
201 	{0,		NULL}
202 };
203 
204 #define IGMP_V0_REPLY_GRANTED	0x00
205 #define IGMP_V0_REPLY_NO_RESOURCES	0x01
206 #define IGMP_V0_REPLY_INVALID_CODE	0x02
207 #define IGMP_V0_REPLY_INVALID_GROUP	0x03
208 #define IGMP_V0_REPLY_INVALID_KEY	0x04
209 
210 static const value_string vs_reply_code[] = {
211 	{IGMP_V0_REPLY_GRANTED,	"Request Granted"	},
212 	{IGMP_V0_REPLY_NO_RESOURCES,	"Request Denied, No Resources"	},
213 	{IGMP_V0_REPLY_INVALID_CODE,	"Request Denied, Invalid Code"	},
214 	{IGMP_V0_REPLY_INVALID_GROUP,	"Request Denied, Invalid Group"	},
215 	{IGMP_V0_REPLY_INVALID_KEY,	"Request Denied, Invalid Key"	},
216 	{0,		NULL}
217 };
218 
219 static const true_false_string tfs_s = {
220 	"SUPPRESS router side processing",
221 	"Do not suppress router side processing"
222 };
223 
224 #define IGMP_V3_MODE_IS_INCLUDE		1
225 #define IGMP_V3_MODE_IS_EXCLUDE		2
226 #define IGMP_V3_CHANGE_TO_INCLUDE_MODE	3
227 #define IGMP_V3_CHANGE_TO_EXCLUDE_MODE	4
228 #define IGMP_V3_ALLOW_NEW_SOURCES	5
229 #define IGMP_V3_BLOCK_OLD_SOURCES	6
230 
231 static const value_string vs_record_type[] = {
232 	{IGMP_V3_MODE_IS_INCLUDE,	"Mode Is Include"		},
233 	{IGMP_V3_MODE_IS_EXCLUDE,	"Mode Is Exclude"		},
234 	{IGMP_V3_CHANGE_TO_INCLUDE_MODE,"Change To Include Mode"	},
235 	{IGMP_V3_CHANGE_TO_EXCLUDE_MODE,"Change To Exclude Mode"	},
236 	{IGMP_V3_ALLOW_NEW_SOURCES,	"Allow New Sources"		},
237 	{IGMP_V3_BLOCK_OLD_SOURCES,	"Block Old Sources"		},
238 	{ 0,	NULL}
239 };
240 
241 static const value_string mtrace_rtg_vals[] = {
242 	{1,  "DVMRP"                                        },
243 	{2,  "MOSPF"                                        },
244 	{3,  "PIM"                                          },
245 	{4,  "CBT"                                          },
246 	{5,  "PIM using special routing table"              },
247 	{6,  "PIM using a static route"                     },
248 	{7,  "DVMRP using a static route"                   },
249 	{8,  "PIM using MBGP (aka BGP4+) route"             },
250 	{9,  "CBT using special routing table"              },
251 	{10, "CBT using a static route"                     },
252 	{11, "PIM using state created by Assert processing" },
253 	{0,  NULL}
254 };
255 
256 static const value_string mtrace_fwd_code_vals[] = {
257 	{0x00, "NO_ERROR"       },
258 	{0x01, "WRONG_IF"       },
259 	{0x02, "PRUNE_SENT"     },
260 	{0x03, "PRUNE_RCVD"     },
261 	{0x04, "SCOPED"         },
262 	{0x05, "NO_ROUTE"       },
263 	{0x06, "WRONG_LAST_HOP" },
264 	{0x07, "NOT_FORWARDING" },
265 	{0x08, "REACHED_RP"     },
266 	{0x09, "RPF_IF"         },
267 	{0x0A, "NO_MULTICAST"   },
268 	{0x0B, "INFO_HIDDEN"    },
269 	{0x81, "NO_SPACE"       },
270 	{0x82, "OLD_ROUTER"     },
271 	{0x83, "ADMIN_PROHIB"   },
272 	{0, NULL}
273 };
274 
igmp_checksum(proto_tree * tree,tvbuff_t * tvb,int hf_index,int hf_index_status,expert_field * ei_index,packet_info * pinfo,guint len)275 void igmp_checksum(proto_tree *tree, tvbuff_t *tvb, int hf_index,
276 	int hf_index_status, expert_field* ei_index, packet_info *pinfo, guint len)
277 {
278 	vec_t cksum_vec[1];
279 
280 	if (len == 0) {
281 		/*
282 		 * Checksum the entire IGMP packet.
283 		 */
284 		len = tvb_reported_length(tvb);
285 	}
286 
287 	if (!pinfo->fragmented && tvb_captured_length(tvb) >= len) {
288 		/*
289 		 * The packet isn't part of a fragmented datagram and isn't
290 		 * truncated, so we can checksum it.
291 		 */
292 		SET_CKSUM_VEC_TVB(cksum_vec[0], tvb, 0, len);
293 		proto_tree_add_checksum(tree, tvb, 2, hf_index, hf_index_status, ei_index, pinfo, in_cksum(&cksum_vec[0], 1),
294                                 ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY|PROTO_CHECKSUM_IN_CKSUM);
295 	} else
296 		proto_tree_add_checksum(tree, tvb, 2, hf_index, hf_index_status, ei_index, pinfo, 0, ENC_BIG_ENDIAN, PROTO_CHECKSUM_NO_FLAGS);
297 
298 	return;
299 }
300 
301 static proto_tree*
dissect_igmp_common(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,int * offset,unsigned char * type,int version)302 dissect_igmp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int* offset, unsigned char* type, int version)
303 {
304 	proto_item* ti;
305 	proto_tree* igmp_tree;
306 
307 	col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "IGMPv%d", version);
308 	col_clear(pinfo->cinfo, COL_INFO);
309 
310 	ti = proto_tree_add_item(tree, proto_igmp, tvb, 0, -1, ENC_NA);
311 	igmp_tree = proto_item_add_subtree(ti, ett_igmp);
312 
313 	*type = tvb_get_guint8(tvb, 0);
314 	col_add_fstr(pinfo->cinfo, COL_INFO, "%s", val_to_str(*type, commands, "Unknown Type:0x%02x"));
315 
316 	/* version of IGMP protocol */
317 	ti = proto_tree_add_uint(igmp_tree, hf_version, tvb, 0, 0, version);
318 	proto_item_set_generated(ti);
319 
320 	/* type of command */
321 	proto_tree_add_item(igmp_tree, hf_type, tvb, 0, 1, ENC_BIG_ENDIAN);
322 	*offset = 1;
323 
324 	return igmp_tree;
325 }
326 
327 
328 /* Unknown IGMP message type */
329 static int
dissect_igmp_unknown(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree)330 dissect_igmp_unknown(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
331 {
332 	proto_item* ti;
333 	proto_tree* tree;
334 	int len;
335 	int offset = 0;
336 	unsigned char type;
337 
338 	col_set_str(pinfo->cinfo, COL_PROTOCOL, "IGMP");
339 	col_clear(pinfo->cinfo, COL_INFO);
340 
341 	ti = proto_tree_add_item(parent_tree, proto_igmp, tvb, offset, -1, ENC_NA);
342 	tree = proto_item_add_subtree(ti, ett_igmp);
343 
344 	type = tvb_get_guint8(tvb, offset);
345 	col_add_str(pinfo->cinfo, COL_INFO,
346 		val_to_str(type, commands, "Unknown Type:0x%02x"));
347 
348 	/* type of command */
349 	proto_tree_add_uint(tree, hf_type, tvb, offset, 1, type);
350 	offset += 1;
351 
352 	/* Just call the rest of it "data" */
353 	len = tvb_captured_length_remaining(tvb, offset);
354 	proto_tree_add_item(tree, hf_data, tvb, offset, -1, ENC_NA);
355 	offset += len;
356 
357 	return offset;
358 }
359 
360 
361 
362 /*************************************************************
363  * IGMP Protocol dissectors
364  *************************************************************/
365 static int
dissect_v3_max_resp(tvbuff_t * tvb,proto_tree * parent_tree,int offset)366 dissect_v3_max_resp(tvbuff_t *tvb, proto_tree *parent_tree, int offset)
367 {
368 	proto_tree *tree;
369 	proto_item *item;
370 	guint8 bits;
371 	guint32 tsecs;
372 
373 	bits = tvb_get_guint8(tvb, offset);
374 	if (bits&0x80) {
375 		tsecs = ((bits&IGMP_MAX_RESP_MANT)|0x10);
376 		tsecs = tsecs << ( ((bits&IGMP_MAX_RESP_EXP)>>4) + 3);
377 	} else {
378 		tsecs = bits;
379 	}
380 
381 	item = proto_tree_add_uint_format_value(parent_tree, hf_max_resp, tvb,
382 			offset, 1, tsecs, "%.1f sec (0x%02x)",tsecs*0.1,bits);
383 
384 	if (bits&0x80) {
385 		tree = proto_item_add_subtree(item, ett_max_resp);
386 
387 		proto_tree_add_uint(tree, hf_max_resp_exp, tvb, offset, 1,
388 			bits);
389 		proto_tree_add_uint(tree, hf_max_resp_mant, tvb, offset, 1,
390 			bits);
391 	}
392 
393 	offset += 1;
394 
395 	return offset;
396 }
397 
398 static int
dissect_v3_sqrv_bits(tvbuff_t * tvb,proto_tree * parent_tree,int offset)399 dissect_v3_sqrv_bits(tvbuff_t *tvb, proto_tree *parent_tree, int offset)
400 {
401     static int * const bits[] = {
402         &hf_suppress,
403         &hf_qrv,
404         NULL
405     };
406 
407     proto_tree_add_bitmask_list(parent_tree, tvb, offset, 1, bits, ENC_NA);
408 	offset += 1;
409 
410 	return offset;
411 }
412 
413 static int
dissect_v3_group_record(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,int offset)414 dissect_v3_group_record(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, int offset)
415 {
416 	proto_tree *tree;
417 	proto_item *item;
418 	int old_offset = offset;
419 	guint8	adl;
420 	guint16 num;
421 	const gchar *maddr_str;
422 	guint8 record_type;
423 
424 	tree = proto_tree_add_subtree_format(parent_tree, tvb, offset, -1,
425 			ett_group_record, &item, "Group Record : %s  %s",
426 			tvb_ip_to_str(pinfo->pool, tvb, offset+4),
427 			val_to_str_const(tvb_get_guint8(tvb, offset), vs_record_type,"")
428 		);
429 
430 	/* record type */
431 	record_type = tvb_get_guint8(tvb, offset);
432 	proto_tree_add_item(tree, hf_record_type, tvb, offset, 1, ENC_BIG_ENDIAN);
433 	offset += 1;
434 
435 	/* aux data len */
436 	adl = tvb_get_guint8(tvb, offset);
437 	proto_tree_add_uint(tree, hf_aux_data_len, tvb, offset, 1, adl);
438 	offset += 1;
439 
440 	/*number of sources*/
441 	num = tvb_get_ntohs(tvb, offset);
442 	proto_tree_add_uint(tree, hf_num_src, tvb, offset, 2, num);
443 	offset += 2;
444 
445 	/* multicast address */
446 	proto_tree_add_item(tree, hf_maddr, tvb, offset, 4, ENC_BIG_ENDIAN);
447 	maddr_str = tvb_ip_to_str(pinfo->pool, tvb, offset);
448 	offset += 4;
449 
450 	if (num == 0) {
451 		switch(record_type) {
452 		case IGMP_V3_MODE_IS_INCLUDE:
453 		case IGMP_V3_CHANGE_TO_INCLUDE_MODE:
454 			col_append_fstr(pinfo->cinfo, COL_INFO, " / Leave group %s", maddr_str);
455 			break;
456 		case IGMP_V3_MODE_IS_EXCLUDE:
457 		case IGMP_V3_CHANGE_TO_EXCLUDE_MODE:
458 			col_append_fstr(pinfo->cinfo, COL_INFO,
459 				" / Join group %s for any sources", maddr_str);
460 			break;
461 		case IGMP_V3_ALLOW_NEW_SOURCES:
462 			col_append_fstr(pinfo->cinfo, COL_INFO,
463 				" / Group %s, ALLOW_NEW_SOURCES but no source specified (?)",
464 				maddr_str);
465 			break;
466 		case IGMP_V3_BLOCK_OLD_SOURCES:
467 			col_append_fstr(pinfo->cinfo, COL_INFO,
468 				" / Group %s, BLOCK_OLD_SOURCES but no source specified (?)",
469 				maddr_str);
470 			break;
471 		default:
472 			col_append_fstr(pinfo->cinfo, COL_INFO,
473 				" / Group %s, unknown record type (?)", maddr_str);
474 				break;
475 		}
476 	} else {
477 		switch(record_type) {
478 		case IGMP_V3_MODE_IS_INCLUDE:
479 		case IGMP_V3_CHANGE_TO_INCLUDE_MODE:
480 			col_append_fstr(pinfo->cinfo, COL_INFO,
481 				" / Join group %s for source%s {",
482 				maddr_str, (num>1) ? "s in" : "");
483 			break;
484 		case IGMP_V3_MODE_IS_EXCLUDE:
485 		case IGMP_V3_CHANGE_TO_EXCLUDE_MODE:
486 			col_append_fstr(pinfo->cinfo, COL_INFO,
487 				" / Join group %s, for source%s {",
488 				maddr_str, (num>1) ? "s not in" : " not");
489 			break;
490 		case IGMP_V3_ALLOW_NEW_SOURCES:
491 			col_append_fstr(pinfo->cinfo, COL_INFO,
492 				" / Group %s, new source%s {",
493 				maddr_str, (num>1) ? "s" : "");
494 			break;
495 		case IGMP_V3_BLOCK_OLD_SOURCES:
496 			col_append_fstr(pinfo->cinfo, COL_INFO,
497 				" / Group %s, block source%s {",
498 				maddr_str, (num>1) ? "s" : "");
499 			break;
500 		default:
501 			col_append_fstr(pinfo->cinfo, COL_INFO,
502 				" / Group %s, unknown record type (?), sources {",
503 				maddr_str);
504 			break;
505 		}
506 	}
507 
508 	/* source addresses */
509 	while(num--){
510 		col_append_fstr(pinfo->cinfo, COL_INFO, "%s%s",
511 				tvb_ip_to_str(pinfo->pool, tvb, offset), (num?", ":"}"));
512 
513 		proto_tree_add_item(tree, hf_saddr, tvb, offset, 4, ENC_BIG_ENDIAN);
514 		offset += 4;
515 	}
516 
517 	/* aux data */
518 	if(adl){
519 		proto_tree_add_item(tree, hf_aux_data, tvb, offset, adl*4, ENC_NA);
520 		offset += adl*4;
521 	}
522 
523 	proto_item_set_len(item, offset-old_offset);
524 	return offset;
525 }
526 
527 /* dissectors for version 3, rfc3376 */
528 static int
dissect_igmp_v3_report(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,void * data _U_)529 dissect_igmp_v3_report(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
530 {
531 	proto_tree* tree;
532 	guint16 num;
533 	int offset;
534 	unsigned char type;
535 
536 	tree = dissect_igmp_common(tvb, pinfo, parent_tree, &offset, &type, 3);
537 
538         proto_tree_add_item(tree, hf_reserved, tvb, offset, 1, ENC_NA);
539 	offset += 1;
540 
541 	/* checksum */
542 	igmp_checksum(tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 0);
543 	offset += 2;
544 
545         proto_tree_add_item(tree, hf_reserved, tvb, offset, 2, ENC_NA);
546 	offset += 2;
547 
548 	/* number of group records */
549 	num = tvb_get_ntohs(tvb, offset);
550 	if (!num)
551 		col_append_str(pinfo->cinfo, COL_INFO, " - General query");
552 
553 	proto_tree_add_uint(tree, hf_num_grp_recs, tvb, offset, 2, num);
554 	offset += 2;
555 
556 	while (num--)
557 		offset = dissect_v3_group_record(tvb, pinfo, tree, offset);
558 
559 	return offset;
560 }
561 
562 static int
dissect_igmp_v3_query(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,void * data _U_)563 dissect_igmp_v3_query(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
564 {
565 	proto_tree* tree;
566 	guint16 num;
567 	int offset;
568 	unsigned char type;
569 
570 	tree = dissect_igmp_common(tvb, pinfo, parent_tree, &offset, &type, 3);
571 
572 	num = tvb_get_ntohs(tvb, offset+9);
573 	/* max resp code */
574 	offset = dissect_v3_max_resp(tvb, tree, offset);
575 
576 	/* checksum */
577 	igmp_checksum(tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 0);
578 	offset += 2;
579 
580 	/* group address */
581 	proto_tree_add_item(tree, hf_maddr, tvb, offset, 4, ENC_BIG_ENDIAN);
582 
583 	if (!tvb_get_ipv4(tvb, offset)) {
584 		col_append_str(pinfo->cinfo, COL_INFO, ", general");
585 	} else {
586 		col_append_fstr(pinfo->cinfo, COL_INFO, ", specific for group %s",
587 			tvb_ip_to_str(pinfo->pool, tvb, offset));
588 	}
589 	offset +=4;
590 
591 	/* bitmask for S and QRV */
592 	offset = dissect_v3_sqrv_bits(tvb, tree, offset);
593 
594 	/* qqic */
595 	proto_tree_add_item(tree, hf_qqic, tvb, offset, 1, ENC_BIG_ENDIAN);
596 	offset += 1;
597 
598 	/*number of sources*/
599 	proto_tree_add_uint(tree, hf_num_src, tvb, offset, 2, num);
600 	if (num) {
601 		col_append_fstr(pinfo->cinfo, COL_INFO, ", source%s {", (num>1)?"s":"");
602 	}
603 	offset += 2;
604 
605 	while(num--){
606 		col_append_fstr(pinfo->cinfo, COL_INFO, "%s%s", tvb_ip_to_str(pinfo->pool, tvb, offset), (num?", ":"}"));
607 		proto_tree_add_item(tree, hf_saddr, tvb, offset, 4, ENC_BIG_ENDIAN);
608 		offset += 4;
609 	}
610 
611 	return offset;
612 }
613 
614 /* dissector for version 2 query and report, rfc2236 */
615 static int
dissect_igmp_v2(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,void * data _U_)616 dissect_igmp_v2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
617 {
618 	proto_tree* tree;
619 	guint8 tsecs;
620 	int offset;
621 	unsigned char type;
622 
623 	tree = dissect_igmp_common(tvb, pinfo, parent_tree, &offset, &type, 2);
624 
625 	/* max resp time */
626 	tsecs = tvb_get_guint8(tvb, offset);
627 	proto_tree_add_uint_format_value(tree, hf_max_resp, tvb,
628 		offset, 1, tsecs, "%.1f sec (0x%02x)", tsecs*0.1,tsecs);
629 	offset += 1;
630 
631 	/* checksum */
632 	igmp_checksum(tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 8);
633 	offset += 2;
634 
635 	/* group address */
636 	proto_tree_add_item(tree, hf_maddr, tvb, offset, 4, ENC_BIG_ENDIAN);
637 
638 	if (! tvb_get_ipv4(tvb, offset)) {
639 		col_append_str(pinfo->cinfo, COL_INFO, ", general");
640 	} else {
641 		switch(type)
642 		{
643 		case IGMP_V2_LEAVE_GROUP:
644 			col_append_fstr(pinfo->cinfo, COL_INFO, " %s", tvb_ip_to_str(pinfo->pool, tvb, offset));
645 			break;
646 		case IGMP_V1_HOST_MEMBERSHIP_QUERY:
647 			col_append_fstr(pinfo->cinfo, COL_INFO, ", specific for group %s", tvb_ip_to_str(pinfo->pool, tvb, offset));
648 			break;
649 		default: /* IGMP_V2_MEMBERSHIP_REPORT is the only case left */
650 			col_append_fstr(pinfo->cinfo, COL_INFO, " group %s", tvb_ip_to_str(pinfo->pool, tvb, offset));
651 			break;
652 		}
653 	}
654 	offset +=4;
655 
656 	return offset;
657 }
658 
659 /* dissector for version 1 query and report, rfc1054 */
660 static int
dissect_igmp_v1(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,void * data _U_)661 dissect_igmp_v1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
662 {
663 	proto_tree* tree;
664 	int offset;
665 	unsigned char type;
666 
667 	tree = dissect_igmp_common(tvb, pinfo, parent_tree, &offset, &type, 1);
668 
669 	/* skip unused byte */
670 	proto_tree_add_item(tree, hf_reserved, tvb, offset, 1, ENC_NA);
671 	offset += 1;
672 
673 	/* checksum */
674 	igmp_checksum(tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 8);
675 	offset += 2;
676 
677 	/* group address */
678 	proto_tree_add_item(tree, hf_maddr, tvb, offset, 4, ENC_BIG_ENDIAN);
679 	offset +=4;
680 
681 	return offset;
682 }
683 
684 /* dissector for version 0, rfc988 */
685 static int
dissect_igmp_v0(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,void * data _U_)686 dissect_igmp_v0(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
687 {
688 	proto_tree* tree;
689 	unsigned char code;
690 	int offset;
691 	unsigned char type;
692 
693 	tree = dissect_igmp_common(tvb, pinfo, parent_tree, &offset, &type, 0);
694 
695 	/* Code */
696 	code = tvb_get_guint8(tvb, offset);
697 	if (type==IGMP_V0_CREATE_GROUP_REQUEST) {
698 		proto_tree_add_uint(tree, hf_group_type, tvb, offset, 1, code);
699 	} else if (!(type&0x01)) {
700 		if (code <5) {
701 			proto_tree_add_uint(tree, hf_reply_code, tvb, offset, 1, code);
702 		} else {
703 			proto_tree_add_uint(tree, hf_reply_pending, tvb, offset, 1, code);
704 		}
705 	}
706 	offset += 1;
707 
708 	/* checksum */
709 	igmp_checksum(tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 20);
710 	offset += 2;
711 
712 	/* identifier */
713 	proto_tree_add_item(tree, hf_identifier, tvb, offset, 4, ENC_BIG_ENDIAN);
714 	offset += 4;
715 
716 	/* group address */
717 	proto_tree_add_item(tree, hf_maddr, tvb, offset, 4, ENC_BIG_ENDIAN);
718 	offset += 4;
719 
720 	/* access key */
721 	proto_tree_add_item(tree, hf_access_key, tvb, offset, 8, ENC_NA);
722 	offset += 8;
723 
724 	return offset;
725 }
726 
727 static int
dissect_igmp_mquery(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,void * data)728 dissect_igmp_mquery(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data)
729 {
730 	if ( tvb_reported_length(tvb)>=12 ) {
731 			/* version 3 */
732 		return dissect_igmp_v3_query(tvb, pinfo, parent_tree, data);
733 	}
734 
735 	/* v1 and v2 differs in second byte of header */
736 	if (tvb_get_guint8(tvb, 1)) {
737 		return dissect_igmp_v2(tvb, pinfo, parent_tree, data);
738 	}
739 
740 	return dissect_igmp_v1(tvb, pinfo, parent_tree, data);
741 }
742 
743 /* dissector for multicast traceroute, rfc???? */
744 static int
dissect_igmp_mtrace(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,void * data _U_)745 dissect_igmp_mtrace(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
746 {
747 	proto_tree* tree;
748 	proto_item* ti;
749 	int offset = 0;
750 	unsigned char type;
751 	const char *typestr, *blocks = NULL;
752 	char buf[20];
753 
754 	ti = proto_tree_add_item(parent_tree, proto_igmp, tvb, offset, -1, ENC_NA);
755 	tree = proto_item_add_subtree(ti, ett_igmp);
756 
757 	col_set_str(pinfo->cinfo, COL_PROTOCOL, "IGMP");
758 	col_clear(pinfo->cinfo, COL_INFO);
759 
760 	/* All multicast traceroute packets (Query, Request and
761 	 * Response) have the same fixed header. Request and Response
762 	 * have one or more response data blocks following this fixed
763 	 * header. Since Query and Request share the same IGMP type,
764 	 * the method to differentiate between them is to check the
765 	 * IGMP packet length. Queries are only
766 	 * IGMP_TRACEROUTE_HDR_LEN bytes long.
767 	 */
768 	type = tvb_get_guint8(tvb, offset);
769 	if (type == IGMP_TRACEROUTE_RESPONSE) {
770 		int i = (tvb_reported_length_remaining(tvb, offset) - IGMP_TRACEROUTE_HDR_LEN) / IGMP_TRACEROUTE_RSP_LEN;
771 		g_snprintf(buf, sizeof buf, ", %d block%s", i, plurality(i, "", "s"));
772 		typestr = "Traceroute Response";
773 		blocks = buf;
774 	} else if (tvb_reported_length_remaining(tvb, offset) == IGMP_TRACEROUTE_HDR_LEN)
775 		typestr = "Traceroute Query";
776 	else
777 		typestr = "Traceroute Request";
778 
779 	col_set_str(pinfo->cinfo, COL_INFO, typestr);
780 	if (blocks)
781 		col_append_str(pinfo->cinfo, COL_INFO, blocks);
782 
783 	proto_tree_add_uint_format_value(tree, hf_type, tvb, offset, 1, type,
784 		"%s (0x%02x)", typestr, type);
785 	offset += 1;
786 
787 	/* maximum number of hops that the requester wants to trace */
788 	proto_tree_add_item(tree, hf_mtrace_max_hops, tvb, offset, 1, ENC_BIG_ENDIAN);
789 	offset += 1;
790 
791 	/* checksum */
792 	igmp_checksum(tree, tvb, hf_checksum, hf_checksum_status, &ei_checksum, pinfo, 0);
793 	offset += 2;
794 
795 	/* group address to be traced */
796 	proto_tree_add_item(tree, hf_maddr, tvb, offset, 4, ENC_BIG_ENDIAN);
797 	offset += 4;
798 
799 	/* address of multicast source for the path being traced */
800 	proto_tree_add_item(tree, hf_mtrace_saddr, tvb, offset, 4, ENC_BIG_ENDIAN);
801 	offset += 4;
802 
803 	/* address of multicast receiver for the path being traced */
804 	proto_tree_add_item(tree, hf_mtrace_raddr, tvb, offset, 4, ENC_BIG_ENDIAN);
805 	offset += 4;
806 
807 	/* address where the completed traceroute response packet gets sent */
808 	proto_tree_add_item(tree, hf_mtrace_rspaddr, tvb, offset, 4, ENC_BIG_ENDIAN);
809 	offset += 4;
810 
811 	/* for multicasted responses, TTL at which to multicast the response */
812 	proto_tree_add_item(tree, hf_mtrace_resp_ttl, tvb, offset, 1, ENC_BIG_ENDIAN);
813 	offset += 1;
814 
815 	/* unique identifier for this traceroute request (for e.g. duplicate/delay detection) */
816 	proto_tree_add_item(tree, hf_mtrace_q_id, tvb, offset, 3, ENC_BIG_ENDIAN);
817 	offset += 3;
818 
819 	/* If this was Query, we only had the fixed header */
820 	if (tvb_reported_length_remaining(tvb, offset) == 0)
821 		return offset;
822 
823 	/* Loop through the response data blocks */
824 	while (tvb_reported_length_remaining(tvb, offset) >= IGMP_TRACEROUTE_RSP_LEN) {
825 		proto_tree *block_tree;
826 
827 		block_tree = proto_tree_add_subtree_format(tree, tvb, offset, IGMP_TRACEROUTE_RSP_LEN,
828 			ett_mtrace_block, NULL, "Response data block: %s -> %s,  Proto: %s,  Forwarding Code: %s",
829 			tvb_ip_to_str(pinfo->pool, tvb, offset + 4),
830 			tvb_ip_to_str(pinfo->pool, tvb, offset + 8),
831 			val_to_str_const(tvb_get_guint8(tvb, offset + 28), mtrace_rtg_vals, "Unknown"),
832 			val_to_str_const(tvb_get_guint8(tvb, offset + 31), mtrace_fwd_code_vals, "Unknown"));
833 
834 		/* Query Arrival Time */
835 		proto_tree_add_item(block_tree, hf_mtrace_q_arrival, tvb, offset, 4, ENC_BIG_ENDIAN);
836 		offset += 4;
837 
838 		/* Incoming Interface Address */
839 		proto_tree_add_item(block_tree, hf_mtrace_q_inaddr, tvb, offset, 4, ENC_BIG_ENDIAN);
840 		offset += 4;
841 
842 		/* Outgoing Interface Address */
843 		proto_tree_add_item(block_tree, hf_mtrace_q_outaddr, tvb, offset, 4, ENC_BIG_ENDIAN);
844 		offset += 4;
845 
846 		/* Previous-Hop Router Address */
847 		proto_tree_add_item(block_tree, hf_mtrace_q_prevrtr, tvb, offset, 4, ENC_BIG_ENDIAN);
848 		offset += 4;
849 
850 		/* Input packet count on incoming interface */
851 		proto_tree_add_item(block_tree, hf_mtrace_q_inpkt, tvb, offset, 4, ENC_BIG_ENDIAN);
852 		offset += 4;
853 
854 		/* Output packet count on outgoing interface */
855 		proto_tree_add_item(block_tree, hf_mtrace_q_outpkt, tvb, offset, 4, ENC_BIG_ENDIAN);
856 		offset += 4;
857 
858 		/* Total number of packets for this source-group pair */
859 		proto_tree_add_item(block_tree, hf_mtrace_q_total, tvb, offset, 4, ENC_BIG_ENDIAN);
860 		offset += 4;
861 
862 		/* Routing protocol in use between this and previous-hop router */
863 		proto_tree_add_item(block_tree, hf_mtrace_q_rtg_proto, tvb, offset, 1, ENC_BIG_ENDIAN);
864 		offset += 1;
865 
866 		/* TTL that a packet is required to be forwarded */
867 		proto_tree_add_item(block_tree, hf_mtrace_q_fwd_ttl, tvb, offset, 1, ENC_BIG_ENDIAN);
868 		offset += 1;
869 
870 		/* Must be zeroed and ignored bit, S bit and src network mask length */
871 		proto_tree_add_item(block_tree, hf_mtrace_q_mbz, tvb, offset, 1, ENC_BIG_ENDIAN);
872 		proto_tree_add_item(block_tree, hf_mtrace_q_s, tvb, offset, 1, ENC_BIG_ENDIAN);
873 		proto_tree_add_item(block_tree, hf_mtrace_q_src_mask, tvb, offset, 1, ENC_BIG_ENDIAN);
874 		offset += 1;
875 
876 		/* Forwarding information/error code */
877 		proto_tree_add_item(block_tree, hf_mtrace_q_fwd_code, tvb, offset, 1, ENC_BIG_ENDIAN);
878 		offset += 1;
879 	}
880 
881 	return offset;
882 }
883 
884 static int
dissect_igmp(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,void * data _U_)885 dissect_igmp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void* data _U_)
886 {
887 	int offset = 0;
888 	unsigned char type;
889 
890 	type = tvb_get_guint8(tvb, offset);
891 
892 	if (!dissector_try_uint(subdissector_table, type, tvb, pinfo, parent_tree))
893 	{
894 		dissect_igmp_unknown(tvb, pinfo, parent_tree);
895 	}
896 	return tvb_captured_length(tvb);
897 }
898 
899 void
proto_register_igmp(void)900 proto_register_igmp(void)
901 {
902 	static hf_register_info hf[] = {
903 		{ &hf_type,
904 			{ "Type", "igmp.type", FT_UINT8, BASE_HEX,
905 			  VALS(commands), 0, "IGMP Packet Type", HFILL }},
906 
907 		{ &hf_reserved,
908 			{ "Reserved", "igmp.reserved", FT_BYTES, BASE_NONE,
909 			  NULL, 0, "IGMP Reserved", HFILL }},
910 
911 		{ &hf_version,
912 			{ "IGMP Version", "igmp.version", FT_UINT8, BASE_DEC,
913 			  NULL, 0, NULL, HFILL }},
914 
915 		{ &hf_group_type,
916 			{ "Type Of Group", "igmp.group_type", FT_UINT8, BASE_DEC,
917 			  VALS(vs_group_type), 0, "IGMP V0 Type Of Group", HFILL }},
918 
919 		{ &hf_reply_code,
920 			{ "Reply", "igmp.reply", FT_UINT8, BASE_DEC,
921 			  VALS(vs_reply_code), 0, "IGMP V0 Reply", HFILL }},
922 
923 		{ &hf_reply_pending,
924 			{ "Reply Pending", "igmp.reply.pending", FT_UINT8, BASE_DEC,
925 			  NULL, 0, "IGMP V0 Reply Pending, Retry in this many seconds", HFILL }},
926 
927 		{ &hf_checksum,
928 			{ "Checksum", "igmp.checksum", FT_UINT16, BASE_HEX,
929 			  NULL, 0, "IGMP Checksum", HFILL }},
930 
931 		{ &hf_checksum_status,
932 			{ "Checksum Status", "igmp.checksum.status", FT_UINT8, BASE_NONE,
933 			  VALS(proto_checksum_vals), 0x0, NULL, HFILL }},
934 
935 		{ &hf_identifier,
936 			{ "Identifier", "igmp.identifier", FT_UINT32, BASE_DEC,
937 			  NULL, 0, "IGMP V0 Identifier", HFILL }},
938 
939 		{ &hf_access_key,
940 			{ "Access Key", "igmp.access_key", FT_BYTES, BASE_NONE,
941 			  NULL, 0, "IGMP V0 Access Key", HFILL }},
942 
943 		{ &hf_max_resp,
944 			{ "Max Resp Time", "igmp.max_resp", FT_UINT8, BASE_DEC,
945 			  NULL, 0, "Max Response Time", HFILL }},
946 
947 		{ &hf_suppress,
948 			{ "S", "igmp.s", FT_BOOLEAN, 8,
949 			  TFS(&tfs_s), IGMP_V3_S, "Suppress Router Side Processing", HFILL }},
950 
951 		{ &hf_qrv,
952 			{ "QRV", "igmp.qrv", FT_UINT8, BASE_DEC,
953 			NULL, IGMP_V3_QRV_MASK, "Querier's Robustness Value", HFILL }},
954 
955 		{ &hf_qqic,
956 			{ "QQIC", "igmp.qqic", FT_UINT8, BASE_DEC,
957 			  NULL, 0, "Querier's Query Interval Code", HFILL }},
958 
959 		{ &hf_num_src,
960 			{ "Num Src", "igmp.num_src", FT_UINT16, BASE_DEC,
961 			  NULL, 0, "Number Of Sources", HFILL }},
962 
963 		{ &hf_saddr,
964 			{ "Source Address", "igmp.saddr", FT_IPv4, BASE_NONE,
965 			  NULL, 0, NULL, HFILL }},
966 
967 		{ &hf_num_grp_recs,
968 			{ "Num Group Records", "igmp.num_grp_recs", FT_UINT16, BASE_DEC,
969 			  NULL, 0, "Number Of Group Records", HFILL }},
970 
971 		{ &hf_record_type,
972 			{ "Record Type", "igmp.record_type", FT_UINT8, BASE_DEC,
973 			VALS(vs_record_type), 0, NULL, HFILL }},
974 
975 		{ &hf_aux_data_len,
976 			{ "Aux Data Len", "igmp.aux_data_len", FT_UINT8, BASE_DEC,
977 			NULL, 0, "Aux Data Len, In units of 32bit words", HFILL }},
978 
979 		{ &hf_maddr,
980 			{ "Multicast Address", "igmp.maddr", FT_IPv4, BASE_NONE,
981 			  NULL, 0, NULL, HFILL }},
982 
983 		{ &hf_aux_data,
984 			{ "Aux Data", "igmp.aux_data", FT_BYTES, BASE_NONE,
985 			  NULL, 0, "IGMP V3 Auxiliary Data", HFILL }},
986 
987 		{ &hf_data,
988 			{ "Data", "igmp.data", FT_BYTES, BASE_NONE,
989 			  NULL, 0, NULL, HFILL }},
990 
991 		{ &hf_max_resp_exp,
992 			{ "Exponent", "igmp.max_resp.exp", FT_UINT8, BASE_HEX,
993 			NULL, IGMP_MAX_RESP_EXP, "Maximum Response Time, Exponent", HFILL }},
994 
995 		{ &hf_max_resp_mant,
996 			{ "Mantissa", "igmp.max_resp.mant", FT_UINT8, BASE_HEX,
997 			NULL, IGMP_MAX_RESP_MANT, "Maximum Response Time, Mantissa", HFILL }},
998 
999 		{ &hf_mtrace_max_hops,
1000 			{ "# hops", "igmp.mtrace.max_hops", FT_UINT8, BASE_DEC,
1001 			NULL, 0, "Maximum Number of Hops to Trace", HFILL }},
1002 
1003 		{ &hf_mtrace_saddr,
1004 			{ "Source Address", "igmp.mtrace.saddr", FT_IPv4, BASE_NONE,
1005 			  NULL, 0, "Multicast Source for the Path Being Traced", HFILL }},
1006 
1007 		{ &hf_mtrace_raddr,
1008 			{ "Receiver Address", "igmp.mtrace.raddr", FT_IPv4, BASE_NONE,
1009 			  NULL, 0, "Multicast Receiver for the Path Being Traced", HFILL }},
1010 
1011 		{ &hf_mtrace_rspaddr,
1012 			{ "Response Address", "igmp.mtrace.rspaddr", FT_IPv4, BASE_NONE,
1013 			  NULL, 0, "Destination of Completed Traceroute Response", HFILL }},
1014 
1015 		{ &hf_mtrace_resp_ttl,
1016 			{ "Response TTL", "igmp.mtrace.resp_ttl", FT_UINT8, BASE_DEC,
1017 			NULL, 0, "TTL for Multicasted Responses", HFILL }},
1018 
1019 		{ &hf_mtrace_q_id,
1020 			{ "Query ID", "igmp.mtrace.q_id", FT_UINT24, BASE_DEC,
1021 			NULL, 0, "Identifier for this Traceroute Request", HFILL }},
1022 
1023 		{ &hf_mtrace_q_arrival,
1024 			{ "Query Arrival", "igmp.mtrace.q_arrival", FT_UINT32, BASE_DEC,
1025 			NULL, 0, "Query Arrival Time", HFILL }},
1026 
1027 		{ &hf_mtrace_q_inaddr,
1028 			{ "In itf addr", "igmp.mtrace.q_inaddr", FT_IPv4, BASE_NONE,
1029 			NULL, 0, "Incoming Interface Address", HFILL }},
1030 
1031 		{ &hf_mtrace_q_outaddr,
1032 			{ "Out itf addr", "igmp.mtrace.q_outaddr", FT_IPv4, BASE_NONE,
1033 			NULL, 0, "Outgoing Interface Address", HFILL }},
1034 
1035 		{ &hf_mtrace_q_prevrtr,
1036 			{ "Previous rtr addr", "igmp.mtrace.q_prevrtr", FT_IPv4, BASE_NONE,
1037 			NULL, 0, "Previous-Hop Router Address", HFILL }},
1038 
1039 		{ &hf_mtrace_q_inpkt,
1040 			{ "In pkts", "igmp.mtrace.q_inpkt", FT_UINT32, BASE_DEC,
1041 			NULL, 0, "Input packet count on incoming interface", HFILL }},
1042 
1043 		{ &hf_mtrace_q_outpkt,
1044 			{ "Out pkts", "igmp.mtrace.q_outpkt", FT_UINT32, BASE_DEC,
1045 			NULL, 0, "Output packet count on outgoing interface", HFILL }},
1046 
1047 		{ &hf_mtrace_q_total,
1048 			{ "S,G pkt count", "igmp.mtrace.q_total", FT_UINT32, BASE_DEC,
1049 			NULL, 0, "Total number of packets for this source-group pair", HFILL }},
1050 
1051 		{ &hf_mtrace_q_rtg_proto,
1052 			{ "Rtg Protocol", "igmp.mtrace.q_rtg_proto", FT_UINT8, BASE_DEC,
1053 			VALS(mtrace_rtg_vals), 0, "Routing protocol between this and previous hop rtr", HFILL }},
1054 
1055 		{ &hf_mtrace_q_fwd_ttl,
1056 			{ "FwdTTL", "igmp.mtrace.q_fwd_ttl", FT_UINT8, BASE_DEC,
1057 			NULL, 0, "TTL required for forwarding", HFILL }},
1058 
1059 		{ &hf_mtrace_q_mbz,
1060 			{ "MBZ", "igmp.mtrace.q_mbz", FT_UINT8, BASE_HEX,
1061 			NULL, 0x80, "Must be zeroed on transmission and ignored on reception", HFILL }},
1062 
1063 		{ &hf_mtrace_q_s,
1064 			{ "S", "igmp.mtrace.q_s", FT_UINT8, BASE_HEX,
1065 			NULL, 0x40, "Set if S,G packet count is for source network", HFILL }},
1066 
1067 		{ &hf_mtrace_q_src_mask,
1068 			{ "Src Mask", "igmp.mtrace.q_src_mask", FT_UINT8, BASE_HEX,
1069 			NULL, 0x3F, "Source mask length. 63 when forwarding on group state", HFILL }},
1070 
1071 		{ &hf_mtrace_q_fwd_code,
1072 			{ "Forwarding Code", "igmp.mtrace.q_fwd_code", FT_UINT8, BASE_HEX,
1073 			VALS(mtrace_fwd_code_vals), 0, "Forwarding information/error code", HFILL }},
1074 
1075 	};
1076 	static gint *ett[] = {
1077 		&ett_igmp,
1078 		&ett_group_record,
1079 		&ett_max_resp,
1080 		&ett_mtrace_block,
1081 	};
1082 
1083 	static ei_register_info ei[] = {
1084 		{ &ei_checksum, { "igmp.bad_checksum", PI_CHECKSUM, PI_ERROR, "Bad checksum", EXPFILL }},
1085 	};
1086 
1087 	expert_module_t* expert_igmp;
1088 
1089 	proto_igmp = proto_register_protocol("Internet Group Management Protocol", "IGMP", "igmp");
1090 	proto_register_field_array(proto_igmp, hf, array_length(hf));
1091 	proto_register_subtree_array(ett, array_length(ett));
1092 	expert_igmp = expert_register_protocol(proto_igmp);
1093 	expert_register_field_array(expert_igmp, ei, array_length(ei));
1094 
1095 	subdissector_table = register_dissector_table("igmp.type", "IGMP commands", proto_igmp, FT_UINT32, BASE_HEX);
1096 
1097 }
1098 
1099 void
proto_reg_handoff_igmp(void)1100 proto_reg_handoff_igmp(void)
1101 {
1102 	dissector_handle_t igmp_handle, igmpv0_handle, igmpv1_handle, igmpv2_handle,
1103 						igmp_mquery_handle, igmp_mtrace_handle, igmp_report_handle;
1104 	range_t *igmpv0_range = NULL;
1105 
1106 	igmp_handle = create_dissector_handle(dissect_igmp, proto_igmp);
1107 	dissector_add_uint("ip.proto", IP_PROTO_IGMP, igmp_handle);
1108 
1109 	/* IGMP v0 */
1110 	range_convert_str(NULL, &igmpv0_range, "0-15", 15);
1111 	igmpv0_handle = create_dissector_handle(dissect_igmp_v0, proto_igmp);
1112 	dissector_add_uint_range("igmp.type", igmpv0_range, igmpv0_handle);
1113 	wmem_free(NULL, igmpv0_range);
1114 
1115 	/* IGMP v1 */
1116 	igmpv1_handle = create_dissector_handle(dissect_igmp_v1, proto_igmp);
1117 	dissector_add_uint("igmp.type", IGMP_V1_HOST_MEMBERSHIP_REPORT, igmpv1_handle);
1118 
1119 	/* IGMP v2 */
1120 	igmpv2_handle = create_dissector_handle(dissect_igmp_v2, proto_igmp);
1121 	dissector_add_uint("igmp.type", IGMP_V2_MEMBERSHIP_REPORT, igmpv2_handle);
1122 	dissector_add_uint("igmp.type", IGMP_V2_LEAVE_GROUP, igmpv2_handle);
1123 
1124 	/* IGMP_V1_HOST_MEMBERSHIP_QUERY, all versions */
1125 	igmp_mquery_handle = create_dissector_handle(dissect_igmp_mquery, proto_igmp);
1126 	dissector_add_uint("igmp.type", IGMP_V1_HOST_MEMBERSHIP_QUERY, igmp_mquery_handle);
1127 
1128 	igmp_report_handle = create_dissector_handle(dissect_igmp_v3_report, proto_igmp);
1129 	dissector_add_uint("igmp.type", IGMP_V3_MEMBERSHIP_REPORT, igmp_report_handle);
1130 
1131 	igmp_mtrace_handle = create_dissector_handle(dissect_igmp_mtrace, proto_igmp);
1132 	dissector_add_uint("igmp.type", IGMP_TRACEROUTE_RESPONSE, igmp_mtrace_handle);
1133 	dissector_add_uint("igmp.type", IGMP_TRACEROUTE_QUERY_REQ, igmp_mtrace_handle);
1134 }
1135 
1136 /*
1137  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
1138  *
1139  * Local variables:
1140  * c-basic-offset: 8
1141  * tab-width: 8
1142  * indent-tabs-mode: t
1143  * End:
1144  *
1145  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1146  * :indentSize=8:tabSize=8:noTabs=false:
1147  */
1148 
1149