checkAPIs.pl: support for new-style dissectors in check_hf_entries
[metze/wireshark/wip.git] / epan / dissectors / packet-igmp.c
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
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*
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
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
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
399 dissect_v3_sqrv_bits(tvbuff_t *tvb, proto_tree *parent_tree, int offset)
400 {
401     static const int * 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
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(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(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(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
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
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(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(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
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(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(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(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
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
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
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
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(tvb, offset + 4),
830                         tvb_ip_to_str(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
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
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
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  -  http://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