From Ronnie Sahlberg:
[obnox/wireshark/wip.git] / packet-icmpv6.c
index 6563cd419e0720e2df14b628e3f02ac77c12687f..f694ffa5a6eedc6811b68ada9c075157ce18501d 100644 (file)
@@ -1,7 +1,7 @@
 /* packet-icmpv6.c
  * Routines for ICMPv6 packet disassembly
  *
- * $Id: packet-icmpv6.c,v 1.43 2001/05/27 04:14:53 guy Exp $
+ * $Id: packet-icmpv6.c,v 1.65 2002/01/30 22:58:54 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -9,6 +9,8 @@
  *
  * MobileIPv6 support added by Tomislav Borosa <tomislav.borosa@siemens.hr>
  *
+ * HMIPv6 support added by Martti Kuparinen <martti.kuparinen@iki.fi>
+ *
  * 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
 # include "snprintf.h"
 #endif
 
-#include "packet.h"
+#include <epan/packet.h>
 #include "packet-ipv6.h"
 #include "packet-dns.h"
 #include "in_cksum.h"
-#include "resolv.h"
+#include <epan/resolv.h>
 #include "ipproto.h"
 
 #ifndef offsetof
 #define        offsetof(type, member)  ((size_t)(&((type *)0)->member))
 #endif
 
+/*
+ * See, under http://www.ietf.org/internet-drafts/
+ *
+ *     draft-ietf-mobileip-ipv6-15.txt
+ *
+ * and
+ *
+ *     draft-ietf-ipngwg-icmp-name-lookups-08.txt
+ *
+ * and
+ *
+ *     draft-ietf-mobileip-hmipv6-05.txt
+ */
+
 static int proto_icmpv6 = -1;
 static int hf_icmpv6_type = -1;
 static int hf_icmpv6_code = -1;
@@ -80,6 +96,7 @@ static gint ett_nodeinfo_nodebitmap = -1;
 static gint ett_nodeinfo_nodedns = -1;
 
 static dissector_handle_t ipv6_handle;
