It's the AppleTalk Session Protocol, not the AppleTalk Stream Protocol.
[obnox/wireshark/wip.git] / packet-pim.c
index 5b450ced74dbc9f4c1db21ff558adda534f1e2af..480410dd7bcb66948202c9e13d183170026043e1 100644 (file)
@@ -2,13 +2,12 @@
  * Routines for PIM disassembly
  * (c) Copyright Jun-ichiro itojun Hagino <itojun@itojun.org>
  *
- * $Id: packet-pim.c,v 1.6 1999/10/21 15:06:02 gram Exp $
+ * $Id: packet-pim.c,v 1.40 2002/04/22 07:41:32 guy Exp $
  *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
+ * By Gerald Combs <gerald@ethereal.com>
  * Copyright 1998 Gerald Combs
  * 
- * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
-#include "config.h"
+
+#ifdef HAVE_CONFIG_H 
+# include "config.h"
+#endif
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #include <netinet/in.h>
 #endif
 
+#include <stddef.h>  /* For offsetof */
+#include <string.h>
+#include <glib.h>
+
 #ifdef NEED_SNPRINTF_H
-# ifdef HAVE_STDARG_H
-#  include <stdarg.h>
-# else
-#  include <varargs.h>
-# endif
 # include "snprintf.h"
 #endif
 
-#include <glib.h>
-#include "packet.h"
+#include <epan/packet.h>
+#include "ipproto.h"
+#include "afn.h"
 #include "packet-ipv6.h"
+#include "in_cksum.h"
 
