It's the AppleTalk Session Protocol, not the AppleTalk Stream Protocol.
[obnox/wireshark/wip.git] / packet-pim.c
index fe920f3f24f23e60b480acb63259b58426540320..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.24 2001/02/08 08:38:58 guy 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
@@ -36,6 +35,7 @@
 #include <netinet/in.h>
 #endif
 
+#include <stddef.h>  /* For offsetof */
 #include <string.h>
 #include <glib.h>
 
@@ -43,8 +43,9 @@
 # include "snprintf.h"
 #endif
 
-#include "packet.h"
-#include "packet-ip.h"
+#include <epan/packet.h>
+#include "ipproto.h"
+#include "afn.h"
 #include "packet-ipv6.h"
 #include "in_cksum.h"
 
@@ -58,31 +59,433 @@ 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;
 
 /*
- * Address family values.
+ * For PIM v1, see the PDF slides at
+ *
+ *     http://www.mbone.de/training/Module3.pdf
+ *
+ * Is it documented anywhere else?
  */
-#define PIM_AF_RESERVED                0
-#define PIM_AF_IP              1       /* IPv4 */
-#define PIM_AF_IPV6            2       /* IPv6 */
-#define PIM_AF_NSAP            3       /* NSAP */
-#define PIM_AF_HDLC            4       /* HDLC (8-bit multidrop) */
-#define PIM_AF_BBN_1822                5       /* BBN 1822 */
-#define PIM_AF_802             6       /* 802 (D/I/X Ethernet, 802.x, FDDI) */
-#define PIM_AF_E_163           7       /* E.163 */
-#define PIM_AF_E_164           8       /* E.164 (SMDS, Frame Relay, ATM) */
-#define PIM_AF_F_69            9       /* F.69 (Telex) */
-#define PIM_AF_X_121           10      /* X.121 (X.25, Frame Relay) */
-#define PIM_AF_IPX             11      /* IPX */
-#define PIM_AF_ATALK           12      /* Appletalk */
-#define PIM_AF_DECNET_IV       13      /* DECnet Phase IV */
-#define PIM_AF_VINES           14      /* Banyan Vines */
-#define PIM_AF_E_164_NSAP      15      /* E.164 with NSAP format subaddress */
+static const char *
+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,
@@ -95,7 +498,7 @@ dissect_pim_addr(tvbuff_t *tvb, int offset, enum pimv2_addrtype at,
     int len = 0;
 
     af = tvb_get_guint8(tvb, offset);
-    if (af != PIM_AF_IP && af != PIM_AF_IPV6) {
+    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.
@@ -117,13 +520,13 @@ dissect_pim_addr(tvbuff_t *tvb, int offset, enum pimv2_addrtype at,
     switch (at) {
     case pimv2_unicast:
        switch (af) {
-       case PIM_AF_IP:
+       case AFNUM_INET:
            len = 4;
            (void)snprintf(buf, sizeof(buf), "%s",
                ip_to_str(tvb_get_ptr(tvb, offset + 2, len)));
            break;
 
-       case PIM_AF_IPV6:
+       case AFNUM_INET6:
            len = 16;
            (void)snprintf(buf, sizeof(buf), "%s",
                ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset + 2, len)));
@@ -136,13 +539,13 @@ dissect_pim_addr(tvbuff_t *tvb, int offset, enum pimv2_addrtype at,
     case pimv2_group:
        mask_len = tvb_get_guint8(tvb, offset + 3);
        switch (af) {
-       case PIM_AF_IP:
+       case AFNUM_INET:
            len = 4;
            (void)snprintf(buf, sizeof(buf), "%s/%u",
                ip_to_str(tvb_get_ptr(tvb, offset + 4, len)), mask_len);
            break;
 
-       case PIM_AF_IPV6:
+       case AFNUM_INET6:
            len = 16;
            (void)snprintf(buf, sizeof(buf), "%s/%u",
                ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset + 4, len)), mask_len);
@@ -156,13 +559,13 @@ dissect_pim_addr(tvbuff_t *tvb, int offset, enum pimv2_addrtype at,
        flags = tvb_get_guint8(tvb, offset + 2);
        mask_len = tvb_get_guint8(tvb, offset + 3);
        switch (af) {
-       case PIM_AF_IP:
+       case AFNUM_INET:
            len = 4;
            (void)snprintf(buf, sizeof(buf), "%s/%u",
                ip_to_str(tvb_get_ptr(tvb, offset + 4, len)), mask_len);
            break;
 
-       case PIM_AF_IPV6:
+       case AFNUM_INET6:
            len = 16;
            (void)snprintf(buf, sizeof(buf), "%s/%u",
                ip6_to_str((struct e_in6_addr *)tvb_get_ptr(tvb, offset + 4, len)), mask_len);
@@ -185,92 +588,88 @@ dissect_pim_addr(tvbuff_t *tvb, int offset, enum pimv2_addrtype at,
     return buf;
 }
 
+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[1];
-    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 },
-    };
+    vec_t cksum_vec[4];
+    guint32 phdr[2];
     char *typestr;
     proto_tree *pim_tree = NULL;
     proto_item *ti; 
     proto_tree *pimopt_tree = NULL;
     proto_item *tiopt; 
 
-    if (check_col(pinfo->fd, COL_PROTOCOL))
-        col_set_str(pinfo->fd, COL_PROTOCOL, "PIM");
-    if (check_col(pinfo->fd, COL_INFO))
-       col_clear(pinfo->fd, COL_INFO);
+    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);
 
     pim_typever = tvb_get_guint8(tvb, 0);
 
     switch (PIM_VER(pim_typever)) {
-    case 1:
-       typestr = val_to_str(PIM_TYPE(pim_typever), type1vals, "Unknown");
-       break;
     case 2:
-       typestr = val_to_str(PIM_TYPE(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(pinfo->fd, COL_PROTOCOL)) {
-        col_add_fstr(pinfo->fd, COL_PROTOCOL, "PIM version %d",
+    if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
+        col_add_fstr(pinfo->cinfo, COL_PROTOCOL, "PIMv%d",
            PIM_VER(pim_typever));
     }
-    if (check_col(pinfo->fd, COL_INFO))
-       col_add_fstr(pinfo->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, tvb, offset,
-           tvb_length_remaining(tvb, offset), FALSE);
+       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_format(pim_tree, hf_pim_type, tvb, offset, 1,
-           PIM_TYPE(pim_typever),
-           "Type: %s (%u)", typestr, PIM_TYPE(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
-            * mesage, and thus can figure out how much to checksum.
+            * 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.
@@ -289,9 +688,34 @@ dissect_pim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
             * 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);
+
+           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,
@@ -311,8 +735,8 @@ dissect_pim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
        offset += 4;
 
        if (tvb_reported_length_remaining(tvb, offset) > 0) {
-           tiopt = proto_tree_add_text(pim_tree, tvb, offset,
-               tvb_length_remaining(tvb, offset), "PIM parameters");
+           tiopt = proto_tree_add_text(pim_tree, tvb, offset, -1,
+               "PIM parameters");
            pimopt_tree = proto_item_add_subtree(tiopt, ett_pim);
        } else
            goto done;
@@ -325,17 +749,65 @@ dissect_pim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
        case 0: /*hello*/
          {
            while (tvb_reported_length_remaining(tvb, offset) >= 2) {
-               if (tvb_get_ntohs(tvb, offset) == 1 &&
-                   tvb_get_ntohs(tvb, offset + 2) == 2) {
-                   guint16 holdtime;
-
-                   holdtime = tvb_get_ntohs(tvb, offset + 4);
-                   proto_tree_add_text(pimopt_tree, tvb, offset, 6,
-                       "Holdtime: %u%s", holdtime,
-                       holdtime == 0xffff ? " (infty)" : "");
-                   offset += 6;
-               } else
-                   break;
+               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;
          }
@@ -345,8 +817,6 @@ dissect_pim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
            guint32 flags;
            guint8 v_hl;
            tvbuff_t *next_tvb;
-           const guint8 *next_pd;
-           int next_offset;
            proto_tree *flag_tree = NULL;
            proto_item *tiflag; 
 
@@ -372,6 +842,37 @@ dissect_pim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
             */
            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);
@@ -380,16 +881,14 @@ dissect_pim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
 #endif
                    break;
            case 6:     /* IPv6 */
-                   tvb_compat(next_tvb, &next_pd, &next_offset);
 #if 0
-                   dissect_ipv6(next_pd, next_offset, pinfo->fd, tree);
+                   call_dissector(ipv6_handle, next_tvb, pinfo, tree);
 #else
-                   dissect_ipv6(next_pd, next_offset, pinfo->fd, pimopt_tree);
+                   call_dissector(ipv6_handle, next_tvb, pinfo, pimopt_tree);
 #endif
                    break;
            default:
-                   proto_tree_add_text(pimopt_tree, tvb,
-                       offset, tvb_length_remaining(tvb, offset),
+                   proto_tree_add_text(pimopt_tree, tvb, offset, -1,
                        "Unknown IP version %d", (v_hl & 0xf0) >> 4);
                    break;
            }
@@ -469,7 +968,7 @@ dissect_pim(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
                    "Join: %d", njoin);
                subtree = proto_item_add_subtree(tisub, ett_pim);
                off = offset + 4;
-               for (j = 0; j < nprune; j++) {
+               for (j = 0; j < njoin; j++) {
                    s = dissect_pim_addr(tvb, off, pimv2_source,
                        &advance);
                    if (s == NULL)
@@ -645,13 +1144,16 @@ 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,
@@ -666,10 +1168,14 @@ proto_register_pim(void)
 void
 proto_reg_handoff_pim(void)
 {
-    dissector_add("ip.proto", IP_PROTO_PIM, dissect_pim, proto_pim);
+    dissector_handle_t pim_handle;
+
+    pim_handle = create_dissector_handle(dissect_pim, proto_pim);
+    dissector_add("ip.proto", IP_PROTO_PIM, pim_handle);
 
     /*
-     * Get a handle for the IP dissector.
+     * Get handles for the IPv4 and IPv6 dissectors.
      */
     ip_handle = find_dissector("ip");
+    ipv6_handle = find_dissector("ipv6");
 }