+static dissector_handle_t data_handle;
 
 static const value_string names_nodeinfo_qtype[] = {
     { NI_QTYPE_NOOP,           "NOOP" },
@@ -97,16 +114,24 @@ static const value_string names_rrenum_matchcode[] = {
     { 0,                       NULL }
 };
 
+static const value_string names_router_pref[] = {
+        { ND_RA_FLAG_RTPREF_HIGH,      "High" }, 
+        { ND_RA_FLAG_RTPREF_MEDIUM,    "Medium" }, 
+        { ND_RA_FLAG_RTPREF_LOW,       "Low" }, 
+        { ND_RA_FLAG_RTPREF_RSV,       "Reserved" }, 
+};
+
 static void
 dissect_contained_icmpv6(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
 {
     tvbuff_t *next_tvb;
-    address save_dl_src;
-    address save_dl_dst;
-    address save_net_src;
-    address save_net_dst;
-    address save_src;
-    address save_dst;
+    volatile address save_dl_src;
+    volatile address save_dl_dst;
+    volatile address save_net_src;
+    volatile address save_net_dst;
+    volatile address save_src;
+    volatile address save_dst;
+    gboolean save_in_error_pkt;
 
     next_tvb = tvb_new_subset(tvb, offset, -1, -1);
 
@@ -117,7 +142,7 @@ dissect_contained_icmpv6(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tr
           Set the columns non-writable, so that the packet list
           shows this as an ICMPv6 packet, not as the type of packet
           for which the ICMPv6 packet was generated. */
-       col_set_writable(pinfo->fd, FALSE);
+       col_set_writable(pinfo->cinfo, FALSE);
 
        /* Also, save the current values of the addresses, and restore
           them when we're finished dissecting the contained packet, so
@@ -130,8 +155,30 @@ dissect_contained_icmpv6(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tr
        save_src = pinfo->src;
        save_dst = pinfo->dst;
 
-       /* Dissect the contained packet. */
-       call_dissector(ipv6_handle, next_tvb, pinfo, tree);
+       /* Save the current value of the "we're inside an error packet"
+          flag, and set that flag; subdissectors may treat packets
+          that are the payload of error packets differently from
+          "real" packets. */
+       save_in_error_pkt = pinfo->in_error_pkt;
+       pinfo->in_error_pkt = TRUE;
+
+       /* Dissect the contained packet.
+          Catch ReportedBoundsError, and do nothing if we see it,
+          because it's not an error if the contained packet is short;
+          there's no guarantee that all of it was included.
+
+          XXX - should catch BoundsError, and re-throw it after cleaning
+          up. */
+       TRY {
+           call_dissector(ipv6_handle, next_tvb, pinfo, tree);
+       }
+       CATCH(ReportedBoundsError) {
+           ; /* do nothing */
+       }
+       ENDTRY;
+
+       /* Restore the "we're inside an error packet" flag. */
+       pinfo->in_error_pkt = save_in_error_pkt;
 
        /* Restore the addresses. */
        pinfo->dl_src = save_dl_src;
@@ -141,14 +188,14 @@ dissect_contained_icmpv6(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tr
        pinfo->src = save_src;
        pinfo->dst = save_dst;
     } else
-       dissect_data(next_tvb, 0, pinfo, tree);
+       call_dissector(data_handle,next_tvb, pinfo, tree);
 }
 
 static void
 dissect_icmpv6opt(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
 {
     proto_tree *icmp6opt_tree, *field_tree;
-       proto_item *ti, *tf;
+    proto_item *ti, *tf;
     struct nd_opt_hdr nd_opt_hdr, *opt;
     int len;
     char *typename;
@@ -158,7 +205,7 @@ dissect_icmpv6opt(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tre
 
 again:
     if ((int)tvb_reported_length(tvb) <= offset)
-            return; /* No more options left */
+       return; /* No more options left */
 
     opt = &nd_opt_hdr;
     tvb_memcpy(tvb, (guint8 *)opt, offset, sizeof *opt);
@@ -168,6 +215,14 @@ again:
     ti = proto_tree_add_text(tree, tvb, offset, len, "ICMPv6 options");
     icmp6opt_tree = proto_item_add_subtree(ti, ett_icmpv6opt);
 
+    if (len == 0) {
+       proto_tree_add_text(icmp6opt_tree, tvb,
+                           offset + offsetof(struct nd_opt_hdr, nd_opt_len), 1,
+                           "Invalid option length: %u",
+                           opt->nd_opt_len);
+       return; /* we must not try to decode this */
+    }
+
     switch (opt->nd_opt_type) {
     case ND_OPT_SOURCE_LINKADDR:
        typename = "Source link-layer address";
@@ -184,14 +239,16 @@ again:
     case ND_OPT_MTU:
        typename = "MTU";
        break;
-    case ND_OPT_ADVERTISEMENT_INTERVAL:
+    case ND_OPT_ADVINTERVAL:
        typename = "Advertisement Interval";
        break;
-    case ND_OPT_HOME_AGENT_INFORMATION:
+    case ND_OPT_HOMEAGENT_INFO:
        typename = "Home Agent Information";
        break;
+    case ND_OPT_MAP:
+       typename = "HMIPv6 MAP option";
+       break;
     default:
-
        typename = "Unknown";
        break;
     }
@@ -221,6 +278,7 @@ again:
        }
        proto_tree_add_text(icmp6opt_tree, tvb,
            offset + sizeof(*opt), len, "Link-layer address: %s", t);
+       free(t);
        break;
       }
     case ND_OPT_PREFIX_INFORMATION:
@@ -240,15 +298,18 @@ again:
        field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
        proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
            decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
-                   0x80, 8, "Onlink", "Not onlink"));
+                   ND_OPT_PI_FLAG_ONLINK, 8, "Onlink", "Not onlink"));
        proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
            decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
-                   0x40, 8, "Auto", "Not auto"));
-  /* BT INSERT BEGIN */
+                   ND_OPT_PI_FLAG_AUTO, 8, "Auto", "Not auto"));
        proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
            decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
-                   0x20, 8, "Router Address", "Not router address"));
-  /* BT INSERT END */
+                   ND_OPT_PI_FLAG_ROUTER, 8,
+                   "Router Address", "Not router address"));
+       proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
+           decode_boolean_bitfield(pi->nd_opt_pi_flags_reserved,
+                   ND_OPT_PI_FLAG_SITEPREF, 8,
+                   "Site prefix", "Not site prefix"));
        proto_tree_add_text(icmp6opt_tree, tvb,
            offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_valid_time),
            4, "Valid lifetime: 0x%08x",