-#ifndef offsetof
-#define        offsetof(type, member)  ((size_t)(&((type *)0)->member))
-#endif
-
-struct pim {
-       guint8  pim_typever;
 #define PIM_TYPE(x)    ((x) & 0x0f)
 #define PIM_VER(x)     (((x) & 0xf0) >> 4)
-       guint8  pim_rsv;        /* Reserved */
-       guint16 pim_cksum;      /* IP style check sum */
-};
 
 enum pimv2_addrtype {
        pimv2_unicast, pimv2_group, pimv2_source
@@ -66,88 +59,524 @@ enum pimv2_addrtype {
 static int proto_pim = -1;
 static int hf_pim_version = -1;
 static int hf_pim_type = -1;
+static int hf_pim_code = -1;
 static int hf_pim_cksum = -1;
 
+static gint ett_pim = -1;
+
+static dissector_handle_t ip_handle;
+static dissector_handle_t ipv6_handle;
+
+/*
+ * For PIM v1, see the PDF slides at
+ *
+ *     http://www.mbone.de/training/Module3.pdf
+ *
+ * Is it documented anywhere else?
+ */
 static const char *
-dissect_pim_addr(const u_char *bp, const u_char *ep, enum pimv2_addrtype at,
+dissect_pimv1_addr(tvbuff_t *tvb, int offset) {
+    static char buf[512];
+    guint16 flags_masklen;
+
+    flags_masklen = tvb_get_ntohs(tvb, offset);
+    if (flags_masklen & 0x0180) {
+       (void)snprintf(buf, sizeof(buf),
+           "(%s%s%s) ",
+           flags_masklen & 0x0100 ? "S" : "",
+           flags_masklen & 0x0080 ? "W" : "",
+           flags_masklen & 0x0040 ? "R" : "");
+    } else
+       buf[0] = '\0';
+    (void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%s/%u",
+       ip_to_str(tvb_get_ptr(tvb, offset + 2, 4)), flags_masklen & 0x3f);
+
+    return buf;
+}
+
+static const value_string type1vals[] = {
+    { 0, "Query" },
+    { 1, "Register" },
+    { 2, "Register-stop" },
+    { 3, "Join/Prune" },
+    { 4, "RP-Reachable" },
+    { 5, "Assert" },
+    { 6, "Graft" },
+    { 7, "Graft-Ack" },
+    { 8, "Mode" },
+    { 0, NULL },
+};
+
+/* This function is only called from the IGMP dissector */
+int
+dissect_pimv1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+             int offset) {
+    guint8 pim_type;
+    guint8 pim_ver;
+    guint length, pim_length;
+    guint16 pim_cksum, computed_cksum;
+    vec_t cksum_vec[1];
+    proto_tree *pim_tree = NULL;
+    proto_item *ti; 
+    proto_tree *pimopt_tree = NULL;
+    proto_item *tiopt; 
+
+    if (!proto_is_protocol_enabled(proto_pim)) {
+       /*
+        * We are not enabled; skip entire packet to be nice to the
+        * IGMP layer (so clicking on IGMP will display the data).
+        */
+       return offset+tvb_length_remaining(tvb, offset);
+    }
+
+    if (check_col(pinfo->cinfo, COL_PROTOCOL))
+        col_set_str(pinfo->cinfo, COL_PROTOCOL, "PIMv1");
+    if (check_col(pinfo->cinfo, COL_INFO))
+       col_clear(pinfo->cinfo, COL_INFO);
+
+    if (tree) {
+       ti = proto_tree_add_item(tree, proto_pim, tvb, offset, -1, FALSE);
+       pim_tree = proto_item_add_subtree(ti, ett_pim);
+
+       /* Put IGMP type, 0x14, into the tree */
+       proto_tree_add_text(pim_tree, tvb, offset, 1,
+           "Type: PIM (0x14)");
+    }
+    offset += 1;
+
+    pim_type = tvb_get_guint8(tvb, offset);
+    if (check_col(pinfo->cinfo, COL_INFO))
+       col_add_str(pinfo->cinfo, COL_INFO,
+           val_to_str(pim_type, type1vals, "Unknown (%u)"));
+
+    if (tree) {
+       proto_tree_add_uint(pim_tree, hf_pim_code, tvb, offset, 1, pim_type);
+    }
+    offset += 1;
+
+    pim_cksum = tvb_get_ntohs(tvb, offset);
+    pim_ver = PIM_VER(tvb_get_guint8(tvb, offset + 2));
+    if (pim_ver != 1) {
+       /*
+        * Not PIMv1 - what gives?
+        */
+       if (tree) {
+           proto_tree_add_uint(pim_tree, hf_pim_cksum, tvb,
+                   offset, 2, pim_cksum);
+       }
+       offset += 2;
+       if (tree)
+           proto_tree_add_uint(pim_tree, hf_pim_version, tvb, offset, 1, pim_ver);
+       return offset+tvb_length_remaining(tvb, offset);
+    }
+
+    /*
+     * Well, it's PIM v1, so we can check whether this is a
+     * Register message, and thus can figure out how much to
+     * checksum and whether to make the columns read-only.
+     */
+    length = tvb_length(tvb);
+    if (pim_type == 1) {
+       /*
+        * Register message - the PIM header is 8 bytes long.
+        * Also set the columns non-writable. Otherwise the IPv4 or
+        * IPv6 dissector for the encapsulated packet that caused
+        * this register will overwrite the PIM info in the columns.
+        */
+       pim_length = 8;
+       col_set_writable(pinfo->cinfo, FALSE);
+    } else {
+       /*
+        * Other message - checksum the entire packet.
+        */
+       pim_length = tvb_reported_length(tvb);
+    }
+
+    if (tree) {
+       if (!pinfo->fragmented && length >= pim_length) {
+           /*
+            * The packet isn't part of a fragmented datagram and isn't
+            * truncated, so we can checksum it.
+            */
+           cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, pim_length);
+           cksum_vec[0].len = pim_length;
+           computed_cksum = in_cksum(&cksum_vec[0], 1);
+           if (computed_cksum == 0) {
+               proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb,
+                           offset, 2, pim_cksum,
+                           "Checksum: 0x%04x (correct)",
+                           pim_cksum);
+           } else {
+               proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb,
+                           offset, 2, pim_cksum,
+                           "Checksum: 0x%04x (incorrect, should be 0x%04x)",
+                           pim_cksum, in_cksum_shouldbe(pim_cksum, computed_cksum));
+           }
+       } else {
+           proto_tree_add_uint(pim_tree, hf_pim_cksum, tvb,
+                   offset, 2, pim_cksum);
+        }
+    }
+    offset += 2;
+
+    if (tree)
+       proto_tree_add_uint(pim_tree, hf_pim_version, tvb, offset, 1, pim_ver);
+    offset += 1;
+
+    offset += 3;       /* skip reserved stuff */
+
+    if (tree) {
+       if (tvb_reported_length_remaining(tvb, offset) > 0) {
+           tiopt = proto_tree_add_text(pim_tree, tvb, offset, -1,
+                   "PIM parameters");
+           pimopt_tree = proto_item_add_subtree(tiopt, ett_pim);
+       } else
+           goto done;
+
+       /* version 1 decoder */
+       switch (pim_type) {
+       case 0: /* query */
+         {
+           guint8 mode;
+           guint16 holdtime;
+           static const value_string pimv1_modevals[] = {
+               { 0, "Dense" },
+               { 1, "Sparse" },
+               { 2, "Sparse-Dense" },
+               { 0, NULL },
+           };
+
+           mode = tvb_get_guint8(tvb, offset) >> 4;
+           proto_tree_add_text(pimopt_tree, tvb, offset, 1,
+               "Mode: %s",
+               val_to_str(mode, pimv1_modevals, "Unknown (%u)"));
+           offset += 2;
+           holdtime = tvb_get_ntohs(tvb, offset);
+           proto_tree_add_text(pimopt_tree, tvb, offset, 2,
+               "Holdtime: %u%s", holdtime,
+               holdtime == 0xffff ? " (infty)" : "");
+           offset += 2;
+           break;
+         }
+
+       case 1: /* register */
+         {
+           guint8 v_hl;
+           tvbuff_t *next_tvb;
+
+           /*
+            * The rest of the packet is a multicast data packet.
+            */
+           next_tvb = tvb_new_subset(tvb, offset, -1, -1);
+
+           /*
+            * It's an IP packet - determine whether it's IPv4 or IPv6.
+            */
+           v_hl = tvb_get_guint8(tvb, offset);
+           switch((v_hl & 0xf0) >> 4) {
+           case 0:     /* Null-Register dummy header.
+                        * Has the same address family as the encapsulating PIM packet,
+                        * e.g. an IPv6 data packet is encapsulated in IPv6 PIM packet.
+                        */
+                   if (pinfo->src.type == AT_IPv4) {
+                           proto_tree_add_text(pimopt_tree, tvb, offset, -1,
+                                               "IPv4 dummy header");
+                           proto_tree_add_text(pimopt_tree, tvb, offset + 12, 4,
+                                               "Source: %s",
+                                               ip_to_str(tvb_get_ptr(tvb, offset + 12, 4)));
+                           proto_tree_add_text(pimopt_tree, tvb, offset + 16, 4,
+                                               "Group: %s",
+                                               ip_to_str(tvb_get_ptr(tvb, offset + 16, 4)));
+                   } else if (pinfo->src.type == AT_IPv6) {
+                           struct ip6_hdr ip6_hdr;
+                           tvb_memcpy(tvb, (guint8 *)&ip6_hdr, offset,
+                                      sizeof ip6_hdr);
+                           proto_tree_add_text(pimopt_tree, tvb, offset, -1,
+                                               "IPv6 dummy header");
+                           proto_tree_add_text(pimopt_tree, tvb,
+                                               offset + offsetof(struct ip6_hdr, ip6_src), 16,
+                                               "Source: %s",
+                                               ip6_to_str(&ip6_hdr.ip6_src));
+                           proto_tree_add_text(pimopt_tree, tvb,
+                                               offset + offsetof(struct ip6_hdr, ip6_dst), 16,
+                                               "Group: %s",
+                                               ip6_to_str(&ip6_hdr.ip6_dst));
+                   } else
+                           proto_tree_add_text(pimopt_tree, tvb, offset, -1,
+                                               "Dummy header for an unknown protocol");
+                   break;
+           case 4:     /* IPv4 */
+#if 0
+                   call_dissector(ip_handle, next_tvb, pinfo, tree);
+#else
+                   call_dissector(ip_handle, next_tvb, pinfo, pimopt_tree);
+#endif
+                   break;
+           case 6:     /* IPv6 */
+#if 0
+                   call_dissector(ipv6_handle, next_tvb, pinfo, tree);
+#else
+                   call_dissector(ipv6_handle, next_tvb, pinfo, pimopt_tree);
+#endif
+                   break;
+           default:
+                   proto_tree_add_text(pimopt_tree, tvb, offset, -1,
+                       "Unknown IP version %d", (v_hl & 0xf0) >> 4);
+                   break;
+           }
+           break;
+         }
+
+       case 2: /* register-stop */
+         {
+           proto_tree_add_text(pimopt_tree, tvb, offset, 4,
+               "Group: %s",
+               ip_to_str(tvb_get_ptr(tvb, offset, 4)));
+           offset += 4;
+           proto_tree_add_text(pimopt_tree, tvb, offset, 4,
+               "Source: %s",
+               ip_to_str(tvb_get_ptr(tvb, offset, 4)));
+           offset += 4;
+           break;
+         }
+
+       case 3: /* join/prune */
+       case 6: /* graft */
+       case 7: /* graft-ack */
+         {
+           int off;
+           const char *s;
+           int ngroup, i, njoin, nprune, j;
+           guint16 holdtime;
+           guint8 mask_len;
+           guint8 adr_len;
+           proto_tree *grouptree = NULL;
+           proto_item *tigroup; 
+           proto_tree *subtree = NULL;
+           proto_item *tisub; 
+
+           proto_tree_add_text(pimopt_tree, tvb, offset, 4,
+               "Upstream-neighbor: %s",
+               ip_to_str(tvb_get_ptr(tvb, offset, 4)));
+           offset += 4;
+
+           offset += 2;        /* skip reserved stuff */
+
+           holdtime = tvb_get_ntohs(tvb, offset);
+           proto_tree_add_text(pimopt_tree, tvb, offset, 2,
+               "Holdtime: %u%s", holdtime,
+               holdtime == 0xffff ? " (infty)" : "");
+           offset += 2;
+
+           offset += 1;        /* skip reserved stuff */
+
+           mask_len = tvb_get_guint8(tvb, offset);
+           proto_tree_add_text(pimopt_tree, tvb, offset, 1,
+               "Mask length: %u", mask_len);
+           offset += 1;
+
+           adr_len = tvb_get_guint8(tvb, offset);
+           proto_tree_add_text(pimopt_tree, tvb, offset, 1,
+               "Address length: %u", adr_len);
+           offset += 1;
+
+           ngroup = tvb_get_guint8(tvb, offset);
+           proto_tree_add_text(pimopt_tree, tvb, offset, 1,
+               "Groups: %u", ngroup);
+           offset += 1;
+
+           for (i = 0; i < ngroup; i++) {
+               tigroup = proto_tree_add_text(pimopt_tree, tvb, offset, 4,
+                   "Group %d: %s", i,
+                   ip_to_str(tvb_get_ptr(tvb, offset, 4)));
+               grouptree = proto_item_add_subtree(tigroup, ett_pim);
+               offset += 4;
+
+               proto_tree_add_text(grouptree, tvb, offset, 4,
+                   "Group %d Mask: %s", i,
+                   ip_to_str(tvb_get_ptr(tvb, offset, 4)));
+               offset += 4;
+
+               njoin = tvb_get_ntohs(tvb, offset);
+               nprune = tvb_get_ntohs(tvb, offset + 2);
+
+               tisub = proto_tree_add_text(grouptree, tvb, offset, 2,
+                   "Join: %d", njoin);
+               subtree = proto_item_add_subtree(tisub, ett_pim);
+               off = offset + 4;
+               for (j = 0; j < njoin; j++) {
+                   s = dissect_pimv1_addr(tvb, off);
+                   proto_tree_add_text(subtree, tvb, off, 6,
+                       "IP address: %s", s);
+                   off += 6;
+               }
+
+               tisub = proto_tree_add_text(grouptree, tvb, offset + 2, 2,
+                   "Prune: %d", nprune);
+               subtree = proto_item_add_subtree(tisub, ett_pim);
+               for (j = 0; j < nprune; j++) {
+                   s = dissect_pimv1_addr(tvb, off);
+                   proto_tree_add_text(subtree, tvb, off, 6,
+                       "IP address: %s", s);
+                   off += 6;
+               }
+           }
+           break;
+         }
+
+       case 4: /* rp-reachability */
+         {
+           guint16 holdtime;
+
+           proto_tree_add_text(pimopt_tree, tvb, offset, 4,
+               "Group Address: %s",
+               ip_to_str(tvb_get_ptr(tvb, offset, 4)));
+           offset += 4;
+
+           proto_tree_add_text(pimopt_tree, tvb, offset, 4,
+               "Group Mask: %s",
+               ip_to_str(tvb_get_ptr(tvb, offset, 4)));
+           offset += 4;
+
+           proto_tree_add_text(pimopt_tree, tvb, offset, 4,
+               "RP Address: %s",
+               ip_to_str(tvb_get_ptr(tvb, offset, 4)));
+           offset += 4;
+
+           offset += 2;        /* skip reserved stuff */
+
+           holdtime = tvb_get_ntohs(tvb, offset);
+           proto_tree_add_text(pimopt_tree, tvb, offset, 2,
+               "Holdtime: %u%s", holdtime,
+               holdtime == 0xffff ? " (infty)" : "");
+           offset += 2;
+           break;
+         }
+
+       case 5: /* assert */
+         {
+           proto_tree_add_text(pimopt_tree, tvb, offset, 4,
+               "Group Address: %s",
+               ip_to_str(tvb_get_ptr(tvb, offset, 4)));
+           offset += 4;
+
+           proto_tree_add_text(pimopt_tree, tvb, offset, 4,
+               "Group Mask: %s",
+               ip_to_str(tvb_get_ptr(tvb, offset, 4)));
+           offset += 4;
+
+           proto_tree_add_text(pimopt_tree, tvb, offset, 1, "%s",
+               decode_boolean_bitfield(tvb_get_guint8(tvb, offset), 0x80, 8,
+                   "RP Tree", "Not RP Tree"));
+           proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Preference: %u",
+               tvb_get_ntohl(tvb, offset) & 0x7fffffff);
+           offset += 4;
+
+           proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Metric: %u",
+               tvb_get_ntohl(tvb, offset));
+
+           break;
+         }
+
+       default:
+           break;
+       }
+    }
+done:;
+
+    return offset+tvb_length_remaining(tvb, offset);
+}
+
+static const char *
+dissect_pim_addr(tvbuff_t *tvb, int offset, enum pimv2_addrtype at,
        int *advance) {
     static char buf[512];
-    int af;
+    guint8 af;
+    guint8 et;
+    guint8 flags;
+    guint8 mask_len;
     int len = 0;
 
-    if (bp >= ep)
-       return NULL;
-
-    switch (bp[0]) {
-    case 1:
-       af = 4;
-       break;
-    case 2:
-       af = 6;
-       break;
-    default:
+    af = tvb_get_guint8(tvb, offset);
+    if (af != AFNUM_INET && af != AFNUM_INET6) {
+       /*
+        * We don't handle the other formats, and addresses don't include
+        * a length field, so we can't even show them as raw bytes.
+        */
        return NULL;
     }
 
-    if (bp[1] != 0)
+    et = tvb_get_guint8(tvb, offset + 1);
+    if (et != 0) {
+       /*
+        * The only defined encoding type is 0, for the native encoding;
+        * again, as addresses don't include a length field, we can't
+        * even show addresses with a different encoding type as raw
+        * bytes.
+        */
        return NULL;
+    }
 
     switch (at) {
     case pimv2_unicast:
-       if (af == 4) {
+       switch (af) {
+       case AFNUM_INET:
            len = 4;
-           if (bp + 2 + len > ep)
-               return NULL;
-           (void)snprintf(buf, sizeof(buf), "%s", ip_to_str(bp + 2));
-       }
-       else if (af == 6) {
+           (void)snprintf(buf, sizeof(buf), "%s",
+               ip_to_str(tvb_get_ptr(tvb, offset + 2, len)));
+           break;
+
+       case AFNUM_INET6:
            len = 16;
-           if (bp + 2 + len > ep)
-               return NULL;
            (void)snprintf(buf, sizeof(buf), "%s",
-               ip6_to_str((struct e_in6_addr *)(bp + 2)));
+               ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset + 2, len)));
+           break;
        }
        if (advance)
            *advance = 2 + len;
        break;
 
     case pimv2_group:
-       if (af == 4) {
+       mask_len = tvb_get_guint8(tvb, offset + 3);
+       switch (af) {
+       case AFNUM_INET:
            len = 4;
-           if (bp + 4 + len > ep)
-               return NULL;
-           (void)snprintf(buf, sizeof(buf), "%s/%u", ip_to_str(bp + 4), bp[3]);
-       }
-       else if (af == 6) {
+           (void)snprintf(buf, sizeof(buf), "%s/%u",
+               ip_to_str(tvb_get_ptr(tvb, offset + 4, len)), mask_len);
+           break;
+
+       case AFNUM_INET6:
            len = 16;
-           if (bp + 4 + len > ep)
-               return NULL;
            (void)snprintf(buf, sizeof(buf), "%s/%u",
-               ip6_to_str((struct e_in6_addr *)(bp + 4)), bp[3]);
+               ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset + 4, len)), mask_len);
+           break;
        }
        if (advance)
            *advance = 4 + len;
        break;
