From Nicolas Dichtel:
[obnox/wireshark/wip.git] / epan / dissectors / packet-icmpv6.c
index a3fb9af4c7a6754c0b3eb4463cae6d76d0d782e1..3bbb4fe4b22f50754a8ec338f8aa4975954028b1 100644 (file)
@@ -3,14 +3,17 @@
  *
  * $Id$
  *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@ethereal.com>
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1998 Gerald Combs
  *
  * MobileIPv6 support added by Tomislav Borosa <tomislav.borosa@siemens.hr>
+ * Copyright 2006, Nicolas DICHTEL - 6WIND - <nicolas.dichtel@6wind.com>
  *
  * HMIPv6 support added by Martti Kuparinen <martti.kuparinen@iki.fi>
  *
+ * FMIPv6 support added by Martin Andre <andre@clarinet.u-strasbg.fr>
+ *
  * 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
  * and
  *
  *     draft-ietf-mobileip-hmipv6-05.txt
+ * 
+ * and
+ * 
+ *     rfc4068.txt
  */
 
 static int proto_icmpv6 = -1;
@@ -73,6 +80,14 @@ static int hf_icmpv6_code = -1;
 static int hf_icmpv6_checksum = -1;
 static int hf_icmpv6_checksum_bad = -1;
 static int hf_icmpv6_haad_ha_addrs = -1;
+static int hf_icmpv6_ra_cur_hop_limit = -1;
+static int hf_icmpv6_ra_router_lifetime = -1;
+static int hf_icmpv6_ra_reachable_time = -1;
+static int hf_icmpv6_ra_retrans_timer = -1;
+
+static int hf_icmpv6_option = -1;
+static int hf_icmpv6_option_type = -1;
+static int hf_icmpv6_option_length = -1;
 
 static gint ett_icmpv6 = -1;
 static gint ett_icmpv6opt = -1;
@@ -113,6 +128,72 @@ static const value_string names_router_pref[] = {
        { 0, NULL}
 };
 
