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