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