+static const value_string names_fmip6_prrtadv_code[] = {
+       { FMIP6_PRRTADV_MNTUP,  "MN should use AP-ID, AR-info tuple" },
+       { FMIP6_PRRTADV_NI_HOVER,       "Network Initiated Handover trigger" },
+       { FMIP6_PRRTADV_NORTINFO,       "No new router information" },
+       { FMIP6_PRRTADV_LIMRTINFO,      "Limited new router information" },
+       { FMIP6_PRRTADV_UNSOL,  "Unsolicited" },
+       { 0,                    NULL }
+};
+
+static const value_string names_fmip6_hi_code[] = {
+       { FMIP6_HI_PCOA,        "FBU sent from previous link" },
+       { FMIP6_HI_NOTPCOA,     "FBU sent from new link" },
+       { 0,                    NULL }
+};
+
+static const value_string names_fmip6_hack_code[] = {
+       { FMIP6_HACK_VALID,     "Handover Accepted, NCoA valid" },
+       { FMIP6_HACK_INVALID,   "Handover Accepted, NCoA not valid" },
+       { FMIP6_HACK_INUSE,     "Handover Accepted, NCoA in use" },
+       { FMIP6_HACK_ASSIGNED,  "Handover Accepted, NCoA assigned" },
+       { FMIP6_HACK_NOTASSIGNED,       "Handover Accepted, NCoA not assigned" },
+       { FMIP6_HACK_NOTACCEPTED,       "Handover Not Accepted, reason unspecified" },
+       { FMIP6_HACK_PROHIBITED,        "Administratively prohibited" },
+       { FMIP6_HACK_INSUFFICIENT,      "Insufficient resources" },
+       { 0,                    NULL }
+};
+
+static const value_string names_fmip6_ip_addr_opt_code[] = {
+       { FMIP6_OPT_IP_ADDRESS_OPTCODE_PCOA,    "Old Care-of Address" },
+       { FMIP6_OPT_IP_ADDRESS_OPTCODE_NCOA,    "New Care-of Address" },
+       { FMIP6_OPT_IP_ADDRESS_OPTCODE_NAR,     "NAR's IP address" },
+       { 0,                    NULL }
+};
+
+static const value_string names_fmip6_lla_opt_code[] = {
+       { FMIP6_OPT_LINK_LAYER_ADDRESS_OPTCODE_WILDCARD,        "Wildcard" },
+       { FMIP6_OPT_LINK_LAYER_ADDRESS_OPTCODE_NAP,     "Link-layer Address of the New Access Point" },
+       { FMIP6_OPT_LINK_LAYER_ADDRESS_OPTCODE_MN,      "Link-layer Address of the MN" },
+       { FMIP6_OPT_LINK_LAYER_ADDRESS_OPTCODE_NAR,     "Link-layer Address of the NAR" },
+       { FMIP6_OPT_LINK_LAYER_ADDRESS_OPTCODE_SRC,     "Link-layer Address of the source" },
+       { FMIP6_OPT_LINK_LAYER_ADDRESS_OPTCODE_CURROUTER,       "The AP belongs to the current interface of the router" },
+       { FMIP6_OPT_LINK_LAYER_ADDRESS_OPTCODE_NOPREFIX,        "No prefix information available" },
+       { FMIP6_OPT_LINK_LAYER_ADDRESS_OPTCODE_NOSUPPORT,       "No fast handovers support available" },
+       { 0,                    NULL }
+};
+
+static const value_string names_fmip6_naack_opt_status[] = {
+       { FMIP6_OPT_NEIGHBOR_ADV_ACK_STATUS_INVALID,    "New CoA is invalid" },
+       { FMIP6_OPT_NEIGHBOR_ADV_ACK_STATUS_INVALID_NEW,        "New CoA is invalid, use the supplied CoA" },
+       { FMIP6_OPT_NEIGHBOR_ADV_ACK_STATUS_UNRECOGNIZED,       "LLA is unrecognized" },
+       { 0,                    NULL }
+};
+
+static const value_string option_vals[] = {
+       { ND_OPT_SOURCE_LINKADDR,       "Source link-layer address" },
+       { ND_OPT_TARGET_LINKADDR,       "Target link-layer address" },
+       { ND_OPT_PREFIX_INFORMATION,    "Prefix information" },
+       { ND_OPT_REDIRECTED_HEADER,     "Redirected header" },
+       { ND_OPT_MTU,                   "MTU" },
+       { ND_OPT_ADVINTERVAL,           "Advertisement Interval" },
+       { ND_OPT_HOMEAGENT_INFO,        "Home Agent Information" },
+       { ND_OPT_MAP,                   "HMIPv6 MAP option" },
+       { FMIP6_OPT_NEIGHBOR_ADV_ACK,   "Neighbor Advertisement Acknowledgment" },
+       { 0,                    NULL }
+};
+
 static void
 dissect_contained_icmpv6(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
 {
@@ -140,7 +221,7 @@ dissect_contained_icmpv6(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tr
 }
 
 static void
-dissect_icmpv6opt(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
+dissect_icmpv6ndopt(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
 {
     proto_tree *icmp6opt_tree, *field_tree;
     proto_item *ti, *tf;
@@ -162,7 +243,7 @@ again:
     len = opt->nd_opt_len << 3;
 
     /* !!! specify length */
-    ti = proto_tree_add_text(tree, tvb, offset, len, "ICMPv6 options");
+    ti = proto_tree_add_item(tree, hf_icmpv6_option, tvb, offset, len, FALSE);
     icmp6opt_tree = proto_item_add_subtree(ti, ett_icmpv6opt);
 
     if (len == 0) {
@@ -173,42 +254,19 @@ again:
        return; /* we must not try to decode this */
     }
 
-    switch (opt->nd_opt_type) {
-    case ND_OPT_SOURCE_LINKADDR:
-       typename = "Source link-layer address";
-       break;
-    case ND_OPT_TARGET_LINKADDR:
-       typename = "Target link-layer address";
-       break;
-    case ND_OPT_PREFIX_INFORMATION:
-       typename = "Prefix information";
-       break;
-    case ND_OPT_REDIRECTED_HEADER:
-       typename = "Redirected header";
-       break;
-    case ND_OPT_MTU:
-       typename = "MTU";
-       break;
-    case ND_OPT_ADVINTERVAL:
-       typename = "Advertisement Interval";
-       break;
-    case ND_OPT_HOMEAGENT_INFO:
-       typename = "Home Agent Information";
-       break;
-    case ND_OPT_MAP:
-       typename = "HMIPv6 MAP option";
-       break;
-    default:
-       typename = "Unknown";
-       break;
-    }
+    typename = val_to_str(opt->nd_opt_type, option_vals, "Unknown");
+
+    /* Add option name to option root label */
+    proto_item_append_text(ti, " (%s)", typename);
 
-    proto_tree_add_text(icmp6opt_tree, tvb,
+    /* Option type */
+    proto_tree_add_uint(icmp6opt_tree, hf_icmpv6_option_type, tvb,
        offset + offsetof(struct nd_opt_hdr, nd_opt_type), 1,
-       "Type: %u (%s)", opt->nd_opt_type, typename);
-    proto_tree_add_text(icmp6opt_tree, tvb,
+       opt->nd_opt_type);
+    /* Option length */
+    proto_tree_add_uint(icmp6opt_tree, hf_icmpv6_option_length, tvb,
        offset + offsetof(struct nd_opt_hdr, nd_opt_len), 1,
-       "Length: %u bytes (%u)", opt->nd_opt_len << 3, opt->nd_opt_len);
+       opt->nd_opt_len << 3);
 
     /* decode... */
     switch (opt->nd_opt_type) {
@@ -253,14 +311,24 @@ again:
            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",
-           pntohl(&pi->nd_opt_pi_valid_time));
-       proto_tree_add_text(icmp6opt_tree, tvb,
-           offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_preferred_time),
-           4, "Preferred lifetime: 0x%08x",
-           pntohl(&pi->nd_opt_pi_preferred_time));
+       if (pntohl(&pi->nd_opt_pi_valid_time) == 0xffffffff)
+               proto_tree_add_text(icmp6opt_tree, tvb,
+                               offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_valid_time),
+                               4, "Valid lifetime: infinity");
+       else
+               proto_tree_add_text(icmp6opt_tree, tvb,
+                               offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_valid_time),
+                               4, "Valid lifetime: %u",
+                               pntohl(&pi->nd_opt_pi_valid_time));
+       if (pntohl(&pi->nd_opt_pi_preferred_time) == 0xffffffff)
+               proto_tree_add_text(icmp6opt_tree, tvb,
+                               offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_preferred_time),
+                               4, "Preferred lifetime: infinity");
+       else
+               proto_tree_add_text(icmp6opt_tree, tvb,
+                               offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_preferred_time),
+                               4, "Preferred lifetime: %u",
+                               pntohl(&pi->nd_opt_pi_preferred_time));
        proto_tree_add_text(icmp6opt_tree, tvb,
            offset + offsetof(struct nd_opt_prefix_info, nd_opt_pi_prefix),
            16, "Prefix: %s", ip6_to_str(&pi->nd_opt_pi_prefix));