+
     case pimv2_source:
-       if (af == 4) {
+       flags = tvb_get_guint8(tvb, offset + 2);
+       mask_len = tvb_get_guint8(tvb, offset + 3);
+       switch (af) {
+       case AFNUM_INET:
            len = 4;
-           if (bp + 4 + len > ep)
-               return NULL;
-           (void)snprintf(buf, sizeof(buf), "%s/%u", ip_to_str(bp + 4), bp[3]);
-       }
-       else if (af == 6) {
+           (void)snprintf(buf, sizeof(buf), "%s/%u",
+               ip_to_str(tvb_get_ptr(tvb, offset + 4, len)), mask_len);
+           break;
+
+       case AFNUM_INET6:
            len = 16;
-           if (bp + 4 + len > ep)
-               return NULL;
            (void)snprintf(buf, sizeof(buf), "%s/%u",
-               ip6_to_str((struct e_in6_addr *)(bp + 4)), bp[3]);
+               ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset + 4, len)), mask_len);
+           break;
        }
-       if (bp[2]) {
+       if (flags) {
            (void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
                " (%s%s%s)",
-               bp[2] & 0x04 ? "S" : "",
-               bp[2] & 0x02 ? "W" : "",
-               bp[2] & 0x01 ? "R" : "");
+               flags & 0x04 ? "S" : "",
+               flags & 0x02 ? "W" : "",
+               flags & 0x01 ? "R" : "");
        }
        if (advance)
            *advance = 4 + len;
