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