@@ -414,12 +482,164 @@ again:
        }
        break;
       }
+
+    case FMIP6_OPT_NEIGHBOR_ADV_ACK:
+      {
+       struct fmip6_opt_neighbor_advertisement_ack fmip6_opt_neighbor_advertisement_ack, *opt_naack;
+       struct e_in6_addr in6;
+
+       opt_naack = &fmip6_opt_neighbor_advertisement_ack;
+       tvb_memcpy(tvb, (guint8 *)opt_naack, offset, sizeof *opt_naack);
+
+       proto_tree_add_text(icmp6opt_tree, tvb,
+                       offset + offsetof(struct fmip6_opt_neighbor_advertisement_ack, fmip6_opt_optcode),
+                       1, "Option-Code: %u",
+                       opt_naack->fmip6_opt_optcode);
+
+       proto_tree_add_text(icmp6opt_tree, tvb,
+                       offset + offsetof(struct fmip6_opt_neighbor_advertisement_ack, fmip6_opt_status),
+                       1, "Status: %s",
+                       val_to_str(opt_naack->fmip6_opt_status, names_fmip6_naack_opt_status, "Unknown"));
+
+       if (opt_naack->fmip6_opt_len == 3) 
+       {
+               tvb_memcpy(tvb, (guint8 *)&in6, offset + sizeof(*opt_naack), 16);
+               proto_tree_add_text(icmp6opt_tree, tvb,
+                       offset + sizeof(*opt_naack), 
+                       16, "New Care-of Address: %s",
+                       ip6_to_str(&in6));
+       }
+
+       break;
+      }
     }
 
     offset += (opt->nd_opt_len << 3);
