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