@@ -272,35 +333,137 @@ again:
            offset + offsetof(struct nd_opt_mtu, nd_opt_mtu_mtu), 4,
            "MTU: %u", tvb_get_ntohl(tvb, offset + offsetof(struct nd_opt_mtu, nd_opt_mtu_mtu)));
        break;
-       /* BT INSERT BEGIN */
-    case ND_OPT_ADVERTISEMENT_INTERVAL:
+    case ND_OPT_ADVINTERVAL:
        proto_tree_add_text(icmp6opt_tree, tvb,
            offset + offsetof(struct nd_opt_adv_int, nd_opt_adv_int_advint), 4,
-           "Advertisement Interval: %d",
+           "Advertisement Interval: %u",
            tvb_get_ntohl(tvb, offset + offsetof(struct nd_opt_adv_int, nd_opt_adv_int_advint)));
        break;
-    case ND_OPT_HOME_AGENT_INFORMATION:
+    case ND_OPT_HOMEAGENT_INFO:
       {
-       struct nd_opt_ha_info *pi = (struct nd_opt_ha_info *)opt;
+       struct nd_opt_ha_info pibuf, *pi;
+
+       pi = &pibuf;
+       tvb_memcpy(tvb, (guint8 *)pi, offset, sizeof *pi);
        proto_tree_add_text(icmp6opt_tree, tvb,
            offset + offsetof(struct nd_opt_ha_info, nd_opt_ha_info_ha_pref),
            2, "Home Agent Preference: %d",
-           pntohs(&pi->nd_opt_ha_info_ha_pref));
+           (gint16)pntohs(&pi->nd_opt_ha_info_ha_pref));
        proto_tree_add_text(icmp6opt_tree, tvb,
            offset + offsetof(struct nd_opt_ha_info, nd_opt_ha_info_ha_life),
-           2, "Home Agent Lifetime: %d",
+           2, "Home Agent Lifetime: %u",
            pntohs(&pi->nd_opt_ha_info_ha_life));
        break;
       }