+
+    /* Set length of option tree */
+    proto_item_set_len(ti, opt->nd_opt_len << 3);
     goto again;
 }
 
+static void
+dissect_icmpv6fmip6opt(tvbuff_t *tvb, int offset, proto_tree *tree)
+{
+       proto_tree *icmp6opt_tree;
+       proto_item *ti;
+       struct fmip6_opt_hdr fmip6_opt_hdr, *opt;
+       int len;
+       char *typename;
+
+       if (!tree)
+               return;
+
+again:
+       if ((int)tvb_reported_length(tvb) <= offset)
+               return; /* No more options left */
+
+       opt = &fmip6_opt_hdr;
+       tvb_memcpy(tvb, (guint8 *)opt, offset, sizeof *opt);
+       len = opt->fmip6_opt_len << 3;
+
+       /* !!! specify length */
+       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 fmip6_opt_hdr, fmip6_opt_len), 1,
+                               "Invalid option length: %u",
+                               opt->fmip6_opt_len);
+               return; /* we must not try to decode this */
+       }
+
+       switch (opt->fmip6_opt_type) {
+               case FMIP6_OPT_IP_ADDRESS:
+                       typename = "IP Address";
+                       break;
+               case FMIP6_OPT_NEW_ROUTER_PREFIX_INFO:
+                       typename = "New Router Prefix Information";
+                       break;
+               case FMIP6_OPT_LINK_LAYER_ADDRESS:
+                       typename = "Link-layer Address";
+                       break;
+               default:
+                       typename = "Unknown";
+                       break;
+       }
+
+       proto_tree_add_text(icmp6opt_tree, tvb,
+                       offset + offsetof(struct fmip6_opt_hdr, fmip6_opt_type), 1,
+                       "Type: %u (%s)", opt->fmip6_opt_type, typename);
+       proto_tree_add_text(icmp6opt_tree, tvb,
+                       offset + offsetof(struct fmip6_opt_hdr, fmip6_opt_len), 1,
+                       "Length: %u bytes (%u)", opt->fmip6_opt_len << 3, opt->fmip6_opt_len);
+
+       /* decode... */
+       switch (opt->fmip6_opt_type) {
+               case FMIP6_OPT_IP_ADDRESS:
+                       {
+                               struct fmip6_opt_ip_address fmip6_opt_ip_address, *opt_ip;
+
+                               opt_ip = &fmip6_opt_ip_address;
+                               tvb_memcpy(tvb, (guint8 *)opt_ip, offset, sizeof *opt_ip);
+
+                               proto_tree_add_text(icmp6opt_tree, tvb,
+                                               offset + offsetof(struct fmip6_opt_hdr, fmip6_opt_optcode), 1, "Option-Code: %s",
+                                               val_to_str(opt->fmip6_opt_optcode, names_fmip6_ip_addr_opt_code, "Unknown"));
+
+                               proto_tree_add_text(icmp6opt_tree, tvb,
+                                               offset + offsetof(struct fmip6_opt_ip_address, fmip6_opt_prefix_len),
+                                               1, "Prefix length: %u", opt_ip->fmip6_opt_prefix_len);
+
+                               proto_tree_add_text(icmp6opt_tree, tvb,
+                                               offset + offsetof(struct fmip6_opt_ip_address, fmip6_opt_ip6_address), 
+                                               16, "IPv6 Address: %s",
+                                               ip6_to_str(&opt_ip->fmip6_opt_ip6_address));
+                               break;
+                       }
+               case FMIP6_OPT_NEW_ROUTER_PREFIX_INFO:
+                       {
+                               struct fmip6_opt_new_router_prefix_info fmip6_opt_new_router_prefix_info, *opt_nr;
+
+                               opt_nr = &fmip6_opt_new_router_prefix_info;
+                               tvb_memcpy(tvb, (guint8 *)opt_nr, offset, sizeof *opt_nr);
+
+                               proto_tree_add_text(icmp6opt_tree, tvb,
+                                               offset + offsetof(struct fmip6_opt_hdr, fmip6_opt_optcode), 1, "Option-Code: %u",
+                                               opt->fmip6_opt_optcode);
+
+                               proto_tree_add_text(icmp6opt_tree, tvb,
+                                               offset + offsetof(struct fmip6_opt_new_router_prefix_info, fmip6_opt_prefix_len),
+                                               1, "Prefix length: %u", opt_nr->fmip6_opt_prefix_len);
+
+                               proto_tree_add_text(icmp6opt_tree, tvb,
+                                               offset + offsetof(struct fmip6_opt_new_router_prefix_info, fmip6_opt_prefix), 
+                                               16, "Prefix: %s",
+                                               ip6_to_str(&opt_nr->fmip6_opt_prefix));
+                               break;
+                       }
+                       break;
+               case FMIP6_OPT_LINK_LAYER_ADDRESS:
+                       {
+                               int len, p;
+
+                               p = offset + sizeof(*opt);
+                               proto_tree_add_text(icmp6opt_tree, tvb,
+                                               offset + offsetof(struct fmip6_opt_hdr, fmip6_opt_optcode), 1, "Option-Code: %s",
+                                               val_to_str(opt->fmip6_opt_optcode, names_fmip6_lla_opt_code, "Unknown"));
+                               len = (opt->fmip6_opt_len << 3) - sizeof(*opt);
+                               proto_tree_add_text(icmp6opt_tree, tvb,
+                                               offset + sizeof(*opt), len, "Link-layer address: %s",
+                                               bytestring_to_str(tvb_get_ptr(tvb, p, len), len, ':'));
+                               break;
+                       }
+       }
+
+       offset += (opt->fmip6_opt_len << 3);
+       goto again;
+}
+
 /*
  * draft-ietf-ipngwg-icmp-name-lookups-07.txt
  * Note that the packet format was changed several times in the past.
@@ -471,8 +691,7 @@ bitrange0(guint32 v, int s, char *buf, int buflen)
                        l = g_snprintf(p, ep - p, ",%d-%d", s + off,
                            s + off + i - 1);
                }
-               if (l == -1 || l > ep - p) {
-                       buf[0] = '\0';
+               if (l == -1 || l >= ep - p) {
                        return NULL;
                }
                v >>= i; off += i;
@@ -506,13 +725,13 @@ static void
 dissect_nodeinfo(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
 {
     proto_tree *field_tree;
-       proto_item *tf;
+    proto_item *tf;
     struct icmp6_nodeinfo icmp6_nodeinfo, *ni;
     int off;
     unsigned int j;
     int i, n, l, p;
     guint16 flags;
-    char dname[MAXDNAME];
+    char *dname;
     guint32 ipaddr;
 
     ni = &icmp6_nodeinfo;
@@ -623,7 +842,7 @@ dissect_nodeinfo(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree
            break;
        case ICMP6_NI_SUBJ_FQDN:
            l = get_dns_name(tvb, offset + sizeof(*ni),
-               offset + sizeof(*ni), dname, sizeof(dname));
+               offset + sizeof(*ni), &dname);
            if (tvb_bytes_exist(tvb, offset + sizeof(*ni) + l, 1) &&
                tvb_get_guint8(tvb, offset + sizeof(*ni) + l) == 0) {
                l++;
@@ -703,7 +922,7 @@ dissect_nodeinfo(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree
            while (j < tvb_reported_length(tvb)) {
                l = get_dns_name(tvb, j,
                   offset + sizeof (*ni) + sizeof(guint32),
-                  dname,sizeof(dname));
+                  &dname);
                if (tvb_bytes_exist(tvb, j + l, 1) &&
                    tvb_get_guint8(tvb, j + l) == 0) {
                    l++;
@@ -764,7 +983,7 @@ static void
 dissect_rrenum(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
 {
     proto_tree *field_tree, *opt_tree;
-       proto_item *tf;
+    proto_item *tf;
     struct icmp6_router_renum icmp6_router_renum, *rr;
     struct rr_pco_match rr_pco_match, *match;
     struct rr_pco_use rr_pco_use, *use;
@@ -1197,6 +1416,31 @@ dissect_icmpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        codename = "Should always be zero";
        colcodename = NULL;
        break;
+               case ICMP6_EXPERIMENTAL_MOBILITY:
+                       typename = coltypename ="Experimental Mobility";
+                       switch (dp->icmp6_data8[0]) {
+                               case FMIP6_SUBTYPE_RTSOLPR:
+                                       typename = coltypename ="RtSolPr (ICMPv6 Experimental Mobility)";
+                                       codename = "Should always be zero";
+                                       colcodename = NULL;
+                                       break;
+                               case FMIP6_SUBTYPE_PRRTADV:
+                                       typename = coltypename ="PrRtAdv (ICMPv6 Experimental Mobility)";
+                                       codename = val_to_str(dp->icmp6_code, names_fmip6_prrtadv_code, "Unknown");
+                                       colcodename = NULL;
+                                       break;
+                               case FMIP6_SUBTYPE_HI:
+                                       typename = coltypename ="HI (ICMPv6 Experimental Mobility)";
+                                       codename = val_to_str(dp->icmp6_code, names_fmip6_hi_code, "Unknown");
+                                       colcodename = NULL;
+                                       break;
+                               case FMIP6_SUBTYPE_HACK:
+                                       typename = coltypename ="HAck (ICMPv6 Experimental Mobility)";
+                                       codename = val_to_str(dp->icmp6_code, names_fmip6_hack_code, "Unknown");
+                                       colcodename = NULL;
+                                       break;
+                       }
+                       break;
     }
 
     if (check_col(pinfo->cinfo, COL_INFO)) {
@@ -1375,7 +1619,7 @@ dissect_icmpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                ip6_to_str((const struct e_in6_addr *)(tvb_get_ptr(tvb, offset + sizeof *dp, sizeof (struct e_in6_addr)))));
            break;
        case ND_ROUTER_SOLICIT:
-           dissect_icmpv6opt(tvb, offset + sizeof(*dp), pinfo, icmp6_tree);
+           dissect_icmpv6ndopt(tvb, offset + sizeof(*dp), pinfo, icmp6_tree);
            break;
        case ICMP6_MLDV2_REPORT: {
          guint16 nbRecords;
@@ -1392,14 +1636,18 @@ dissect_icmpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
            ra = &nd_router_advert;
            tvb_memcpy(tvb, (guint8 *)ra, offset, sizeof *ra);
-           proto_tree_add_text(icmp6_tree, tvb,
+
+           /* Current hop limit */
+           proto_tree_add_uint(icmp6_tree, hf_icmpv6_ra_cur_hop_limit, tvb,
                offset + offsetof(struct nd_router_advert, nd_ra_curhoplimit),
-               1, "Cur hop limit: %u", ra->nd_ra_curhoplimit);
+               1, ra->nd_ra_curhoplimit);
 
