just to get things straight: a dissector should *never* do any g_assert() calls!
[obnox/wireshark/wip.git] / epan / dissectors / packet-pim.c
1 /* packet-pim.c
2  * Routines for PIM disassembly
3  * (c) Copyright Jun-ichiro itojun Hagino <itojun@itojun.org>
4  *
5  * $Id$
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <stddef.h>  /* For offsetof */
31 #include <string.h>
32 #include <glib.h>
33
34 #ifdef NEED_SNPRINTF_H
35 # include "snprintf.h"
36 #endif
37
38 #include <epan/packet.h>
39 #include <epan/ipproto.h>
40 #include <epan/afn.h>
41 #include "packet-ipv6.h"
42 #include <epan/in_cksum.h>
43
44 #define PIM_TYPE(x)     ((x) & 0x0f)
45 #define PIM_VER(x)      (((x) & 0xf0) >> 4)
46
47 enum pimv2_addrtype {
48         pimv2_unicast, pimv2_group, pimv2_source
49 };
50
51 static int proto_pim = -1;
52 static int hf_pim_version = -1;
53 static int hf_pim_type = -1;
54 static int hf_pim_code = -1;
55 static int hf_pim_cksum = -1;
56
57 static gint ett_pim = -1;
58
59 static dissector_handle_t ip_handle;
60 static dissector_handle_t ipv6_handle;
61
62 /*
63  * For PIM v1, see the PDF slides at
64  *
65  *      http://www.mbone.de/training/Module3.pdf
66  *
67  * Is it documented anywhere else?
68  */
69 static const char *
70 dissect_pimv1_addr(tvbuff_t *tvb, int offset) {
71     static char buf[512];
72     guint16 flags_masklen;
73
74     flags_masklen = tvb_get_ntohs(tvb, offset);
75     if (flags_masklen & 0x0180) {
76         (void)snprintf(buf, sizeof(buf),
77             "(%s%s%s) ",
78             flags_masklen & 0x0100 ? "S" : "",
79             flags_masklen & 0x0080 ? "W" : "",
80             flags_masklen & 0x0040 ? "R" : "");
81     } else
82         buf[0] = '\0';
83     (void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s/%u",
84         ip_to_str(tvb_get_ptr(tvb, offset + 2, 4)), flags_masklen & 0x3f);
85
86     return buf;
87 }
88
89 static const value_string type1vals[] = {
90     { 0, "Query" },
91     { 1, "Register" },
92     { 2, "Register-stop" },
93     { 3, "Join/Prune" },
94     { 4, "RP-Reachable" },
95     { 5, "Assert" },
96     { 6, "Graft" },
97     { 7, "Graft-Ack" },
98     { 8, "Mode" },
99     { 0, NULL },
100 };
101
102 /* This function is only called from the IGMP dissector */
103 int
104 dissect_pimv1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
105               int offset) {
106     guint8 pim_type;
107     guint8 pim_ver;
108     guint length, pim_length;
109     guint16 pim_cksum, computed_cksum;
110     vec_t cksum_vec[1];
111     proto_tree *pim_tree = NULL;
112     proto_item *ti;
113     proto_tree *pimopt_tree = NULL;
114     proto_item *tiopt;
115
116     if (!proto_is_protocol_enabled(find_protocol_by_id(proto_pim))) {
117         /*
118          * We are not enabled; skip entire packet to be nice to the
119          * IGMP layer (so clicking on IGMP will display the data).
120          */
121         return offset+tvb_length_remaining(tvb, offset);
122     }
123
124     if (check_col(pinfo->cinfo, COL_PROTOCOL))
125         col_set_str(pinfo->cinfo, COL_PROTOCOL, "PIMv1");
126     if (check_col(pinfo->cinfo, COL_INFO))
127         col_clear(pinfo->cinfo, COL_INFO);
128
129     if (tree) {
130         ti = proto_tree_add_item(tree, proto_pim, tvb, offset, -1, FALSE);
131         pim_tree = proto_item_add_subtree(ti, ett_pim);
132
133         /* Put IGMP type, 0x14, into the tree */
134         proto_tree_add_text(pim_tree, tvb, offset, 1,
135             "Type: PIM (0x14)");
136     }
137     offset += 1;
138
139     pim_type = tvb_get_guint8(tvb, offset);
140     if (check_col(pinfo->cinfo, COL_INFO))
141         col_add_str(pinfo->cinfo, COL_INFO,
142             val_to_str(pim_type, type1vals, "Unknown (%u)"));
143
144     if (tree) {
145         proto_tree_add_uint(pim_tree, hf_pim_code, tvb, offset, 1, pim_type);
146     }
147     offset += 1;
148
149     pim_cksum = tvb_get_ntohs(tvb, offset);
150     pim_ver = PIM_VER(tvb_get_guint8(tvb, offset + 2));
151     if (pim_ver != 1) {
152         /*
153          * Not PIMv1 - what gives?
154          */
155         if (tree) {
156             proto_tree_add_uint(pim_tree, hf_pim_cksum, tvb,
157                     offset, 2, pim_cksum);
158         }
159         offset += 2;
160         if (tree)
161             proto_tree_add_uint(pim_tree, hf_pim_version, tvb, offset, 1, pim_ver);
162         return offset+tvb_length_remaining(tvb, offset);
163     }
164
165     /*
166      * Well, it's PIM v1, so we can check whether this is a
167      * Register message, and thus can figure out how much to
168      * checksum and whether to make the columns read-only.
169      */
170     length = tvb_length(tvb);
171     if (pim_type == 1) {
172         /*
173          * Register message - the PIM header is 8 bytes long.
174          * Also set the columns non-writable. Otherwise the IPv4 or
175          * IPv6 dissector for the encapsulated packet that caused
176          * this register will overwrite the PIM info in the columns.
177          */
178         pim_length = 8;
179         col_set_writable(pinfo->cinfo, FALSE);
180     } else {
181         /*
182          * Other message - checksum the entire packet.
183          */
184         pim_length = tvb_reported_length(tvb);
185     }
186
187     if (tree) {
188         if (!pinfo->fragmented && length >= pim_length) {
189             /*
190              * The packet isn't part of a fragmented datagram and isn't
191              * truncated, so we can checksum it.
192              */
193             cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, pim_length);
194             cksum_vec[0].len = pim_length;
195             computed_cksum = in_cksum(&cksum_vec[0], 1);
196             if (computed_cksum == 0) {
197                 proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb,
198                             offset, 2, pim_cksum,
199                             "Checksum: 0x%04x (correct)",
200                             pim_cksum);
201             } else {
202                 proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb,
203                             offset, 2, pim_cksum,
204                             "Checksum: 0x%04x (incorrect, should be 0x%04x)",
205                             pim_cksum, in_cksum_shouldbe(pim_cksum, computed_cksum));
206             }
207         } else {
208             proto_tree_add_uint(pim_tree, hf_pim_cksum, tvb,
209                     offset, 2, pim_cksum);
210         }
211     }
212     offset += 2;
213
214     if (tree)
215         proto_tree_add_uint(pim_tree, hf_pim_version, tvb, offset, 1, pim_ver);
216     offset += 1;
217
218     offset += 3;        /* skip reserved stuff */
219
220     if (tree) {
221         if (tvb_reported_length_remaining(tvb, offset) > 0) {
222             tiopt = proto_tree_add_text(pim_tree, tvb, offset, -1,
223                     "PIM parameters");
224             pimopt_tree = proto_item_add_subtree(tiopt, ett_pim);
225         } else
226             goto done;
227
228         /* version 1 decoder */
229         switch (pim_type) {
230         case 0: /* query */
231           {
232             guint8 mode;
233             guint16 holdtime;
234             static const value_string pimv1_modevals[] = {
235                 { 0, "Dense" },
236                 { 1, "Sparse" },
237                 { 2, "Sparse-Dense" },
238                 { 0, NULL },
239             };
240
241             mode = tvb_get_guint8(tvb, offset) >> 4;
242             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
243                 "Mode: %s",
244                 val_to_str(mode, pimv1_modevals, "Unknown (%u)"));
245             offset += 2;
246             holdtime = tvb_get_ntohs(tvb, offset);
247             proto_tree_add_text(pimopt_tree, tvb, offset, 2,
248                 "Holdtime: %u%s", holdtime,
249                 holdtime == 0xffff ? " (infty)" : "");
250             offset += 2;
251             break;
252           }
253
254         case 1: /* register */
255           {
256             guint8 v_hl;
257             tvbuff_t *next_tvb;
258
259             /*
260              * The rest of the packet is a multicast data packet.
261              */
262             next_tvb = tvb_new_subset(tvb, offset, -1, -1);
263
264             /*
265              * It's an IP packet - determine whether it's IPv4 or IPv6.
266              */
267             v_hl = tvb_get_guint8(tvb, offset);
268             switch((v_hl & 0xf0) >> 4) {
269             case 0:     /* Null-Register dummy header.
270                          * Has the same address family as the encapsulating PIM packet,
271                          * e.g. an IPv6 data packet is encapsulated in IPv6 PIM packet.
272                          */
273                     if (pinfo->src.type == AT_IPv4) {
274                             proto_tree_add_text(pimopt_tree, tvb, offset, -1,
275                                                 "IPv4 dummy header");
276                             proto_tree_add_text(pimopt_tree, tvb, offset + 12, 4,
277                                                 "Source: %s",
278                                                 ip_to_str(tvb_get_ptr(tvb, offset + 12, 4)));
279                             proto_tree_add_text(pimopt_tree, tvb, offset + 16, 4,
280                                                 "Group: %s",
281                                                 ip_to_str(tvb_get_ptr(tvb, offset + 16, 4)));
282                     } else if (pinfo->src.type == AT_IPv6) {
283                             struct ip6_hdr ip6_hdr;
284                             tvb_memcpy(tvb, (guint8 *)&ip6_hdr, offset,
285                                        sizeof ip6_hdr);
286                             proto_tree_add_text(pimopt_tree, tvb, offset, -1,
287                                                 "IPv6 dummy header");
288                             proto_tree_add_text(pimopt_tree, tvb,
289                                                 offset + offsetof(struct ip6_hdr, ip6_src), 16,
290                                                 "Source: %s",
291                                                 ip6_to_str(&ip6_hdr.ip6_src));
292                             proto_tree_add_text(pimopt_tree, tvb,
293                                                 offset + offsetof(struct ip6_hdr, ip6_dst), 16,
294                                                 "Group: %s",
295                                                 ip6_to_str(&ip6_hdr.ip6_dst));
296                     } else
297                             proto_tree_add_text(pimopt_tree, tvb, offset, -1,
298                                                 "Dummy header for an unknown protocol");
299                     break;
300             case 4:     /* IPv4 */
301 #if 0
302                     call_dissector(ip_handle, next_tvb, pinfo, tree);
303 #else
304                     call_dissector(ip_handle, next_tvb, pinfo, pimopt_tree);
305 #endif
306                     break;
307             case 6:     /* IPv6 */
308 #if 0
309                     call_dissector(ipv6_handle, next_tvb, pinfo, tree);
310 #else
311                     call_dissector(ipv6_handle, next_tvb, pinfo, pimopt_tree);
312 #endif
313                     break;
314             default:
315                     proto_tree_add_text(pimopt_tree, tvb, offset, -1,
316                         "Unknown IP version %d", (v_hl & 0xf0) >> 4);
317                     break;
318             }
319             break;
320           }
321
322         case 2: /* register-stop */
323           {
324             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
325                 "Group: %s",
326                 ip_to_str(tvb_get_ptr(tvb, offset, 4)));
327             offset += 4;
328             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
329                 "Source: %s",
330                 ip_to_str(tvb_get_ptr(tvb, offset, 4)));
331             offset += 4;
332             break;
333           }
334
335         case 3: /* join/prune */
336         case 6: /* graft */
337         case 7: /* graft-ack */
338           {
339             int off;
340             const char *s;
341             int ngroup, i, njoin, nprune, j;
342             guint16 holdtime;
343             guint8 mask_len;
344             guint8 adr_len;
345             proto_tree *grouptree = NULL;
346             proto_item *tigroup;
347             proto_tree *subtree = NULL;
348             proto_item *tisub;
349
350             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
351                 "Upstream-neighbor: %s",
352                 ip_to_str(tvb_get_ptr(tvb, offset, 4)));
353             offset += 4;
354
355             offset += 2;        /* skip reserved stuff */
356
357             holdtime = tvb_get_ntohs(tvb, offset);
358             proto_tree_add_text(pimopt_tree, tvb, offset, 2,
359                 "Holdtime: %u%s", holdtime,
360                 holdtime == 0xffff ? " (infty)" : "");
361             offset += 2;
362
363             offset += 1;        /* skip reserved stuff */
364
365             mask_len = tvb_get_guint8(tvb, offset);
366             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
367                 "Mask length: %u", mask_len);
368             offset += 1;
369
370             adr_len = tvb_get_guint8(tvb, offset);
371             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
372                 "Address length: %u", adr_len);
373             offset += 1;
374
375             ngroup = tvb_get_guint8(tvb, offset);
376             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
377                 "Groups: %u", ngroup);
378             offset += 1;
379
380             for (i = 0; i < ngroup; i++) {
381                 /*
382                  * XXX - does the group address have the length "adr_len"
383                  * and the group mask the length "mask_len"?
384                  */
385                 tigroup = proto_tree_add_text(pimopt_tree, tvb, offset, 4,
386                     "Group %d: %s", i,
387                     ip_to_str(tvb_get_ptr(tvb, offset, 4)));
388                 grouptree = proto_item_add_subtree(tigroup, ett_pim);
389                 offset += 4;
390
391                 proto_tree_add_text(grouptree, tvb, offset, 4,
392                     "Group %d Mask: %s", i,
393                     ip_to_str(tvb_get_ptr(tvb, offset, 4)));
394                 offset += 4;
395
396                 njoin = tvb_get_ntohs(tvb, offset);
397                 nprune = tvb_get_ntohs(tvb, offset + 2);
398
399                 tisub = proto_tree_add_text(grouptree, tvb, offset, 2,
400                     "Join: %d", njoin);
401                 subtree = proto_item_add_subtree(tisub, ett_pim);
402                 off = offset + 4;
403                 for (j = 0; j < njoin; j++) {
404                     s = dissect_pimv1_addr(tvb, off);
405                     proto_tree_add_text(subtree, tvb, off, 6,
406                         "IP address: %s", s);
407                     off += 6;
408                 }
409
410                 tisub = proto_tree_add_text(grouptree, tvb, offset + 2, 2,
411                     "Prune: %d", nprune);
412                 subtree = proto_item_add_subtree(tisub, ett_pim);
413                 for (j = 0; j < nprune; j++) {
414                     s = dissect_pimv1_addr(tvb, off);
415                     proto_tree_add_text(subtree, tvb, off, 6,
416                         "IP address: %s", s);
417                     off += 6;
418                 }
419                 offset = off;
420             }
421             break;
422           }
423
424         case 4: /* rp-reachability */
425           {
426             guint16 holdtime;
427
428             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
429                 "Group Address: %s",
430                 ip_to_str(tvb_get_ptr(tvb, offset, 4)));
431             offset += 4;
432
433             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
434                 "Group Mask: %s",
435                 ip_to_str(tvb_get_ptr(tvb, offset, 4)));
436             offset += 4;
437
438             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
439                 "RP Address: %s",
440                 ip_to_str(tvb_get_ptr(tvb, offset, 4)));
441             offset += 4;
442
443             offset += 2;        /* skip reserved stuff */
444
445             holdtime = tvb_get_ntohs(tvb, offset);
446             proto_tree_add_text(pimopt_tree, tvb, offset, 2,
447                 "Holdtime: %u%s", holdtime,
448                 holdtime == 0xffff ? " (infty)" : "");
449             offset += 2;
450             break;
451           }
452
453         case 5: /* assert */
454           {
455             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
456                 "Group Address: %s",
457                 ip_to_str(tvb_get_ptr(tvb, offset, 4)));
458             offset += 4;
459
460             proto_tree_add_text(pimopt_tree, tvb, offset, 4,
461                 "Group Mask: %s",
462                 ip_to_str(tvb_get_ptr(tvb, offset, 4)));
463             offset += 4;
464
465             proto_tree_add_text(pimopt_tree, tvb, offset, 1, "%s",
466                 decode_boolean_bitfield(tvb_get_guint8(tvb, offset), 0x80, 8,
467                     "RP Tree", "Not RP Tree"));
468             proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Preference: %u",
469                 tvb_get_ntohl(tvb, offset) & 0x7fffffff);
470             offset += 4;
471
472             proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Metric: %u",
473                 tvb_get_ntohl(tvb, offset));
474
475             break;
476           }
477
478         default:
479             break;
480         }
481     }
482 done:;
483
484     return offset+tvb_length_remaining(tvb, offset);
485 }
486
487 static const char *
488 dissect_pim_addr(tvbuff_t *tvb, int offset, enum pimv2_addrtype at,
489         int *advance) {
490     static char buf[512];
491     guint8 af;
492     guint8 et;
493     guint8 flags;
494     guint8 mask_len;
495     int len = 0;
496
497     af = tvb_get_guint8(tvb, offset);
498     if (af != AFNUM_INET && af != AFNUM_INET6) {
499         /*
500          * We don't handle the other formats, and addresses don't include
501          * a length field, so we can't even show them as raw bytes.
502          */
503         return NULL;
504     }
505
506     et = tvb_get_guint8(tvb, offset + 1);
507     if (et != 0) {
508         /*
509          * The only defined encoding type is 0, for the native encoding;
510          * again, as addresses don't include a length field, we can't
511          * even show addresses with a different encoding type as raw
512          * bytes.
513          */
514         return NULL;
515     }
516
517     switch (at) {
518     case pimv2_unicast:
519         switch (af) {
520         case AFNUM_INET:
521             len = 4;
522             (void)snprintf(buf, sizeof(buf), "%s",
523                 ip_to_str(tvb_get_ptr(tvb, offset + 2, len)));
524             break;
525
526         case AFNUM_INET6:
527             len = 16;
528             (void)snprintf(buf, sizeof(buf), "%s",
529                 ip6_to_str((const struct e_in6_addr *)tvb_get_ptr(tvb, offset + 2, len)));
530             break;
531         }
532         if (advance)
533             *advance = 2 + len;
534         break;
535
536     case pimv2_group:
537         mask_len = tvb_get_guint8(tvb, offset + 3);
538         switch (af) {
539         case AFNUM_INET:
540             len = 4;
541             (void)snprintf(buf, sizeof(buf), "%s/%u",
542                 ip_to_str(tvb_get_ptr(tvb, offset + 4, len)), mask_len);
543             break;
544
545         case AFNUM_INET6:
546             len = 16;
547             (void)snprintf(buf, sizeof(buf), "%s/%u",
548                 ip6_to_str((const struct e_in6_addr *)tvb_get_ptr(tvb, offset + 4, len)), mask_len);
549             break;
550         }
551         if (advance)
552             *advance = 4 + len;
553         break;
554
555     case pimv2_source:
556         flags = tvb_get_guint8(tvb, offset + 2);
557         mask_len = tvb_get_guint8(tvb, offset + 3);
558         switch (af) {
559         case AFNUM_INET:
560             len = 4;
561             (void)snprintf(buf, sizeof(buf), "%s/%u",
562                 ip_to_str(tvb_get_ptr(tvb, offset + 4, len)), mask_len);
563             break;
564
565         case AFNUM_INET6:
566             len = 16;
567             (void)snprintf(buf, sizeof(buf), "%s/%u",
568                 ip6_to_str((const struct e_in6_addr *)tvb_get_ptr(tvb, offset + 4, len)), mask_len);
569             break;
570         }
571         if (flags) {
572             (void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
573                 " (%s%s%s)",
574                 flags & 0x04 ? "S" : "",
575                 flags & 0x02 ? "W" : "",
576                 flags & 0x01 ? "R" : "");
577         }
578         if (advance)
579             *advance = 4 + len;
580         break;
581     default:
582         return NULL;
583     }
584
585     return buf;
586 }
587
588 static const value_string type2vals[] = {
589     { 0, "Hello" },
590     { 1, "Register" },
591     { 2, "Register-stop" },
592     { 3, "Join/Prune" },
593     { 4, "Bootstrap" },
594     { 5, "Assert" },
595     { 6, "Graft" },
596     { 7, "Graft-Ack" },
597     { 8, "Candidate-RP-Advertisement" },
598     { 0, NULL },
599 };
600
601 /*
602  * For PIM v2, see RFC 2362, and draft-ietf-pim-sm-v2-new-03 (when PIM
603  * is run over IPv6, the rules for computing the PIM checksum from the
604  * draft in question, not from RFC 2362, should be used).
605  */
606 static void
607 dissect_pim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
608     int offset = 0;
609     guint8 pim_typever;
610     guint length, pim_length;
611     guint16 pim_cksum, computed_cksum;
612     vec_t cksum_vec[4];
613     guint32 phdr[2];
614     char *typestr;
615     proto_tree *pim_tree = NULL;
616     proto_item *ti;
617     proto_tree *pimopt_tree = NULL;
618     proto_item *tiopt;
619
620     if (check_col(pinfo->cinfo, COL_PROTOCOL))
621         col_set_str(pinfo->cinfo, COL_PROTOCOL, "PIM");
622     if (check_col(pinfo->cinfo, COL_INFO))
623         col_clear(pinfo->cinfo, COL_INFO);
624
625     pim_typever = tvb_get_guint8(tvb, 0);
626
627     switch (PIM_VER(pim_typever)) {
628     case 2:
629         typestr = val_to_str(PIM_TYPE(pim_typever), type2vals, "Unknown (%u)");
630         break;
631     case 1:     /* PIMv1 - we should never see this */
632     default:
633         typestr = "Unknown";
634         break;
635     }
636
637     if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
638         col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "PIMv%d",
639             PIM_VER(pim_typever));
640     }
641     if (check_col(pinfo->cinfo, COL_INFO))
642         col_add_str(pinfo->cinfo, COL_INFO, typestr);
643
644     if (tree) {
645         ti = proto_tree_add_item(tree, proto_pim, tvb, offset, -1, FALSE);
646         pim_tree = proto_item_add_subtree(ti, ett_pim);
647
648         proto_tree_add_uint(pim_tree, hf_pim_version, tvb, offset, 1,
649             PIM_VER(pim_typever));
650         proto_tree_add_uint(pim_tree, hf_pim_type, tvb, offset, 1,
651             PIM_TYPE(pim_typever));
652
653         pim_cksum = tvb_get_ntohs(tvb, offset + 2);
654         length = tvb_length(tvb);
655         if (PIM_VER(pim_typever) == 2) {
656             /*
657              * Well, it's PIM v2, so we can check whether this is a Register
658              * message, and thus can figure out how much to checksum and
659              * whether to make the columns read-only.
660              */
661             if (PIM_TYPE(pim_typever) == 1) {
662                 /*
663                  * Register message - the PIM header is 8 bytes long.
664                  * Also set the columns non-writable. Otherwise the IPv4 or
665                  * IPv6 dissector for the encapsulated packet that caused
666                  * this register will overwrite the PIM info in the columns.
667                  */
668                 pim_length = 8;
669                 col_set_writable(pinfo->cinfo, FALSE);
670             } else {
671                 /*
672                  * Other message - checksum the entire packet.
673                  */
674                 pim_length = tvb_reported_length(tvb);
675             }
676         } else {
677             /*
678              * We don't know what type of message this is, so say that
679              * the length is 0, to force it not to be checksummed.
680              */
681             pim_length = 0;
682         }
683         if (!pinfo->fragmented && length >= pim_length) {
684             /*
685              * The packet isn't part of a fragmented datagram and isn't
686              * truncated, so we can checksum it.
687              */
688
689             switch (pinfo->src.type) {
690             case AT_IPv4:
691                 cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, pim_length);
692                 cksum_vec[0].len = pim_length;
693                 computed_cksum = in_cksum(&cksum_vec[0], 1);
694                 break;
695             case AT_IPv6:
696                 /* Set up the fields of the pseudo-header. */
697                 cksum_vec[0].ptr = pinfo->src.data;
698                 cksum_vec[0].len = pinfo->src.len;
699                 cksum_vec[1].ptr = pinfo->dst.data;
700                 cksum_vec[1].len = pinfo->dst.len;
701                 cksum_vec[2].ptr = (const guint8 *)&phdr;
702                 phdr[0] = g_htonl(pim_length);
703                 phdr[1] = g_htonl(IP_PROTO_PIM);
704                 cksum_vec[2].len = 8;
705                 cksum_vec[3].ptr = tvb_get_ptr(tvb, 0, pim_length);
706                 cksum_vec[3].len = pim_length;
707                 computed_cksum = in_cksum(&cksum_vec[0], 4);
708                 break;
709             default:
710                 /* PIM is available for IPv4 and IPv6 right now */
711                 computed_cksum = 0;     /* squelch GCC complaints */
712                 DISSECTOR_ASSERT_NOT_REACHED();
713                 break;
714             }
715
716             if (computed_cksum == 0) {
717                 proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb,
718                         offset + 2, 2, pim_cksum,
719                         "Checksum: 0x%04x (correct)",
720                         pim_cksum);
721             } else {
722                 proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb,
723                         offset + 2, 2, pim_cksum,
724                         "Checksum: 0x%04x (incorrect, should be 0x%04x)",
725                         pim_cksum, in_cksum_shouldbe(pim_cksum, computed_cksum));
726             }
727         } else {
728             proto_tree_add_uint(pim_tree, hf_pim_cksum, tvb,
729                 offset + 2, 2, pim_cksum);
730         }
731
732         offset += 4;
733
734         if (tvb_reported_length_remaining(tvb, offset) > 0) {
735             tiopt = proto_tree_add_text(pim_tree, tvb, offset, -1,
736                 "PIM parameters");
737             pimopt_tree = proto_item_add_subtree(tiopt, ett_pim);
738         } else
739             goto done;
740
741         if (PIM_VER(pim_typever) != 2)
742             goto done;
743
744         /* version 2 decoder */
745         switch (PIM_TYPE(pim_typever)) {
746         case 0: /*hello*/
747           {
748             while (tvb_reported_length_remaining(tvb, offset) >= 2) {
749                 guint16 hello_opt, opt_len;
750                 guint16 holdtime;
751                 guint16 lan_delay;
752                 guint16 override_interval;
753                 guint32 priority;
754                 guint32 opt_value = 0;
755
756                 hello_opt = tvb_get_ntohs(tvb, offset);
757                 opt_len = tvb_get_ntohs(tvb, offset + 2);
758
759                 if(opt_len == 2)
760                         opt_value = tvb_get_ntohs(tvb, offset + 4);
761                 if(opt_len == 4)
762                         opt_value = tvb_get_ntohl(tvb, offset + 4);
763
764                 switch(hello_opt) {
765                 case 1: /* holdtime */
766                         holdtime = opt_value;
767                         proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len,
768                                             "Holdtime (%u): %us %s", hello_opt, holdtime,
769                                             holdtime == 0xffff ? " (infty)" : "");
770                         break;
771                 case 2: /* LAN prune delay
772                          *
773                          * 0                   1                   2                   3
774                          * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
775                          * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
776                          * |            Type = 2           |           Length = 4          |
777                          * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
778                          * |T|       LAN Prune Delay       |       Override Interval       |
779                          * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
780                          */
781                 {
782                         proto_tree *sub_tree = NULL;
783                         proto_item *landelay_option;
784
785                         landelay_option = proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len,
786                                                         "LAN Prune Delay (%u)", hello_opt);
787                         sub_tree = proto_item_add_subtree(landelay_option, ett_pim);
788
789                         lan_delay = (opt_value & 0x7fff0000) >> 16;
790                         override_interval = opt_value & 0x0000ffff;
791                         proto_tree_add_text(sub_tree, tvb, offset + 4, 1,
792                                             "T bit is %s",
793                                             opt_value & 0x8000 ? "set" : "not set");
794                         proto_tree_add_text(sub_tree, tvb, offset + 4, 2,
795                                             "LAN Delay: %ums", lan_delay);
796                         proto_tree_add_text(sub_tree, tvb, offset + 6, 2,
797                                             "Override Interval: %ums", override_interval);
798                         break;
799                 }
800                 case 19: /* priority */
801                         priority = opt_value;
802                         proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len,
803                                         "DR Priority (%u): %u", hello_opt, priority);
804                         break;
805                 case 20: /* generation ID */
806                         proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len,
807                                             "Generation ID (%u): %d", hello_opt, opt_value);
808                         break;
809
810                 case 24: /* address list */
811                 case 65001: /* address list (old implementations) */
812                 {
813                         int i;
814                         proto_tree *sub_tree = NULL;
815                         proto_item *addrlist_option;
816
817                         addrlist_option = proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len,
818                                             "%sAddress List (%u)",
819                                             hello_opt == 65001 ? "old " : "",
820                                             hello_opt);
821                         sub_tree = proto_item_add_subtree(addrlist_option, ett_pim);
822
823                         for (i = offset + 4; i < offset + 4 + opt_len; ) {
824                                 int advance;
825                                 const char *s;
826
827                                 s = dissect_pim_addr(tvb, i, pimv2_unicast, &advance);
828                                 if (s == NULL)
829                                         break;
830                                 proto_tree_add_text(sub_tree, tvb, offset, 
831                                                     advance, "Address: %s", s);
832                                 i += advance;
833                         }
834                         break;
835                 }
836                 default:
837                         proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len,
838                                             "Unknown option (%u), length: %u, value: 0x%x",
839                                             hello_opt, opt_len, opt_value);
840                         break;
841                 }
842                 offset += 4 + opt_len;
843             }
844             break;
845           }
846
847         case 1: /* register */
848           {
849             guint32 flags;
850             guint8 v_hl;
851             tvbuff_t *next_tvb;
852             proto_tree *flag_tree = NULL;
853             proto_item *tiflag;
854
855             flags = tvb_get_ntohl(tvb, offset);
856             tiflag = proto_tree_add_text(pimopt_tree, tvb, offset, 4,
857                 "Flags: 0x%08x", flags);
858             flag_tree = proto_item_add_subtree(tiflag, ett_pim);
859             proto_tree_add_text(flag_tree, tvb, offset, 1, "%s",
860                 decode_boolean_bitfield(flags, 0x80000000, 32,
861                     "Border", "Not border"));
862             proto_tree_add_text(flag_tree, tvb, offset, 1, "%s",
863                 decode_boolean_bitfield(flags, 0x40000000, 32,
864                     "Null-Register", "Not Null-Register"));
865             offset += 4;
866
867             /*
868              * The rest of the packet is a multicast data packet.
869              */
870             next_tvb = tvb_new_subset(tvb, offset, -1, -1);
871
872             /*
873              * It's an IP packet - determine whether it's IPv4 or IPv6.
874              */
875             v_hl = tvb_get_guint8(tvb, offset);
876             switch((v_hl & 0xf0) >> 4) {
877             case 0:     /* Null-Register dummy header.
878                          * Has the same address family as the encapsulating PIM packet,
879                          * e.g. an IPv6 data packet is encapsulated in IPv6 PIM packet.
880                          */
881                     if (pinfo->src.type == AT_IPv4) {
882                             proto_tree_add_text(pimopt_tree, tvb, offset, -1,
883                                                 "IPv4 dummy header");
884                             proto_tree_add_text(pimopt_tree, tvb, offset + 12, 4,
885                                                 "Source: %s",
886                                                 ip_to_str(tvb_get_ptr(tvb, offset + 12, 4)));
887                             proto_tree_add_text(pimopt_tree, tvb, offset + 16, 4,
888                                                 "Group: %s",
889                                                 ip_to_str(tvb_get_ptr(tvb, offset + 16, 4)));
890                     } else if (pinfo->src.type == AT_IPv6) {
891                             struct ip6_hdr ip6_hdr;
892                             tvb_memcpy(tvb, (guint8 *)&ip6_hdr, offset,
893                                        sizeof ip6_hdr);
894                             proto_tree_add_text(pimopt_tree, tvb, offset, -1,
895                                                 "IPv6 dummy header");
896                             proto_tree_add_text(pimopt_tree, tvb,
897                                                 offset + offsetof(struct ip6_hdr, ip6_src), 16,
898                                                 "Source: %s",
899                                                 ip6_to_str(&ip6_hdr.ip6_src));
900                             proto_tree_add_text(pimopt_tree, tvb,
901                                                 offset + offsetof(struct ip6_hdr, ip6_dst), 16,
902                                                 "Group: %s",
903                                                 ip6_to_str(&ip6_hdr.ip6_dst));
904                     } else
905                             proto_tree_add_text(pimopt_tree, tvb, offset, -1,
906                                                 "Dummy header for an unknown protocol");
907                     break;
908             case 4:     /* IPv4 */
909 #if 0
910                     call_dissector(ip_handle, next_tvb, pinfo, tree);
911 #else
912                     call_dissector(ip_handle, next_tvb, pinfo, pimopt_tree);
913 #endif
914                     break;
915             case 6:     /* IPv6 */
916 #if 0
917                     call_dissector(ipv6_handle, next_tvb, pinfo, tree);
918 #else
919                     call_dissector(ipv6_handle, next_tvb, pinfo, pimopt_tree);
920 #endif
921                     break;
922             default:
923                     proto_tree_add_text(pimopt_tree, tvb, offset, -1,
924                         "Unknown IP version %d", (v_hl & 0xf0) >> 4);
925                     break;
926             }
927             break;
928           }
929
930         case 2: /* register-stop */
931           {
932             int advance;
933             const char *s;
934
935             s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
936             if (s == NULL)
937                 break;
938             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Group: %s", s);
939             offset += advance;
940             s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
941             if (s == NULL)
942                 break;
943             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Source: %s", s);
944             break;
945           }
946
947         case 3: /* join/prune */
948         case 6: /* graft */
949         case 7: /* graft-ack */
950           {
951             int advance;
952             int off;
953             const char *s;
954             int ngroup, i, njoin, nprune, j;
955             guint16 holdtime;
956             proto_tree *grouptree = NULL;
957             proto_item *tigroup;
958             proto_tree *subtree = NULL;
959             proto_item *tisub;
960
961             if (PIM_TYPE(pim_typever) != 7) {
962                 /* not graft-ack */
963                 s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
964                 if (s == NULL)
965                     break;
966                 proto_tree_add_text(pimopt_tree, tvb, offset, advance,
967                     "Upstream-neighbor: %s", s);
968                 offset += advance;
969             }
970
971             offset += 1;        /* skip reserved field */
972
973             ngroup = tvb_get_guint8(tvb, offset);
974             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
975                 "Groups: %u", ngroup);
976             offset += 1;
977
978             if (PIM_TYPE(pim_typever) != 7)     {
979                 /* not graft-ack */
980                 holdtime = tvb_get_ntohs(tvb, offset);
981                 proto_tree_add_text(pimopt_tree, tvb, offset, 2,
982                     "Holdtime: %u%s", holdtime,
983                     holdtime == 0xffff ? " (infty)" : "");
984             }
985             offset += 2;
986
987             for (i = 0; i < ngroup; i++) {
988                 s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
989                 if (s == NULL)
990                     goto breakbreak3;
991                 tigroup = proto_tree_add_text(pimopt_tree, tvb, offset, advance,
992                     "Group %d: %s", i, s);
993                 grouptree = proto_item_add_subtree(tigroup, ett_pim);
994                 offset += advance;
995
996                 njoin = tvb_get_ntohs(tvb, offset);
997                 nprune = tvb_get_ntohs(tvb, offset + 2);
998
999                 tisub = proto_tree_add_text(grouptree, tvb, offset, 2,
1000                     "Join: %d", njoin);
1001                 subtree = proto_item_add_subtree(tisub, ett_pim);
1002                 off = offset + 4;
1003                 for (j = 0; j < njoin; j++) {
1004                     s = dissect_pim_addr(tvb, off, pimv2_source,
1005                         &advance);
1006                     if (s == NULL)
1007                         goto breakbreak3;
1008                     proto_tree_add_text(subtree, tvb, off, advance,
1009                         "IP address: %s", s);
1010                     off += advance;
1011                 }
1012
1013                 tisub = proto_tree_add_text(grouptree, tvb, offset + 2, 2,
1014                     "Prune: %d", nprune);
1015                 subtree = proto_item_add_subtree(tisub, ett_pim);
1016                 for (j = 0; j < nprune; j++) {
1017                     s = dissect_pim_addr(tvb, off, pimv2_source,
1018                         &advance);
1019                     if (s == NULL)
1020                         goto breakbreak3;
1021                     proto_tree_add_text(subtree, tvb, off, advance,
1022                         "IP address: %s", s);
1023                     off += advance;
1024                 }
1025                 offset = off;
1026             }
1027     breakbreak3:
1028             break;
1029           }
1030
1031         case 4: /* bootstrap */
1032           {
1033             const char *s;
1034             int advance;
1035             int i, j;
1036             int frpcnt;
1037             guint16 holdtime;
1038             proto_tree *grouptree = NULL;
1039             proto_item *tigroup;
1040
1041             proto_tree_add_text(pimopt_tree, tvb, offset, 2,
1042                 "Fragment tag: 0x%04x", tvb_get_ntohs(tvb, offset));
1043             offset += 2;
1044
1045             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
1046                 "Hash mask len: %u", tvb_get_guint8(tvb, offset));
1047             offset += 1;
1048             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
1049                 "BSR priority: %u", tvb_get_guint8(tvb, offset));
1050             offset += 1;
1051
1052             s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
1053             if (s == NULL)
1054                 break;
1055             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "BSR: %s", s);
1056             offset += advance;
1057
1058             for (i = 0; tvb_reported_length_remaining(tvb, offset) > 0; i++) {
1059                 s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
1060                 if (s == NULL)
1061                     goto breakbreak4;
1062                 tigroup = proto_tree_add_text(pimopt_tree, tvb, offset, advance,
1063                     "Group %d: %s", i, s);
1064                 grouptree = proto_item_add_subtree(tigroup, ett_pim);
1065                 offset += advance;
1066
1067                 proto_tree_add_text(grouptree, tvb, offset, 1,
1068                     "RP count: %u", tvb_get_guint8(tvb, offset));
1069                 offset += 1;
1070                 frpcnt = tvb_get_guint8(tvb, offset);
1071                 proto_tree_add_text(grouptree, tvb, offset, 1,
1072                     "FRP count: %u", frpcnt);
1073                 offset += 3;
1074
1075                 for (j = 0; j < frpcnt; j++) {
1076                     s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
1077                     if (s == NULL)
1078                         goto breakbreak4;
1079                     proto_tree_add_text(grouptree, tvb, offset, advance,
1080                         "RP %d: %s", j, s);
1081                     offset += advance;
1082
1083                     holdtime = tvb_get_ntohs(tvb, offset);
1084                     proto_tree_add_text(grouptree, tvb, offset, 2,
1085                         "Holdtime: %u%s", holdtime,
1086                         holdtime == 0xffff ? " (infty)" : "");
1087                     offset += 2;
1088                     proto_tree_add_text(grouptree, tvb, offset, 1,
1089                         "Priority: %u", tvb_get_guint8(tvb, offset));
1090                     offset += 2;        /* also skips reserved field */
1091                 }
1092             }
1093
1094     breakbreak4:
1095             break;
1096           }
1097
1098         case 5: /* assert */
1099           {
1100             const char *s;
1101             int advance;
1102
1103             s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
1104             if (s == NULL)
1105                 break;
1106             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Group: %s", s);
1107             offset += advance;
1108
1109             s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
1110             if (s == NULL)
1111                 break;
1112             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Source: %s", s);
1113             offset += advance;
1114
1115             proto_tree_add_text(pimopt_tree, tvb, offset, 1, "%s",
1116                 decode_boolean_bitfield(tvb_get_guint8(tvb, offset), 0x80, 8,
1117                     "RP Tree", "Not RP Tree"));
1118             proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Preference: %u",
1119                 tvb_get_ntohl(tvb, offset) & 0x7fffffff);
1120             offset += 4;
1121
1122             proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Metric: %u",
1123                 tvb_get_ntohl(tvb, offset));
1124
1125             break;
1126           }
1127
1128         case 8: /* Candidate-RP-Advertisement */
1129           {
1130             const char *s;
1131             int advance;
1132             int pfxcnt;
1133             guint16 holdtime;
1134             int i;
1135
1136             pfxcnt = tvb_get_guint8(tvb, offset);
1137             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
1138                 "Prefix-count: %u", pfxcnt);
1139             offset += 1;
1140             proto_tree_add_text(pimopt_tree, tvb, offset, 1,
1141                 "Priority: %u", tvb_get_guint8(tvb, offset));
1142             offset += 1;
1143             holdtime = tvb_get_ntohs(tvb, offset);
1144             proto_tree_add_text(pimopt_tree, tvb, offset, 2,
1145                 "Holdtime: %u%s", holdtime,
1146                 holdtime == 0xffff ? " (infty)" : "");
1147             offset += 2;
1148
1149             s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
1150             if (s == NULL)
1151                 break;
1152             proto_tree_add_text(pimopt_tree, tvb, offset, advance, "RP: %s", s);
1153             offset += advance;
1154
1155             for (i = 0; i < pfxcnt; i++) {
1156                 s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
1157                 if (s == NULL)
1158                     goto breakbreak8;
1159                 proto_tree_add_text(pimopt_tree, tvb, offset, advance,
1160                     "Group %d: %s", i, s);
1161                 offset += advance;
1162             }
1163     breakbreak8:
1164             break;
1165           }
1166
1167         default:
1168             break;
1169         }
1170     }
1171 done:;
1172 }
1173
1174 void
1175 proto_register_pim(void)
1176 {
1177     static hf_register_info hf[] = {
1178       { &hf_pim_version,
1179         { "Version",            "pim.version",
1180                                 FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
1181       { &hf_pim_type,
1182         { "Type",               "pim.type",
1183                                 FT_UINT8, BASE_DEC, VALS(type2vals), 0x0, "", HFILL }},
1184       { &hf_pim_code,
1185         { "Code",               "pim.code",
1186                                 FT_UINT8, BASE_DEC, VALS(type1vals), 0x0, "", HFILL }},
1187       { &hf_pim_cksum,
1188         { "Checksum",           "pim.cksum",
1189                                 FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
1190     };
1191     static gint *ett[] = {
1192         &ett_pim,
1193     };
1194
1195     proto_pim = proto_register_protocol("Protocol Independent Multicast",
1196         "PIM", "pim");
1197     proto_register_field_array(proto_pim, hf, array_length(hf));
1198     proto_register_subtree_array(ett, array_length(ett));
1199 }
1200
1201 void
1202 proto_reg_handoff_pim(void)
1203 {
1204     dissector_handle_t pim_handle;
1205
1206     pim_handle = create_dissector_handle(dissect_pim, proto_pim);
1207     dissector_add("ip.proto", IP_PROTO_PIM, pim_handle);
1208
1209     /*
1210      * Get handles for the IPv4 and IPv6 dissectors.
1211      */
1212     ip_handle = find_dissector("ip");
1213     ipv6_handle = find_dissector("ipv6");
1214 }