@@ -159,143 +588,308 @@ dissect_pim_addr(const u_char *bp, const u_char *ep, enum pimv2_addrtype at,
     return buf;
 }
 
-void 
-dissect_pim(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
-    struct pim pim;
-    static const value_string type1vals[] = {
-       { 0, "Query" },
-       { 1, "Register" },
-       { 2, "Register-stop" },
-       { 3, "Join/Prune" },
-       { 4, "RP-Reachable" },
-       { 5, "Assert" },
-       { 6, "Graft" },
-       { 7, "Graft-Ack" },
-       { 8, "Mode" },
-       { 0, NULL },
-    };
-    static const value_string type2vals[] = {
-       { 0, "Hello" },
-       { 1, "Register" },
-       { 2, "Register-stop" },
-       { 3, "Join/Prune" },
-       { 4, "Bootstrap" },
-       { 5, "Assert" },
-       { 6, "Graft" },
-       { 7, "Graft-Ack" },
-       { 8, "Candidate-RP-Advertisement" },
-       { 0, NULL },
-    };
+static const value_string type2vals[] = {
+    { 0, "Hello" },
+    { 1, "Register" },
+    { 2, "Register-stop" },
+    { 3, "Join/Prune" },
+    { 4, "Bootstrap" },
+    { 5, "Assert" },
+    { 6, "Graft" },
+    { 7, "Graft-Ack" },
+    { 8, "Candidate-RP-Advertisement" },
+    { 0, NULL },
+};
+
+/*
+ * For PIM v2, see RFC 2362, and draft-ietf-pim-sm-v2-new-03 (when PIM
+ * is run over IPv6, the rules for computing the PIM checksum from the
+ * draft in question, not from RFC 2362, should be used).
+ */
+static void 
+dissect_pim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
+    int offset = 0;
+    guint8 pim_typever;
+    guint length, pim_length;
+    guint16 pim_cksum, computed_cksum;
+    vec_t cksum_vec[4];
+    guint32 phdr[2];
     char *typestr;
     proto_tree *pim_tree = NULL;
-       proto_item *ti; 
+    proto_item *ti; 
     proto_tree *pimopt_tree = NULL;
-       proto_item *tiopt; 
+    proto_item *tiopt; 
 
-    /* avoid alignment problem */
-    memcpy(&pim, &pd[offset], sizeof(pim));
+    if (check_col(pinfo->cinfo, COL_PROTOCOL))
+        col_set_str(pinfo->cinfo, COL_PROTOCOL, "PIM");
+    if (check_col(pinfo->cinfo, COL_INFO))
+       col_clear(pinfo->cinfo, COL_INFO);
 
-    switch (PIM_VER(pim.pim_typever)) {
-    case 1:
-       typestr = val_to_str(PIM_TYPE(pim.pim_typever), type1vals, "Unknown");
-       break;
+    pim_typever = tvb_get_guint8(tvb, 0);
+
+    switch (PIM_VER(pim_typever)) {
     case 2:
-       typestr = val_to_str(PIM_TYPE(pim.pim_typever), type2vals, "Unknown");
+       typestr = val_to_str(PIM_TYPE(pim_typever), type2vals, "Unknown (%u)");
        break;
+    case 1:    /* PIMv1 - we should never see this */
     default:
        typestr = "Unknown";
        break;
     }
 
-    if (check_col(fd, COL_PROTOCOL)) {
-        col_add_fstr(fd, COL_PROTOCOL, "PIM version %d",
-           PIM_VER(pim.pim_typever));
+    if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
+        col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "PIMv%d",
+           PIM_VER(pim_typever));
     }
-    if (check_col(fd, COL_INFO))
-       col_add_fstr(fd, COL_INFO, "%s", typestr); 
+    if (check_col(pinfo->cinfo, COL_INFO))
+       col_add_str(pinfo->cinfo, COL_INFO, typestr); 
 
     if (tree) {
-       ti = proto_tree_add_item(tree, proto_pim, offset, END_OF_FRAME, NULL);
-       pim_tree = proto_item_add_subtree(ti, ETT_PIM);
-
-       proto_tree_add_item(pim_tree, hf_pim_version, offset, 1,
-           PIM_VER(pim.pim_typever)); 
-       proto_tree_add_item_format(pim_tree, hf_pim_type, offset, 1,
-           PIM_TYPE(pim.pim_typever),
-           "Type: %s (%u)", typestr, PIM_TYPE(pim.pim_typever)); 
-
-       proto_tree_add_item(pim_tree, hf_pim_cksum,
-           offset + offsetof(struct pim, pim_cksum), sizeof(pim.pim_cksum),
-           ntohs(pim.pim_cksum));
-
-       if (sizeof(struct pim) < END_OF_FRAME) {
-           tiopt = proto_tree_add_text(pim_tree,
-               offset + sizeof(struct pim), END_OF_FRAME,
-               "PIM parameters");
-           pimopt_tree = proto_item_add_subtree(tiopt, ETT_PIM);
+       ti = proto_tree_add_item(tree, proto_pim, tvb, offset, -1, FALSE);
+       pim_tree = proto_item_add_subtree(ti, ett_pim);
+
+       proto_tree_add_uint(pim_tree, hf_pim_version, tvb, offset, 1,
+           PIM_VER(pim_typever)); 
+       proto_tree_add_uint(pim_tree, hf_pim_type, tvb, offset, 1,
+           PIM_TYPE(pim_typever)); 
+
+       pim_cksum = tvb_get_ntohs(tvb, offset + 2);
+       length = tvb_length(tvb);
+       if (PIM_VER(pim_typever) == 2) {
+           /*
+            * Well, it's PIM v2, so we can check whether this is a Register
+            * message, and thus can figure out how much to checksum and
+            * whether to make the columns read-only.
+            */
+           if (PIM_TYPE(pim_typever) == 1) {
+               /*
+                * Register message - the PIM header is 8 bytes long.
+                * Also set the columns non-writable. Otherwise the IPv4 or
+                * IPv6 dissector for the encapsulated packet that caused
+                * this register will overwrite the PIM info in the columns.
+                */
+               pim_length = 8;
+               col_set_writable(pinfo->cinfo, FALSE);
+           } else {
+               /*
+                * Other message - checksum the entire packet.
+                */
+               pim_length = tvb_reported_length(tvb);
+           }
+       } else {
+           /*
+            * We don't know what type of message this is, so say that
+            * the length is 0, to force it not to be checksummed.
+            */
+           pim_length = 0;
+       }
+       if (!pinfo->fragmented && length >= pim_length) {
+           /*
+            * The packet isn't part of a fragmented datagram and isn't
+            * truncated, so we can checksum it.
+            */
+
+           switch (pinfo->src.type) {
+           case AT_IPv4:
+               cksum_vec[0].ptr = tvb_get_ptr(tvb, 0, pim_length);
+               cksum_vec[0].len = pim_length;
+               computed_cksum = in_cksum(&cksum_vec[0], 1);
+               break;
+           case AT_IPv6:
+               /* Set up the fields of the pseudo-header. */
+               cksum_vec[0].ptr = pinfo->src.data;
+               cksum_vec[0].len = pinfo->src.len;
+               cksum_vec[1].ptr = pinfo->dst.data;
+               cksum_vec[1].len = pinfo->dst.len;
+               cksum_vec[2].ptr = (const guint8 *)&phdr;
+               phdr[0] = htonl(pim_length);
+               phdr[1] = htonl(IP_PROTO_PIM);
+               cksum_vec[2].len = 8;
+               cksum_vec[3].ptr = tvb_get_ptr(tvb, 0, pim_length);
+               cksum_vec[3].len = pim_length;
+               computed_cksum = in_cksum(&cksum_vec[0], 4);
+               break;
+           default:
+               /* PIM is available for IPv4 and IPv6 right now */
+               computed_cksum = 0;     /* squelch GCC complaints */
+               g_assert_not_reached();
+               break;
+           }
+
+           if (computed_cksum == 0) {
+               proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb,
+                       offset + 2, 2, pim_cksum,
+                       "Checksum: 0x%04x (correct)",
+                       pim_cksum);
+           } else {
+               proto_tree_add_uint_format(pim_tree, hf_pim_cksum, tvb,
+                       offset + 2, 2, pim_cksum,
+                       "Checksum: 0x%04x (incorrect, should be 0x%04x)",
+                       pim_cksum, in_cksum_shouldbe(pim_cksum, computed_cksum));
+           }
+       } else {
+           proto_tree_add_uint(pim_tree, hf_pim_cksum, tvb,
+               offset + 2, 2, pim_cksum);
+       }
+
+       offset += 4;
+
+       if (tvb_reported_length_remaining(tvb, offset) > 0) {
+           tiopt = proto_tree_add_text(pim_tree, tvb, offset, -1,
+               "PIM parameters");
+           pimopt_tree = proto_item_add_subtree(tiopt, ett_pim);
        } else
            goto done;
 
-       if (PIM_VER(pim.pim_typever) != 2)
+       if (PIM_VER(pim_typever) != 2)
            goto done;
 
        /* version 2 decoder */
-       switch (PIM_TYPE(pim.pim_typever)) {
+       switch (PIM_TYPE(pim_typever)) {
        case 0: /*hello*/
          {
-           guint16 *w;
-           w = (guint16 *)&pd[offset + sizeof(struct pim)];
-           while ((guint8 *)w < &pd[offset + END_OF_FRAME]) {
-               if (ntohs(w[0]) == 1 && ntohs(w[1]) == 2
-                && (guint8 *)&w[3] <= &pd[offset + END_OF_FRAME]) {
-                   proto_tree_add_text(pimopt_tree, (guint8 *)w - pd, 6,
-                       "Holdtime: %u%s", ntohs(w[2]),
-                       ntohs(w[2]) == 0xffff ? " (infty)" : "");
-                   w += 3;
-               } else
-                   break;
+           while (tvb_reported_length_remaining(tvb, offset) >= 2) {
+               guint16 hello_opt, opt_len;
+               guint16 holdtime;
+               guint16 lan_delay;
+               guint16 override_interval;
+               guint32 priority;
+               guint32 opt_value = 0;
+
+               hello_opt = tvb_get_ntohs(tvb, offset);
+               opt_len = tvb_get_ntohs(tvb, offset + 2);
+               if(opt_len == 2)
+                       opt_value = tvb_get_ntohs(tvb, offset + 4);
+               if(opt_len == 4)
+                       opt_value = tvb_get_ntohl(tvb, offset + 4);
+               switch(hello_opt) {
+               case 1: /* holdtime */
+                       holdtime = opt_value;
+                       proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len,
+                                           "Holdtime: %u%s", holdtime,
+                                           holdtime == 0xffff ? " (infty)" : "");
+                       break;
+               case 2: /* LAN prune delay */
+               {
+                       proto_tree *sub_tree = NULL;
+                       proto_item *landelay_option;
+                       landelay_option = proto_tree_add_text(pim_tree, tvb, offset, -1,
+                                                       "LAN Prune Delay");
+                       sub_tree = proto_item_add_subtree(landelay_option, ett_pim);
+                       lan_delay = (opt_value & 0x7f00) >> 8;
+                       override_interval = opt_value & 0xff;
+                       proto_tree_add_text(sub_tree, tvb, offset, 4 + opt_len,
+                                           "T bit is %s",
+                                           opt_value & 0x8000 ? "set" : "not set");
+                       proto_tree_add_text(sub_tree, tvb, offset, 4 + opt_len,
+                                           "LAN Delay:  %u", lan_delay);
+                       proto_tree_add_text(sub_tree, tvb, offset, 4 + opt_len,
+                                           "Override Interval:  %u", override_interval);
+                       break;
+               }
+               case 19: /* priority */
+                       priority = opt_value;
+                       proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len,
+                                       "DR Priority: %u", priority);
+                       break;
+               case 20: /* generation ID */
+                       proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len,
+                                           "Generation ID: %d", opt_value);
+                       break;
+               default:
+                       proto_tree_add_text(pimopt_tree, tvb, offset, 4 + opt_len,
+                                           "Unknown option: %d  len: %d  value: 0x%x",
+                                           hello_opt, opt_len, opt_value);
+                       break;
+               }
+               offset += 4 + opt_len;
            }
            break;
          }
 
        case 1: /* register */
          {
-           const guint8 *ip;
+           guint32 flags;
+           guint8 v_hl;
+           tvbuff_t *next_tvb;
            proto_tree *flag_tree = NULL;
-               proto_item *tiflag; 
-           int flagoff;
-
-           flagoff = offset + sizeof(struct pim);
-           tiflag = proto_tree_add_text(pimopt_tree, flagoff, 4,
-               "Flags: 0x%08x", ntohl(*(guint32 *)&pd[flagoff]));
-           flag_tree = proto_item_add_subtree(tiflag, ETT_PIM);
-           proto_tree_add_text(flag_tree, flagoff, 1, "%s",
-               decode_boolean_bitfield(pd[flagoff], 0x80000000, 32,
+           proto_item *tiflag; 
+
+           flags = tvb_get_ntohl(tvb, offset);
+           tiflag = proto_tree_add_text(pimopt_tree, tvb, offset, 4,
+               "Flags: 0x%08x", flags);
+           flag_tree = proto_item_add_subtree(tiflag, ett_pim);
+           proto_tree_add_text(flag_tree, tvb, offset, 1, "%s",
+               decode_boolean_bitfield(flags, 0x80000000, 32,
                    "Border", "Not border"));
-           proto_tree_add_text(flag_tree, flagoff, 1, "%s",
-               decode_boolean_bitfield(pd[flagoff], 0x40000000, 32,
+           proto_tree_add_text(flag_tree, tvb, offset, 1, "%s",
+               decode_boolean_bitfield(flags, 0x40000000, 32,
                    "Null-Register", "Not Null-Register"));
-
-           ip = &pd[flagoff + sizeof(guint32)];
-           switch((*ip & 0xf0) >> 4) {
+           offset += 4;
+           
+           /*
+            * The rest of the packet is a multicast data packet.
+            */
+           next_tvb = tvb_new_subset(tvb, offset, -1, -1);
+
+           /*
+            * It's an IP packet - determine whether it's IPv4 or IPv6.
+            */
+           v_hl = tvb_get_guint8(tvb, offset);
+           switch((v_hl & 0xf0) >> 4) {
+           case 0:     /* Null-Register dummy header.
+                        * Has the same address family as the encapsulating PIM packet,
+                        * e.g. an IPv6 data packet is encapsulated in IPv6 PIM packet.
+                        */
+                   if (pinfo->src.type == AT_IPv4) {
+                           proto_tree_add_text(pimopt_tree, tvb, offset, -1,
+                                               "IPv4 dummy header");
+                           proto_tree_add_text(pimopt_tree, tvb, offset + 12, 4,
+                                               "Source: %s",
+                                               ip_to_str(tvb_get_ptr(tvb, offset + 12, 4)));
+                           proto_tree_add_text(pimopt_tree, tvb, offset + 16, 4,
+                                               "Group: %s",
+                                               ip_to_str(tvb_get_ptr(tvb, offset + 16, 4)));
+                   } else if (pinfo->src.type == AT_IPv6) {
+                           struct ip6_hdr ip6_hdr;
+                           tvb_memcpy(tvb, (guint8 *)&ip6_hdr, offset,
+                                      sizeof ip6_hdr);
+                           proto_tree_add_text(pimopt_tree, tvb, offset, -1,
+                                               "IPv6 dummy header");
+                           proto_tree_add_text(pimopt_tree, tvb,
+                                               offset + offsetof(struct ip6_hdr, ip6_src), 16,
+                                               "Source: %s",
+                                               ip6_to_str(&ip6_hdr.ip6_src));
+                           proto_tree_add_text(pimopt_tree, tvb,
+                                               offset + offsetof(struct ip6_hdr, ip6_dst), 16,
+                                               "Group: %s",
+                                               ip6_to_str(&ip6_hdr.ip6_dst));
+                   } else
+                           proto_tree_add_text(pimopt_tree, tvb, offset, -1,
+                                               "Dummy header for an unknown protocol");
+                   break;
            case 4:     /* IPv4 */
 #if 0
-                   dissect_ip(pd, ip - pd, fd, tree);
+                   call_dissector(ip_handle, next_tvb, pinfo, tree);
 #else
-                   dissect_ip(pd, ip - pd, fd, pimopt_tree);
+                   call_dissector(ip_handle, next_tvb, pinfo, pimopt_tree);
 #endif
                    break;
            case 6:     /* IPv6 */
 #if 0
-                   dissect_ipv6(pd, ip - pd, fd, tree);
+                   call_dissector(ipv6_handle, next_tvb, pinfo, tree);
 #else
-                   dissect_ipv6(pd, ip - pd, fd, pimopt_tree);
+                   call_dissector(ipv6_handle, next_tvb, pinfo, pimopt_tree);
 #endif
                    break;
            default:
-                   proto_tree_add_text(pimopt_tree,
-                       ip - pd, END_OF_FRAME,
-                       "Unknown IP version %d", (*ip & 0xf0) >> 4);
+                   proto_tree_add_text(pimopt_tree, tvb, offset, -1,
+                       "Unknown IP version %d", (v_hl & 0xf0) >> 4);
                    break;
            }
            break;
@@ -304,20 +898,17 @@ dissect_pim(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
        case 2: /* register-stop */
          {
            int advance;
-           const guint8 *ep;
            const char *s;
 
-           ep = &pd[offset + END_OF_FRAME];
-           offset += sizeof(struct pim);
-           s = dissect_pim_addr(&pd[offset], ep, pimv2_group, &advance);
+           s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
            if (s == NULL)
                break;
-           proto_tree_add_text(pimopt_tree, offset, advance, "Group: %s", s);
+           proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Group: %s", s);
            offset += advance;
-           s = dissect_pim_addr(&pd[offset], ep, pimv2_unicast, &advance);
+           s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
            if (s == NULL)
                break;
-           proto_tree_add_text(pimopt_tree, offset, advance, "Source: %s", s);
+           proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Source: %s", s);
            break;
          }
 
@@ -326,84 +917,76 @@ dissect_pim(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
        case 7: /* graft-ack */
          {
            int advance;
-           const guint8 *ep;
            int off;
            const char *s;
            int ngroup, i, njoin, nprune, j;
+           guint16 holdtime;
            proto_tree *grouptree = NULL;
-               proto_item *tigroup; 
+           proto_item *tigroup; 
            proto_tree *subtree = NULL;
-               proto_item *tisub; 
+           proto_item *tisub; 
 
-           ep = &pd[offset + END_OF_FRAME];
-           offset += sizeof(struct pim);
-           if (PIM_TYPE(pim.pim_typever) != 7) {
+           if (PIM_TYPE(pim_typever) != 7) {
                /* not graft-ack */
-               s = dissect_pim_addr(&pd[offset], ep, pimv2_unicast, &advance);
+               s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
                if (s == NULL)
                    break;
-               proto_tree_add_text(pimopt_tree, offset, advance,
+               proto_tree_add_text(pimopt_tree, tvb, offset, advance,
                    "Upstream-neighbor: %s", s);
                offset += advance;
            }
 
-           if (&pd[offset + 2] > ep)
-               break;
-           ngroup = pd[offset + 1];
-           proto_tree_add_text(pimopt_tree, offset + 1, 1,
-               "Groups: %u", pd[offset + 1]);
-           offset += 2;
+           offset += 1;        /* skip reserved field */
 
-           if (&pd[offset + 2] > ep)
-               break;
-           if (PIM_TYPE(pim.pim_typever) != 7) {
+           ngroup = tvb_get_guint8(tvb, offset);
+           proto_tree_add_text(pimopt_tree, tvb, offset, 1,
+               "Groups: %u", ngroup);
+           offset += 1;
+
+           if (PIM_TYPE(pim_typever) != 7)     {
                /* not graft-ack */
-               proto_tree_add_text(pimopt_tree, offset, 2,
-                   "Holdtime: %u%s", ntohs(*(guint16 *)&pd[offset]),
-                   ntohs(*(guint16 *)&pd[offset]) == 0xffff ? " (infty)" : "");
+               holdtime = tvb_get_ntohs(tvb, offset);
+               proto_tree_add_text(pimopt_tree, tvb, offset, 2,
+                   "Holdtime: %u%s", holdtime,
+                   holdtime == 0xffff ? " (infty)" : "");
            }
            offset += 2;
 
            for (i = 0; i < ngroup; i++) {
-               if (&pd[offset] >= ep)
-                   goto breakbreak3;
-
-               s = dissect_pim_addr(&pd[offset], ep, pimv2_group, &advance);
+               s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
                if (s == NULL)
                    goto breakbreak3;
-               tigroup = proto_tree_add_text(pimopt_tree, offset, advance,
+               tigroup = proto_tree_add_text(pimopt_tree, tvb, offset, advance,
                    "Group %d: %s", i, s);
-               grouptree = proto_item_add_subtree(tigroup, ETT_PIM);
+               grouptree = proto_item_add_subtree(tigroup, ett_pim);
                offset += advance;
 
-               if (&pd[offset + 4] > ep)
-                   goto breakbreak3;
-               njoin = ntohs(*(guint16 *)&pd[offset]);
-               nprune = ntohs(*(guint16 *)&pd[offset + 2]);
+               njoin = tvb_get_ntohs(tvb, offset);
+               nprune = tvb_get_ntohs(tvb, offset + 2);
 
-               tisub = proto_tree_add_text(grouptree, offset, 2,
+               tisub = proto_tree_add_text(grouptree, tvb, offset, 2,
                    "Join: %d", njoin);
-               subtree = proto_item_add_subtree(tisub, ETT_PIM);
+               subtree = proto_item_add_subtree(tisub, ett_pim);
                off = offset + 4;
-               for (j = 0; j < nprune; j++) {
-                   s = dissect_pim_addr(&pd[off], ep, pimv2_source,
+               for (j = 0; j < njoin; j++) {
+                   s = dissect_pim_addr(tvb, off, pimv2_source,
                        &advance);
                    if (s == NULL)
                        goto breakbreak3;
-                   proto_tree_add_text(subtree, off, advance,
+                   proto_tree_add_text(subtree, tvb, off, advance,
                        "IP address: %s", s);
                    off += advance;
                }
 
-               tisub = proto_tree_add_text(grouptree, offset + 2, 2,
+               tisub = proto_tree_add_text(grouptree, tvb, offset + 2, 2,
                    "Prune: %d", nprune);
-               subtree = proto_item_add_subtree(tisub, ETT_PIM);
+               subtree = proto_item_add_subtree(tisub, ett_pim);
                for (j = 0; j < nprune; j++) {
-                   s = dissect_pim_addr(&pd[off], ep, pimv2_source,
+                   s = dissect_pim_addr(tvb, off, pimv2_source,
                        &advance);
                    if (s == NULL)
                        goto breakbreak3;
-                   proto_tree_add_text(subtree, off, advance,
+                   proto_tree_add_text(subtree, tvb, off, advance,
                        "IP address: %s", s);
                    off += advance;
                }
@@ -418,69 +1001,60 @@ dissect_pim(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
            int advance;
            int i, j;
            int frpcnt;
+           guint16 holdtime;
            proto_tree *grouptree = NULL;
-               proto_item *tigroup; 
-
-           offset += sizeof(struct pim);
+           proto_item *tigroup; 
 
-           if (END_OF_FRAME < 2)
-               break;
-           proto_tree_add_text(pimopt_tree, offset, 2,
-               "Fragment tag: 0x%04x", ntohs(*(guint16 *)&pd[offset]));
+           proto_tree_add_text(pimopt_tree, tvb, offset, 2,
+               "Fragment tag: 0x%04x", tvb_get_ntohs(tvb, offset));
            offset += 2;
 
-           if (END_OF_FRAME < 2)
-               break;
-           proto_tree_add_text(pimopt_tree, offset, 1,
-               "Hash mask len: %u", pd[offset]);
-           proto_tree_add_text(pimopt_tree, offset + 1, 1,
-               "BSR priority: %u", pd[offset + 1]);
-           offset += 2;
+           proto_tree_add_text(pimopt_tree, tvb, offset, 1,
+               "Hash mask len: %u", tvb_get_guint8(tvb, offset));
+           offset += 1;
+           proto_tree_add_text(pimopt_tree, tvb, offset, 1,
+               "BSR priority: %u", tvb_get_guint8(tvb, offset));
+           offset += 1;
 
-           s = dissect_pim_addr(&pd[offset], &pd[offset + END_OF_FRAME],
-               pimv2_unicast, &advance);
+           s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
            if (s == NULL)
                break;
-           proto_tree_add_text(pimopt_tree, offset, advance, "BSR: %s", s);
+           proto_tree_add_text(pimopt_tree, tvb, offset, advance, "BSR: %s", s);
            offset += advance;
 
-           for (i = 0; END_OF_FRAME > 0; i++) {
-               s = dissect_pim_addr(&pd[offset], &pd[offset + END_OF_FRAME],
-                   pimv2_group, &advance);
+           for (i = 0; tvb_reported_length_remaining(tvb, offset) > 0; i++) {
+               s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
                if (s == NULL)
                    goto breakbreak4;
-               tigroup = proto_tree_add_text(pimopt_tree, offset, advance,
+               tigroup = proto_tree_add_text(pimopt_tree, tvb, offset, advance,
                    "Group %d: %s", i, s);
-               grouptree = proto_item_add_subtree(tigroup, ETT_PIM);
+               grouptree = proto_item_add_subtree(tigroup, ett_pim);
                offset += advance;
 
-               if (END_OF_FRAME < 2)
-                   goto breakbreak4;
-               proto_tree_add_text(grouptree, offset, 1,
-                   "RP count: %u", pd[offset]);
-               proto_tree_add_text(grouptree, offset + 1, 1,
-                   "FRP count: %u", pd[offset + 1]);
-               frpcnt = pd[offset + 1];
-               offset += 4;
+               proto_tree_add_text(grouptree, tvb, offset, 1,
+                   "RP count: %u", tvb_get_guint8(tvb, offset));
+               offset += 1;
+               frpcnt = tvb_get_guint8(tvb, offset);
+               proto_tree_add_text(grouptree, tvb, offset, 1,
+                   "FRP count: %u", frpcnt);
+               offset += 3;
 
-               for (j = 0; j < frpcnt && END_OF_FRAME > 0; j++) {
-                   s = dissect_pim_addr(&pd[offset],
-                       &pd[offset + END_OF_FRAME], pimv2_unicast, &advance);
+               for (j = 0; j < frpcnt; j++) {
+                   s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
                    if (s == NULL)
                        goto breakbreak4;
-                   proto_tree_add_text(grouptree, offset, advance,
+                   proto_tree_add_text(grouptree, tvb, offset, advance,
                        "RP %d: %s", j, s);
                    offset += advance;
 
-                   if (END_OF_FRAME < 4)
-                       goto breakbreak4;
-                   proto_tree_add_text(grouptree, offset, 2,
-                       "Holdtime: %u%s", ntohs(*(guint16 *)&pd[offset]),
-                       ntohs(*(guint16 *)&pd[offset]) == 0xffff ? " (infty)" : "");
-                   proto_tree_add_text(grouptree, offset + 3, 1,
-                       "Priority: %u", pd[offset + 3]);
-
-                   offset += 4;
+                   holdtime = tvb_get_ntohs(tvb, offset);
+                   proto_tree_add_text(grouptree, tvb, offset, 2,
+                       "Holdtime: %u%s", holdtime,
+                       holdtime == 0xffff ? " (infty)" : "");
+                   offset += 2;
+                   proto_tree_add_text(grouptree, tvb, offset, 1,
+                       "Priority: %u", tvb_get_guint8(tvb, offset));
+                   offset += 2;        /* also skips reserved field */
                }
            }
 
@@ -493,35 +1067,27 @@ dissect_pim(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
            const char *s;
            int advance;
 
-           offset += sizeof(struct pim);
-
-           s = dissect_pim_addr(&pd[offset], &pd[offset + END_OF_FRAME],
-               pimv2_group, &advance);
+           s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
            if (s == NULL)
                break;
-           proto_tree_add_text(pimopt_tree, offset, advance, "Group: %s", s);
+           proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Group: %s", s);
            offset += advance;
 
-           s = dissect_pim_addr(&pd[offset], &pd[offset + END_OF_FRAME],
-               pimv2_unicast, &advance);
+           s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
            if (s == NULL)
                break;
-           proto_tree_add_text(pimopt_tree, offset, advance, "Source: %s", s);
+           proto_tree_add_text(pimopt_tree, tvb, offset, advance, "Source: %s", s);
            offset += advance;
 
-           if (END_OF_FRAME < 4)
-               break;
-           proto_tree_add_text(pimopt_tree, offset, 1, "%s",
-               decode_boolean_bitfield(pd[offset], 0x80, 8,
+           proto_tree_add_text(pimopt_tree, tvb, offset, 1, "%s",
+               decode_boolean_bitfield(tvb_get_guint8(tvb, offset), 0x80, 8,
                    "RP Tree", "Not RP Tree"));
-           proto_tree_add_text(pimopt_tree, offset, 4, "Preference: %u",
-               ntohl(*(guint32 *)&pd[offset] & 0x7fffffff));
+           proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Preference: %u",
+               tvb_get_ntohl(tvb, offset) & 0x7fffffff);
            offset += 4;
 
-           if (END_OF_FRAME < 4)
-               break;
-           proto_tree_add_text(pimopt_tree, offset, 4, "Metric: %u",
-               ntohl(*(guint32 *)&pd[offset]));
+           proto_tree_add_text(pimopt_tree, tvb, offset, 4, "Metric: %u",
+               tvb_get_ntohl(tvb, offset));
 
            break;
          }
@@ -531,36 +1097,33 @@ dissect_pim(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
            const char *s;
            int advance;
            int pfxcnt;
+           guint16 holdtime;
            int i;
 
-           offset += sizeof(struct pim);
-           if (END_OF_FRAME < 4)
-               break;
-           pfxcnt = pd[offset];
-           proto_tree_add_text(pimopt_tree, offset, 1,
-               "Prefix-count: %u", pd[offset]);
-           proto_tree_add_text(pimopt_tree, offset + 1, 1,
-               "Priority: %u", pd[offset + 1]);
-           proto_tree_add_text(pimopt_tree, offset + 2, 2,
-               "Holdtime: %u%s", ntohs(*(guint16 *)&pd[offset + 2]),
-               ntohs(*(guint16 *)&pd[offset + 2]) == 0xffff ? " (infty)" : "");
-           offset += 4;
+           pfxcnt = tvb_get_guint8(tvb, offset);
+           proto_tree_add_text(pimopt_tree, tvb, offset, 1,
+               "Prefix-count: %u", pfxcnt);
+           offset += 1;
+           proto_tree_add_text(pimopt_tree, tvb, offset, 1,
+               "Priority: %u", tvb_get_guint8(tvb, offset));
+           offset += 1;
+           holdtime = tvb_get_ntohs(tvb, offset);
+           proto_tree_add_text(pimopt_tree, tvb, offset, 2,
+               "Holdtime: %u%s", holdtime,
+               holdtime == 0xffff ? " (infty)" : "");
+           offset += 2;
 
-           if (END_OF_FRAME <= 0)
-               break;
-           s = dissect_pim_addr(&pd[offset], &pd[offset + END_OF_FRAME],
-               pimv2_unicast, &advance);
+           s = dissect_pim_addr(tvb, offset, pimv2_unicast, &advance);
            if (s == NULL)
                break;
-           proto_tree_add_text(pimopt_tree, offset, advance, "RP: %s", s);
+           proto_tree_add_text(pimopt_tree, tvb, offset, advance, "RP: %s", s);
            offset += advance;
 
-           for (i = 0; i < pfxcnt && END_OF_FRAME > 0; i++) {
-               s = dissect_pim_addr(&pd[offset], &pd[offset + END_OF_FRAME],
-                   pimv2_group, &advance);
+           for (i = 0; i < pfxcnt; i++) {
+               s = dissect_pim_addr(tvb, offset, pimv2_group, &advance);
                if (s == NULL)
                    goto breakbreak8;
-               proto_tree_add_text(pimopt_tree, offset, advance,
+               proto_tree_add_text(pimopt_tree, tvb, offset, advance,
                    "Group %d: %s", i, s);
                offset += advance;
            }
@@ -581,16 +1144,38 @@ proto_register_pim(void)
     static hf_register_info hf[] = {
       { &hf_pim_version,
        { "Version",            "pim.version",
-                               FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
+                               FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
       { &hf_pim_type,
-       { "Type",                       "pim.type",
-                               FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
+       { "Type",               "pim.type",
+                               FT_UINT8, BASE_DEC, VALS(type2vals), 0x0, "", HFILL }},
+      { &hf_pim_code,
+       { "Code",               "pim.code",
+                               FT_UINT8, BASE_DEC, VALS(type1vals), 0x0, "", HFILL }},
       { &hf_pim_cksum,
        { "Checksum",           "pim.cksum",
-                               FT_UINT16, BASE_HEX, NULL, 0x0, "" }},
+                               FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL }},
+    };
+    static gint *ett[] = {
+        &ett_pim,
     };
 
     proto_pim = proto_register_protocol("Protocol Independent Multicast",
-       "pim");
+       "PIM", "pim");
     proto_register_field_array(proto_pim, hf, array_length(hf));
+    proto_register_subtree_array(ett, array_length(ett));
+}
+
+void
+proto_reg_handoff_pim(void)
+{
+    dissector_handle_t pim_handle;
+
+    pim_handle = create_dissector_handle(dissect_pim, proto_pim);
+    dissector_add("ip.proto", IP_PROTO_PIM, pim_handle);
+
+    /*
+     * Get handles for the IPv4 and IPv6 dissectors.
+     */
+    ip_handle = find_dissector("ip");
+    ipv6_handle = find_dissector("ipv6");
 }