+           /* Flags */
            flagoff = offset + offsetof(struct nd_router_advert, nd_ra_flags_reserved);
            ra_flags = tvb_get_guint8(tvb, flagoff);
            tf = proto_tree_add_text(icmp6_tree, tvb, flagoff, 1, "Flags: 0x%02x", ra_flags);
            field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
+
            proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
                decode_boolean_bitfield(ra_flags,
                        ND_RA_FLAG_MANAGED, 8, "Managed", "Not managed"));
@@ -1413,17 +1661,23 @@ dissect_icmpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
            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,
+
+           /* Router lifetime */
+           proto_tree_add_uint(icmp6_tree, hf_icmpv6_ra_router_lifetime, tvb,
                offset + offsetof(struct nd_router_advert, nd_ra_router_lifetime),
-               2, "Router lifetime: %u",
-               (guint16)g_ntohs(ra->nd_ra_router_lifetime));
-           proto_tree_add_text(icmp6_tree, tvb,
+               2, (guint16)g_ntohs(ra->nd_ra_router_lifetime));
+
+           /* Reachable time */
+           proto_tree_add_uint(icmp6_tree, hf_icmpv6_ra_reachable_time, tvb,
                offset + offsetof(struct nd_router_advert, nd_ra_reachable), 4,
-               "Reachable time: %u", pntohl(&ra->nd_ra_reachable));
-           proto_tree_add_text(icmp6_tree, tvb,
+               pntohl(&ra->nd_ra_reachable));
+
+           /* Retrans timer */
+           proto_tree_add_uint(icmp6_tree, hf_icmpv6_ra_retrans_timer, tvb,
                offset + offsetof(struct nd_router_advert, nd_ra_retransmit), 4,
-               "Retrans time: %u", pntohl(&ra->nd_ra_retransmit));
-           dissect_icmpv6opt(tvb, offset + sizeof(struct nd_router_advert), pinfo, icmp6_tree);
+               pntohl(&ra->nd_ra_retransmit));
+
+           dissect_icmpv6ndopt(tvb, offset + sizeof(struct nd_router_advert), pinfo, icmp6_tree);
            break;
          }
        case ND_NEIGHBOR_SOLICIT:
@@ -1442,7 +1696,7 @@ dissect_icmpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 #endif
                        ip6_to_str(&ns->nd_ns_target));
 
-           dissect_icmpv6opt(tvb, offset + sizeof(*ns), pinfo, icmp6_tree);
+           dissect_icmpv6ndopt(tvb, offset + sizeof(*ns), pinfo, icmp6_tree);
            break;
          }
        case ND_NEIGHBOR_ADVERT:
@@ -1477,7 +1731,7 @@ dissect_icmpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 #endif
                        ip6_to_str(&na_target));
 
-           dissect_icmpv6opt(tvb, offset + sizeof(struct nd_neighbor_advert), pinfo, icmp6_tree);
+           dissect_icmpv6ndopt(tvb, offset + sizeof(struct nd_neighbor_advert), pinfo, icmp6_tree);
            break;
          }
        case ND_REDIRECT:
@@ -1506,7 +1760,7 @@ dissect_icmpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 #endif
                        ip6_to_str(&rd->nd_rd_dst));
 
-           dissect_icmpv6opt(tvb, offset + sizeof(*rd), pinfo, icmp6_tree);
+           dissect_icmpv6ndopt(tvb, offset + sizeof(*rd), pinfo, icmp6_tree);
            break;
          }
        case ICMP6_ROUTER_RENUMBERING:
@@ -1583,7 +1837,76 @@ dissect_icmpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                offset + 7, 1, "Reserved: %d",
                tvb_get_guint8(tvb, offset + 7));
            /* Show all options */
-           dissect_icmpv6opt(tvb, offset + 8, pinfo, icmp6_tree);
+           dissect_icmpv6ndopt(tvb, offset + 8, pinfo, icmp6_tree);
+           break;
+                       case ICMP6_EXPERIMENTAL_MOBILITY:
+               switch (dp->icmp6_data8[0]) {
+                       case FMIP6_SUBTYPE_RTSOLPR:
+                               {
+                                       struct fmip6_rtsolpr *rtsolpr;
+                                       rtsolpr = (struct fmip6_rtsolpr*) dp;
+                                       proto_tree_add_text(icmp6_tree, tvb,
+                                                       offset + 4, 1,
+                                                       "Subtype: Router Solicitation for Proxy Advertisement");
+                                       proto_tree_add_text(icmp6_tree, tvb,
+                                                       offset + 6, 2,
+                                                       "Identifier: %d", pntohs(&rtsolpr->fmip6_rtsolpr_id));
+                                       dissect_icmpv6fmip6opt(tvb, offset + sizeof(*dp), icmp6_tree);
+                                       break;
+                               }
+                       case FMIP6_SUBTYPE_PRRTADV:
+                               {
+                                       struct fmip6_prrtadv *prrtadv;
+                                       prrtadv = (struct fmip6_prrtadv*) dp;
+                                       proto_tree_add_text(icmp6_tree, tvb,
+                                                       offset + 4, 1,
+                                                       "Subtype: Proxy Router Advertisement");
+                                       proto_tree_add_text(icmp6_tree, tvb,
+                                                       offset + 6, 2,
+                                                       "Identifier: %d", pntohs(&prrtadv->fmip6_prrtadv_id));
+                                       dissect_icmpv6fmip6opt(tvb, offset + sizeof(*dp), icmp6_tree);
+                                       break;
+                               }
+                       case FMIP6_SUBTYPE_HI:
+                               {
+                                       struct fmip6_hi *hi;
+                                       int flagoff;
+                                       guint8 hi_flags;
+                                       hi = (struct fmip6_hi*) dp;
+                                       proto_tree_add_text(icmp6_tree, tvb,
+                                                       offset + 4, 1,
+                                                       "Subtype: Handover Initiate");
+
+                                       flagoff = offset + offsetof(struct fmip6_hi, fmip6_hi_flags_reserved);
+                                       hi_flags = tvb_get_guint8(tvb, flagoff);
+                                       tf = proto_tree_add_text(icmp6_tree, tvb, flagoff, 1, "Flags: 0x%02x", hi_flags);
+                                       field_tree = proto_item_add_subtree(tf, ett_icmpv6flag);
+                                       proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
+                                                       decode_boolean_bitfield(hi_flags,
+                                                               FMIP_HI_FLAG_ASSIGNED, 8, "Assigned", "Not assigned"));
+                                       proto_tree_add_text(field_tree, tvb, flagoff, 1, "%s",
+                                                       decode_boolean_bitfield(hi_flags,
+                                                               FMIP_HI_FLAG_BUFFER, 8, "Buffered", "Not buffered"));
+                                       proto_tree_add_text(icmp6_tree, tvb,
+                                                       offset + 6, 2,
+                                                       "Identifier: %d", pntohs(&hi->fmip6_hi_id));
+                                       dissect_icmpv6fmip6opt(tvb, offset + sizeof(*dp), icmp6_tree);
+                                       break;
+                               }
+                       case FMIP6_SUBTYPE_HACK:
+                               {
+                                       struct fmip6_hack *hack;
+                                       hack = (struct fmip6_hack*) dp;
+                                       proto_tree_add_text(icmp6_tree, tvb,
+                                                       offset + 4, 1,
+                                                       "Subtype: Handover Acknowledge");
+                                       proto_tree_add_text(icmp6_tree, tvb,
+                                                       offset + 6, 2,
+                                                       "Identifier: %d", pntohs(&hack->fmip6_hack_id));
+                                       dissect_icmpv6fmip6opt(tvb, offset + sizeof(*dp), icmp6_tree);
+                                       break;
+                               }
+               }
            break;
        default:
            next_tvb = tvb_new_subset(tvb, offset + sizeof(*dp), -1, -1);