-       /* BT INSERT END */
-    }
+    case ND_OPT_MAP:
+      {
+       struct nd_opt_map_info mapbuf, *map;
+       int flagoff;
 
-    if (opt->nd_opt_len == 0) {
-        proto_tree_add_text(icmp6opt_tree, tvb,
-                            offset + offsetof(struct nd_opt_hdr, nd_opt_len), 1,
-                            "Invalid option length: %u",
-                            opt->nd_opt_len);
-        return;
+       map = &mapbuf;
+       tvb_memcpy(tvb, (guint8 *)map, offset, sizeof *map);
+       proto_tree_add_text(icmp6opt_tree, tvb,
+           offset + offsetof(struct nd_opt_map_info, nd_opt_map_dist_and_pref),
+           1, "Distance: %u", (map->nd_opt_map_dist_and_pref >> 4));
+       proto_tree_add_text(icmp6opt_tree, tvb,
+           offset + offsetof(struct nd_opt_map_info, nd_opt_map_dist_and_pref),
+           1, "Preference: %u", (map->nd_opt_map_dist_and_pref & 0x0F));
+       flagoff = offset + offsetof(struct nd_opt_map_info,
+           nd_opt_map_flags);
+       tf = proto_tree_add_text(icmp6opt_tree, tvb, flagoff, 1,
+           "Flags: 0x%02x",
+           tvb_get_guint8(tvb, offset + offsetof(struct nd_opt_map_info,
+           nd_opt_map_flags)));
+       field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
+       proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
+           decode_boolean_bitfield(map->nd_opt_map_flags,
+               ND_OPT_MAP_FLAG_R, 8, "R", "No R"));
+       proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
+           decode_boolean_bitfield(map->nd_opt_map_flags,
+               ND_OPT_MAP_FLAG_M, 8, "M", "No M"));
+       proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
+           decode_boolean_bitfield(map->nd_opt_map_flags,
+               ND_OPT_MAP_FLAG_I, 8, "I", "No I"));
+       proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
+           decode_boolean_bitfield(map->nd_opt_map_flags,
+               ND_OPT_MAP_FLAG_T, 8, "T", "No T"));
+       proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
+           decode_boolean_bitfield(map->nd_opt_map_flags,
+               ND_OPT_MAP_FLAG_P, 8, "P", "No P"));
+       proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
+           decode_boolean_bitfield(map->nd_opt_map_flags,
+               ND_OPT_MAP_FLAG_V, 8, "V", "No V"));
+       proto_tree_add_text(icmp6opt_tree, tvb,
+           offset + offsetof(struct nd_opt_map_info, nd_opt_map_lifetime),
+           4, "Lifetime: %u", pntohl(&map->nd_opt_map_lifetime));
+
+       proto_tree_add_text(icmp6opt_tree, tvb,
+           offset + offsetof(struct nd_opt_map_info, nd_opt_map_address), 16,
+#ifdef INET6
+           "Address of MAP: %s (%s)",
+           get_hostname6(&map->nd_opt_map_address),
+#else
+           "Address of MAP: %s",
+#endif
+           ip6_to_str(&map->nd_opt_map_address));
+       break;
+      }
+    case ND_OPT_ROUTE_INFO:
+      {
+       struct nd_opt_route_info ribuf, *ri;
+       struct e_in6_addr in6;
+       int l;
+       guint32 lifetime;
+
+       ri = &ribuf;
+       tvb_memcpy(tvb, (guint8 *)ri, offset, sizeof *ri);
+       memset(&in6, 0, sizeof(in6));
+       switch (ri->nd_opt_rti_len) {
+       case 1:
+           l = 0;
+           break;
+       case 2:
+           tvb_memcpy(tvb, (guint8 *)&in6, offset + sizeof(*ri), l = 8);
+           break;
+       case 3:
+           tvb_memcpy(tvb, (guint8 *)&in6, offset + sizeof(*ri), l = 16);
+           break;
+       default:
+           l = -1;
+           break;
+       }
+       if (l >= 0) {
+           proto_tree_add_text(icmp6opt_tree, tvb,
+               offset + offsetof(struct nd_opt_route_info, nd_opt_rti_prefixlen),
+               1, "Prefix length: %u", ri->nd_opt_rti_prefixlen);
+           tf = proto_tree_add_text(icmp6opt_tree, tvb,
+               offset + offsetof(struct nd_opt_route_info, nd_opt_rti_flags),
+               1, "Flags: 0x%02x", ri->nd_opt_rti_flags);
+           field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
+           proto_tree_add_text(field_tree, tvb,
+               offset + offsetof(struct nd_opt_route_info, nd_opt_rti_flags),
+               1, "%s",
+               decode_enumerated_bitfield(ri->nd_opt_rti_flags,
+                   ND_RA_FLAG_RTPREF_MASK, 8, names_router_pref,
+                   "Router preference: %s"));
+           lifetime = pntohl(&ri->nd_opt_rti_lifetime);
+           if (lifetime == 0xffffffff)
+               proto_tree_add_text(icmp6opt_tree, tvb,
+                   offset + offsetof(struct nd_opt_route_info, nd_opt_rti_lifetime),
+                   sizeof(ri->nd_opt_rti_lifetime), "Lifetime: infinity");
+           else
+               proto_tree_add_text(icmp6opt_tree, tvb,
+                   offset + offsetof(struct nd_opt_route_info, nd_opt_rti_lifetime),
+                   sizeof(ri->nd_opt_rti_lifetime), "Lifetime: %u", lifetime);
+           proto_tree_add_text(icmp6opt_tree, tvb,
+               offset + sizeof(*ri), l, "Prefix: %s", ip6_to_str(&in6));
+       } else {
+           proto_tree_add_text(icmp6opt_tree, tvb,
+               offset + offsetof(struct nd_opt_hdr, nd_opt_len), 1,
+               "Invalid option length: %u", opt->nd_opt_len);
+       }
+       break;
+      }
     }
 
     offset += (opt->nd_opt_len << 3);
@@ -362,7 +525,7 @@ bitrange0(v, s, buf, buflen)
                        l = snprintf(p, ep - p, ",%d-%d", s + off,
                            s + off + i - 1);
                }