@@ -1613,6 +1936,27 @@ proto_register_icmpv6(void)
       { "Home Agent Addresses", "icmpv6.haad.ha_addrs",
        FT_IPv6, BASE_HEX, NULL, 0x0,
        "", HFILL }},
+    { &hf_icmpv6_ra_cur_hop_limit,
+      { "Cur hop limit",           "icmpv6.ra.cur_hop_limit", FT_UINT8,  BASE_DEC, NULL, 0x0,
+       "Current hop limit", HFILL }},
+    { &hf_icmpv6_ra_router_lifetime,
+      { "Router lifetime",           "icmpv6.ra.router_lifetime", FT_UINT16,  BASE_DEC, NULL, 0x0,
+       "Router lifetime (s)", HFILL }},
+    { &hf_icmpv6_ra_reachable_time,
+      { "Reachable time",           "icmpv6.ra.reachable_time", FT_UINT32,  BASE_DEC, NULL, 0x0,
+       "Reachable time (ms)", HFILL }},
+    { &hf_icmpv6_ra_retrans_timer,
+      { "Retrans timer",           "icmpv6.ra.retrans_timer", FT_UINT32,  BASE_DEC, NULL, 0x0,
+       "Retrans timer (ms)", HFILL }},
+    { &hf_icmpv6_option,
+      { "ICMPv6 Option",           "icmpv6.option", FT_NONE,  BASE_NONE, NULL, 0x0,
+       "Option", HFILL }},
+    { &hf_icmpv6_option_type,
+      { "Type",           "icmpv6.option.type", FT_UINT8,  BASE_DEC, VALS(option_vals), 0x0,
+       "Options type", HFILL }},
+    { &hf_icmpv6_option_length,
+      { "Length",           "icmpv6.option.length", FT_UINT8,  BASE_DEC, NULL, 0x0,
+       "Options length (in bytes)", HFILL }},
   };
   static gint *ett[] = {
     &ett_icmpv6,