-               if (l > ep - p) {
+               if (l == -1 || l > ep - p) {
                        buf[0] = '\0';
                        return NULL;
                }
@@ -405,8 +568,6 @@ dissect_nodeinfo(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree
     guint16 flags;
     char dname[MAXDNAME];
     guint8 ipaddr[4];
-    const u_char *pd;
-    int top_level_offset;
 
     ni = &icmp6_nodeinfo;
     tvb_memcpy(tvb, (guint8 *)ni, offset, sizeof *ni);
@@ -498,10 +659,10 @@ dissect_nodeinfo(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree
     if (ni->ni_type == ICMP6_NI_QUERY) {
        switch (ni->ni_code) {
        case ICMP6_NI_SUBJ_IPV6:
-           n = tvb_length_remaining(tvb, offset + sizeof(*ni));
+           n = tvb_reported_length_remaining(tvb, offset + sizeof(*ni));
            n /= sizeof(struct e_in6_addr);
            tf = proto_tree_add_text(tree, tvb,
-               offset + sizeof(*ni), tvb_length_remaining(tvb, offset), "IPv6 subject addresses");
+               offset + sizeof(*ni), -1, "IPv6 subject addresses");
            field_tree = proto_item_add_subtree(tf, ett_nodeinfo_subject6);
            p = offset + sizeof *ni;
            for (i = 0; i < n; i++) {
@@ -515,11 +676,8 @@ dissect_nodeinfo(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree
            off = tvb_length_remaining(tvb, offset);
            break;
        case ICMP6_NI_SUBJ_FQDN:
-           /* XXXX - clean this up when packet-dns.c has been tvbuffified */
-           tvb_compat(tvb, &pd, &top_level_offset);
-           l = get_dns_name(pd, top_level_offset + offset + sizeof(*ni),
-               top_level_offset + offset + sizeof(*ni),
-               dname, sizeof(dname));
+           l = get_dns_name(tvb, offset + sizeof(*ni),
+               offset + sizeof(*ni), dname, sizeof(dname));
            if (tvb_bytes_exist(tvb, offset + sizeof(*ni) + l, 1) &&
                tvb_get_guint8(tvb, offset + sizeof(*ni) + l) == 0) {
                l++;
@@ -532,10 +690,10 @@ dissect_nodeinfo(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree
            off = tvb_length_remaining(tvb, offset + sizeof(*ni) + l);
            break;
        case ICMP6_NI_SUBJ_IPV4:
-           n = tvb_length_remaining(tvb, offset + sizeof(*ni));
+           n = tvb_reported_length_remaining(tvb, offset + sizeof(*ni));
            n /= sizeof(guint32);
            tf = proto_tree_add_text(tree, tvb,
-               offset + sizeof(*ni), tvb_length_remaining(tvb, offset), "IPv4 subject addresses");
+               offset + sizeof(*ni), -1, "IPv4 subject addresses");
            field_tree = proto_item_add_subtree(tf, ett_nodeinfo_subject4);
            p = offset + sizeof *ni;
            for (i = 0; i < n; i++) {
@@ -554,7 +712,7 @@ dissect_nodeinfo(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree
        case NI_QTYPE_SUPTYPES:
            p = offset + sizeof *ni;
            tf = proto_tree_add_text(tree, tvb,
-               offset + sizeof(*ni), tvb_length_remaining(tvb, p),
+               offset + sizeof(*ni), -1,
                "Supported type bitmap%s",
                (flags & 0x0001) ? ", compressed" : "");
            field_tree = proto_item_add_subtree(tf,
@@ -562,7 +720,7 @@ dissect_nodeinfo(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree
            n = 0;
            while (tvb_bytes_exist(tvb, p, sizeof(guint32))) { /* XXXX Check what? */
                if ((flags & 0x0001) == 0) {
-                   l = tvb_length_remaining(tvb, offset + sizeof(*ni));
+                   l = tvb_reported_length_remaining(tvb, offset + sizeof(*ni));
                    l /= sizeof(guint32);
                    i = 0;
                } else {
@@ -592,19 +750,16 @@ dissect_nodeinfo(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree
            proto_tree_add_text(tree, tvb, offset + sizeof(*ni),
                sizeof(gint32), "TTL: %d", (gint32)tvb_get_ntohl(tvb, offset + sizeof *ni));
            tf = proto_tree_add_text(tree, tvb,
-               offset + sizeof(*ni) + sizeof(guint32),
-               tvb_length_remaining(tvb, offset),
+               offset + sizeof(*ni) + sizeof(guint32), -1,
                "DNS labels");
            field_tree = proto_item_add_subtree(tf, ett_nodeinfo_nodedns);
-           /* XXXX - clean this up when packet-dns.c has been tvbuffified */
-           tvb_compat(tvb, &pd, &top_level_offset);
            j = offset + sizeof (*ni) + sizeof(guint32);
-           while (j < tvb_length(tvb)) {
-               l = get_dns_name(pd, top_level_offset + j,
-                  top_level_offset + offset + sizeof (*ni) + sizeof(guint32),
+           while (j < tvb_reported_length(tvb)) {
+               l = get_dns_name(tvb, j,
+                  offset + sizeof (*ni) + sizeof(guint32),
                   dname,sizeof(dname));
-               if (tvb_bytes_exist(tvb, top_level_offset + j + l, 1) &&
-                   tvb_get_guint8(tvb, top_level_offset + j + l) == 0) {
+               if (tvb_bytes_exist(tvb, j + l, 1) &&
+                   tvb_get_guint8(tvb, j + l) == 0) {
                    l++;
                    proto_tree_add_text(field_tree, tvb, j, l,
                        "DNS label: %s (truncated)", dname);
@@ -617,10 +772,10 @@ dissect_nodeinfo(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree
            off = tvb_length_remaining(tvb, offset);
            break;
        case NI_QTYPE_NODEADDR:
-           n = tvb_length_remaining(tvb, offset + sizeof(*ni));
+           n = tvb_reported_length_remaining(tvb, offset + sizeof(*ni));
            n /= sizeof(gint32) + sizeof(struct e_in6_addr);
            tf = proto_tree_add_text(tree, tvb,
-               offset + sizeof(*ni), tvb_length_remaining(tvb, offset), "IPv6 node addresses");
+               offset + sizeof(*ni), -1, "IPv6 node addresses");
            field_tree = proto_item_add_subtree(tf, ett_nodeinfo_node6);
            p = offset + sizeof (*ni);
            for (i = 0; i < n; i++) {
@@ -636,10 +791,10 @@ dissect_nodeinfo(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree
            off = tvb_length_remaining(tvb, offset);
            break;
        case NI_QTYPE_IPV4ADDR:
-           n = tvb_length_remaining(tvb, offset + sizeof(*ni));
+           n = tvb_reported_length_remaining(tvb, offset + sizeof(*ni));
            n /= sizeof(gint32) + sizeof(guint32);
            tf = proto_tree_add_text(tree, tvb,
-               offset + sizeof(*ni), tvb_length_remaining(tvb, offset), "IPv4 node addresses");
+               offset + sizeof(*ni), -1, "IPv4 node addresses");
            field_tree = proto_item_add_subtree(tf, ett_nodeinfo_node4);
            p = offset + sizeof *ni;
            for (i = 0; i < n; i++) {
@@ -655,7 +810,7 @@ dissect_nodeinfo(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree
 nodata:;
 
     /* the rest of data */
-    dissect_data(tvb_new_subset(tvb, offset + off, -1, -1), 0, pinfo, tree);
+    call_dissector(data_handle,tvb_new_subset(tvb, offset + off, -1, -1), pinfo, tree);
 }
 
 static void
@@ -703,7 +858,7 @@ dissect_rrenum(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
     proto_tree_add_text(tree, tvb,
        offset + offsetof(struct icmp6_router_renum, rr_maxdelay), 2,
        "Max delay: 0x%04x", pntohs(&rr->rr_maxdelay));
-    dissect_data(tvb_new_subset(tvb, offset + sizeof(*rr), -1, -1), 0, pinfo, tree);   /*XXX*/
+    call_dissector(data_handle,tvb_new_subset(tvb, offset + sizeof(*rr), -1, -1), pinfo, tree);        /*XXX*/
 
     if (rr->rr_code == ICMP6_ROUTER_RENUMBERING_COMMAND) {
        off = offset + sizeof(*rr);
@@ -838,10 +993,10 @@ dissect_icmpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     int offset;
     tvbuff_t *next_tvb;
 
-    if (check_col(pinfo->fd, COL_PROTOCOL))
-       col_set_str(pinfo->fd, COL_PROTOCOL, "ICMPv6");
-    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, "ICMPv6");
+    if (check_col(pinfo->cinfo, COL_INFO))
+       col_clear(pinfo->cinfo, COL_INFO);
 
     offset = 0;
     tvb_memcpy(tvb, (guint8 *)&icmp6_hdr, offset, sizeof icmp6_hdr);
@@ -997,7 +1152,7 @@ dissect_icmpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        break;
     }
 
-    if (check_col(pinfo->fd, COL_INFO)) {
+    if (check_col(pinfo->cinfo, COL_INFO)) {
        char typebuf[256], codebuf[256];
 
        if (coltypename && strcmp(coltypename, "Unknown") == 0) {
@@ -1011,9 +1166,9 @@ dissect_icmpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
            colcodename = codebuf;
        }
        if (colcodename) {
-           col_add_fstr(pinfo->fd, COL_INFO, "%s (%s)", coltypename, colcodename);
+           col_add_fstr(pinfo->cinfo, COL_INFO, "%s (%s)", coltypename, colcodename);
        } else {
-           col_add_fstr(pinfo->fd, COL_INFO, "%s", coltypename);
+           col_add_fstr(pinfo->cinfo, COL_INFO, "%s", coltypename);
        }
     }
 
@@ -1110,7 +1265,7 @@ dissect_icmpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                offset + offsetof(struct icmp6_hdr, icmp6_seq), 2,
                "Sequence: 0x%04x", (guint16)ntohs(dp->icmp6_seq));
            next_tvb = tvb_new_subset(tvb, offset + sizeof(*dp), -1, -1);
-           dissect_data(next_tvb, 0, pinfo, icmp6_tree);
+           call_dissector(data_handle,next_tvb, pinfo, icmp6_tree);
            break;
        case ICMP6_MEMBERSHIP_QUERY:
        case ICMP6_MEMBERSHIP_REPORT:
@@ -1144,15 +1299,17 @@ dissect_icmpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
            field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
            proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
                decode_boolean_bitfield(ra_flags,
-                       0x80, 8, "Managed", "Not managed"));
+                       ND_RA_FLAG_MANAGED, 8, "Managed", "Not managed"));
            proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
                decode_boolean_bitfield(ra_flags,
-                       0x40, 8, "Other", "Not other"));
-    /* BT INSERT BEGIN */
+                       ND_RA_FLAG_OTHER, 8, "Other", "Not other"));
            proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
                decode_boolean_bitfield(ra_flags,
-                       0x20, 8, "Home Agent", "Not Home Agent"));              
-    /* BT INSERT END */
+                       ND_RA_FLAG_HOME_AGENT, 8,
+                       "Home Agent", "Not Home Agent"));               
+           proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
+               decode_enumerated_bitfield(ra_flags, ND_RA_FLAG_RTPREF_MASK, 8,
+               names_router_pref, "Router preference: %s"));
            proto_tree_add_text(icmp6_tree, tvb,
                offset + offsetof(struct nd_router_advert, nd_ra_router_lifetime),
                2, "Router lifetime: %u",
@@ -1265,7 +1422,7 @@ dissect_icmpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
            break;
        default:
            next_tvb = tvb_new_subset(tvb, offset + sizeof(*dp), -1, -1);
-           dissect_data(next_tvb, 0, pinfo, tree);
+           call_dissector(data_handle,next_tvb, pinfo, tree);
            break;
        }
     }
@@ -1277,16 +1434,16 @@ proto_register_icmpv6(void)
   static hf_register_info hf[] = {
     { &hf_icmpv6_type,
       { "Type",           "icmpv6.type",       FT_UINT8,  BASE_DEC, NULL, 0x0,
-       "" }},
+       "", HFILL }},
     { &hf_icmpv6_code,
       { "Code",           "icmpv6.code",       FT_UINT8,  BASE_DEC, NULL, 0x0,
-       "" }},
+       "", HFILL }},
     { &hf_icmpv6_checksum,
       { "Checksum",       "icmpv6.checksum",   FT_UINT16, BASE_HEX, NULL, 0x0,
-       "" }},
+       "", HFILL }},
     { &hf_icmpv6_checksum_bad,
       { "Bad Checksum",   "icmpv6.checksum_bad", FT_BOOLEAN, BASE_NONE,        NULL, 0x0,
-       "" }},
+       "", HFILL }},
   };
   static gint *ett[] = {
     &ett_icmpv6,
@@ -1310,10 +1467,14 @@ proto_register_icmpv6(void)
 void
 proto_reg_handoff_icmpv6(void)
 {
-  dissector_add("ip.proto", IP_PROTO_ICMPV6, dissect_icmpv6, proto_icmpv6);
+  dissector_handle_t icmpv6_handle;
+
+  icmpv6_handle = create_dissector_handle(dissect_icmpv6, proto_icmpv6);
+  dissector_add("ip.proto", IP_PROTO_ICMPV6, icmpv6_handle);
 
   /*
    * Get a handle for the IPv6 dissector.
    */
   ipv6_handle = find_dissector("ipv6");
+  data_handle = find_dissector("data");
 }