update to netlogon to show DsrGetDcNameEx2() Client account name, domain name and...
[obnox/wireshark/wip.git] / packet-dhcpv6.c
index effb37cb7ad9f98360db21169a9bf1cd30c336d1..75c3654819890c74b726d9dfc6550951eed0e124 100644 (file)
@@ -3,31 +3,34 @@
  * Jun-ichiro itojun Hagino <itojun@iijlab.net>
  * IItom Tsutomu MIENO <iitom@utouto.com>
  * SHIRASAKI Yasuhiro <yasuhiro@gnome.gr.jp>
+ * Tony Lindstrom <tony.lindstrom@ericsson.com>
  *
- * $Id: packet-dhcpv6.c,v 1.6 2002/08/02 23:35:48 jmayer Exp $
+ * $Id: packet-dhcpv6.c,v 1.11 2004/02/25 09:31:05 guy Exp $
  *
  * The information used comes from:
- * draft-ietf-dhc-dhcpv6-26.txt
- * draft-troan-dhcpv6-opt-prefix-delegation-01.txt
- * draft-ietf-dhc-dhcpv6-opt-dnsconfig-02.txt
- *
+ * RFC3315.txt
+ * RFC3319.txt
+ * RFC3633.txt
+ * RFC3646.txt
+ * draft-ietf-dhc-dhcpv6-opt-nisconfig-02.txt
+ * draft-ietf-dhc-dhcpv6-opt-timeconfig-02.txt
  * Note that protocol constants are still subject to change, based on IANA
  * assignment decisions.
  *
  * Ethereal - Network traffic analyzer
  * 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
  * of the License, or (at your option) any later version.
- * 
+ *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
@@ -66,11 +69,11 @@ static guint ett_dhcpv6_option = -1;
 #define        RECONFIGURE             10
 #define        INFORMATION_REQUEST     11
 #define        RELAY_FORW              12
-#define        RELAY_REPL              13
+#define        RELAY_REPLY             13
 
 #define        OPTION_CLIENTID         1
 #define        OPTION_SERVERID         2
-#define        OPTION_IA               3
+#define        OPTION_IA_NA            3
 #define        OPTION_IA_TA            4
 #define        OPTION_IAADDR           5
 #define        OPTION_ORO              6
@@ -87,13 +90,23 @@ static guint ett_dhcpv6_option = -1;
 #define        OPTION_VENDOR_OPTS      17
 #define        OPTION_INTERFACE_ID     18
 #define        OPTION_RECONF_MSG       19
-#define        OPTION_RECONF_NONCE     20
-
-#define        OPTION_DNS_SERVERS      25
-#define        OPTION_DOMAIN_LIST      26
-#define        OPTION_PREFIXDEL        30
-#define        OPTION_PREFIX_INFO      31
-#define        OPTION_PREFIXREQ        32
+#define        OPTION_RECONF_ACCEPT    20
+#define        OPTION_SIP_SERVER_D     21
+#define        OPTION_SIP_SERVER_A     22
+#define        OPTION_DNS_SERVERS      23
+#define        OPTION_DOMAIN_LIST      24
+#define        OPTION_IA_PD            25
+#define        OPTION_IAPREFIX         26
+
+/*
+ * The followings are unassigned numbers.
+ */
+#define OPTION_NIS_SERVERS     35
+#define OPTION_NISP_SERVERS    36
+#define OPTION_NIS_DOMAIN_NAME  37
+#define OPTION_NISP_DOMAIN_NAME 38
+#define OPTION_NTP_SERVERS     40
+#define OPTION_TIME_ZONE       41
 
 #define        DUID_LLT                1
 #define        DUID_EN                 2
@@ -103,7 +116,7 @@ static guint ett_dhcpv6_option = -1;
 static const value_string msgtype_vals[] = {
        { SOLICIT,      "Solicit" },
        { ADVERTISE,    "Advertise" },
-       { REQUEST,      "Request" }, 
+       { REQUEST,      "Request" },
        { CONFIRM,      "Confirm" },
        { RENEW,        "Renew" },
        { REBIND,       "Rebind" },
@@ -113,14 +126,14 @@ static const value_string msgtype_vals[] = {
        { RECONFIGURE,  "Reconfigure" },
        { INFORMATION_REQUEST,  "Information-request" },
        { RELAY_FORW,   "Relay-forw" },
-       { RELAY_REPL,   "Relay-repl" },
+       { RELAY_REPLY,  "Relay-reply" },
        { 0, NULL }
 };
 
 static const value_string opttype_vals[] = {
        { OPTION_CLIENTID,      "Client Identifier" },
        { OPTION_SERVERID,      "Server Identifier" },
-       { OPTION_IA,            "Identify Association" },
+       { OPTION_IA_NA,         "Identify Association" },
        { OPTION_IA_TA,         "Identify Association for Temporary Address" },
        { OPTION_IAADDR,        "IA Address" },
        { OPTION_ORO,           "Option Request" },
@@ -137,12 +150,19 @@ static const value_string opttype_vals[] = {
        { OPTION_VENDOR_OPTS,   "Vendor-specific Information" },
        { OPTION_INTERFACE_ID,  "Interface-Id" },
        { OPTION_RECONF_MSG,    "Reconfigure Message" },
-       { OPTION_RECONF_NONCE,  "Reconfigure Nonce" },
-       { OPTION_DNS_SERVERS,   "Domain Name Server" },
+       { OPTION_RECONF_ACCEPT, "Reconfigure Accept" },
+       { OPTION_SIP_SERVER_D,  "SIP Server Domain Name List" },
+       { OPTION_SIP_SERVER_A,  "SIP Servers IPv6 Address List" },
+       { OPTION_DNS_SERVERS,   "DNS recursive name server" },
        { OPTION_DOMAIN_LIST,   "Domain Search List" },
-       { OPTION_PREFIXDEL,     "Prefix Delegation" },
-       { OPTION_PREFIX_INFO,   "Prefix Information" },
-       { OPTION_PREFIXREQ,     "Prefix Request" },
+       { OPTION_IA_PD,         "Identify Association for Prefix Delegation" },
+       { OPTION_IAPREFIX,      "IA Prefix" },
+       { OPTION_NIS_SERVERS,   "Network Information Server" },
+       { OPTION_NISP_SERVERS,  "Network Information Server V2" },
+       { OPTION_NIS_DOMAIN_NAME, "Network Information Server Domain Name" },
+       { OPTION_NISP_DOMAIN_NAME,"Network Information Server V2 Domain Name" },
+       { OPTION_NTP_SERVERS,   "Network Time Protocol Server" },
+       { OPTION_TIME_ZONE,     "Time zone" },
        { 0,    NULL }
 };
 
@@ -150,13 +170,11 @@ static const value_string statuscode_vals[] =
 {
        {0, "Success" },
        {1, "UnspecFail" },
-       {2, "AuthFailed" },
-       {3, "AddrUnvail" },
-       {4, "NoAddrAvail" },
-       {5, "NoBinding" },
-       {6, "ConfNoMatch" },
-       {7, "NotOnLink" },
-       {8, "UseMulticast" },
+       {2, "NoAddrAvail" },
+       {3, "NoBinding" },
+       {4, "NotOnLink" },
+       {5, "UseMulticast" },
+       {6, "NoPrefixAvail" },
        {0, NULL }
 };
 
@@ -176,6 +194,7 @@ dhcpv6_option(tvbuff_t *tvb, proto_tree *bp_tree, int off, int eoff,
 {
        guint16 opttype;
        guint16 optlen;
+       guint16 temp_optlen = 0;
        proto_item *ti;
        proto_tree *subtree;
        int i;
@@ -216,8 +235,8 @@ dhcpv6_option(tvbuff_t *tvb, proto_tree *bp_tree, int off, int eoff,
                }
                duidtype = tvb_get_ntohs(tvb, off);
                proto_tree_add_text(subtree, tvb, off, 2,
-                       "DUID type: %s (%u)", 
-                                   val_to_str(duidtype, 
+                       "DUID type: %s (%u)",
+                                   val_to_str(duidtype,
                                               duidtype_vals, "Unknown"),
                                    duidtype);
                switch (duidtype) {
@@ -268,11 +287,16 @@ dhcpv6_option(tvbuff_t *tvb, proto_tree *bp_tree, int off, int eoff,
                        break;
                }
                break;
-       case OPTION_IA:
-         if (optlen < 12) {
-           proto_tree_add_text(subtree, tvb, off,
-                               optlen, "IA: malformed option");
-           break;
+       case OPTION_IA_NA:
+       case OPTION_IA_PD:
+          if (optlen < 12) {
+             if (opttype == OPTION_IA_NA)
+                proto_tree_add_text(subtree, tvb, off,
+                                    optlen, "IA_NA: malformed option");
+             else
+                proto_tree_add_text(subtree, tvb, off,
+                                    optlen, "IA_PD: malformed option");
+             break;
          }
          proto_tree_add_text(subtree, tvb, off, 4,
                              "IAID: %u",
@@ -281,9 +305,11 @@ dhcpv6_option(tvbuff_t *tvb, proto_tree *bp_tree, int off, int eoff,
                              "T1: %u", tvb_get_ntohl(tvb, off+4));
          proto_tree_add_text(subtree, tvb, off+8, 4,
                              "T2: %u", tvb_get_ntohl(tvb, off+8));
-         if (optlen > 12) {
-           gboolean at_end_;
-           dhcpv6_option(tvb, subtree, off+12, off + optlen - 12, &at_end_);
+
+          temp_optlen = 12;
+         while ((optlen - temp_optlen) > 0) {
+           gboolean at_end_ = FALSE;
+           temp_optlen += dhcpv6_option(tvb, subtree, off+temp_optlen, off + optlen, &at_end_);
          }
          break;
        case OPTION_IA_TA:
@@ -295,38 +321,57 @@ dhcpv6_option(tvbuff_t *tvb, proto_tree *bp_tree, int off, int eoff,
          proto_tree_add_text(subtree, tvb, off, 4,
                              "IAID: %u",
                              tvb_get_ntohl(tvb, off));
-         if (optlen > 4) {
+          temp_optlen = 4;
+         while ((optlen - temp_optlen) > 0) {
            gboolean at_end_;
-           dhcpv6_option(tvb, subtree, off+4, off + optlen - 4, &at_end_);
+           temp_optlen += dhcpv6_option(tvb, subtree, off+temp_optlen, off + optlen, &at_end_);
          }
          break;
        case OPTION_IAADDR:
-         if (optlen < 24) {
-           proto_tree_add_text(subtree, tvb, off,
-                               optlen, "IAADDR: malformed option");
-           break;
-         }
-         tvb_memcpy(tvb, (guint8 *)&in6, off, sizeof(in6));
-         proto_tree_add_text(subtree, tvb, off,
-                             sizeof(in6), "IPv6 address: %s",
-                               ip6_to_str(&in6));
-         proto_tree_add_text(subtree, tvb, off+16, 4,
-                             "preferred-lifetime: %u",
-                             tvb_get_ntohl(tvb, off+16));
-         proto_tree_add_text(subtree, tvb, off+20, 4,
-                             "valid-lifetime: %u",
-                             tvb_get_ntohl(tvb, off+20));
-         if (optlen > 24) {
-           gboolean at_end_;
-           dhcpv6_option(tvb, subtree, off+24, off + optlen - 24, &at_end_);
-         }
-         break;
+        {
+           guint32 preferred_lifetime, valid_lifetime;
+
+           if (optlen < 24) {
+              proto_tree_add_text(subtree, tvb, off,
+                                  optlen, "IAADDR: malformed option");
+              break;
+           }
+           tvb_memcpy(tvb, (guint8 *)&in6, off, sizeof(in6));
+           proto_tree_add_text(subtree, tvb, off,
+                               sizeof(in6), "IPv6 address: %s",
+                               ip6_to_str(&in6));
+           
+           preferred_lifetime = tvb_get_ntohl(tvb, off + 16);
+           valid_lifetime = tvb_get_ntohl(tvb, off + 20);
+           
+           if (preferred_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
+              proto_tree_add_text(subtree, tvb, off + 16, 4,
+                                  "Preferred lifetime: infinity");
+           } else {
+              proto_tree_add_text(subtree, tvb, off + 16, 4,
+                                  "Preferred lifetime: %u", preferred_lifetime);
+           }
+           if (valid_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
+              proto_tree_add_text(subtree, tvb, off + 20, 4,
+                                  "Valid lifetime: infinity");
+           } else {
+              proto_tree_add_text(subtree, tvb, off + 20, 4,
+                                  "Valid lifetime: %u", valid_lifetime);
+           }
+           
+           temp_optlen = 24;
+           while ((optlen - temp_optlen) > 0) {
+              gboolean at_end_;
+              temp_optlen += dhcpv6_option(tvb, subtree, off+temp_optlen, off + optlen, &at_end_);
+           }
+        }
+        break;
        case OPTION_ORO:
                for (i = 0; i < optlen; i += 2) {
                    guint16 requested_opt_code;
                    requested_opt_code = tvb_get_ntohs(tvb, off + i);
                    proto_tree_add_text(subtree, tvb, off + i,
-                           2, "Requested Option code: %s (%d)", 
+                           2, "Requested Option code: %s (%d)",
                                            val_to_str(requested_opt_code,
                                                       opttype_vals,
                                                       "Unknown"),
@@ -361,7 +406,7 @@ dhcpv6_option(tvbuff_t *tvb, proto_tree *bp_tree, int off, int eoff,
          } else {
            gboolean at_end_;
            dhcpv6_option(tvb, subtree, off, off + optlen, &at_end_);
-         }
+          } 
          break;
        case OPTION_AUTH:
          if (optlen < 15) {
@@ -399,18 +444,14 @@ dhcpv6_option(tvbuff_t *tvb, proto_tree *bp_tree, int off, int eoff,
                guint16 status_code;
                char *status_message = 0;
                status_code = tvb_get_ntohs(tvb, off);
-               proto_tree_add_text(subtree, tvb, off, 2, 
+               proto_tree_add_text(subtree, tvb, off, 2,
                                    "Status Code: %s (%d)",
-                                   val_to_str(status_code, statuscode_vals, 
+                                   val_to_str(status_code, statuscode_vals,
                                               "Unknown"),
                                    status_code);
 
-               if (optlen - 2 > 0)
-                   status_message = g_malloc(optlen - 2 + 1);
-               if (status_message != 0){
-                   memset(status_message, 0, optlen - 2 + 1);
-                   status_message = tvb_memcpy(tvb, status_message, off + 2, 
-                                               optlen - 2);
+               if (optlen - 2 > 0) {
+                   status_message = tvb_get_string(tvb, off + 2, optlen - 2);
                    proto_tree_add_text(subtree, tvb, off + 2, optlen - 2,
                                        "Status Message: %s",
                                        status_message);
@@ -466,15 +507,24 @@ dhcpv6_option(tvbuff_t *tvb, proto_tree *bp_tree, int off, int eoff,
                                         msgtype_vals,
                                         "Message Type %u"));
          break;
-       case OPTION_RECONF_NONCE:
-         if (optlen != 8) {
-           proto_tree_add_text(subtree, tvb, off,
-                               optlen, "RECONF_NONCE: malformed option");
-           break;
-         }
-         proto_tree_add_text(subtree, tvb, off, optlen,
-                             "Reconfigure-nonce");
-         break;
+       case OPTION_SIP_SERVER_D:
+               if (optlen > 0) {
+                       proto_tree_add_text(subtree, tvb, off, optlen,
+                               "SIP Servers Domain Search List");
+               }
+       case OPTION_SIP_SERVER_A:
+               if (optlen % 16) {
+                       proto_tree_add_text(subtree, tvb, off, optlen,
+                               "SIP servers address: malformed option");
+                       break;
+               }
+               for (i = 0; i < optlen; i += 16) {
+                       tvb_memcpy(tvb, (guint8 *)&in6, off + i, sizeof(in6));
+                       proto_tree_add_text(subtree, tvb, off + i,
+                               sizeof(in6), "SIP servers address: %s",
+                               ip6_to_str(&in6));
+               }
+               break;
        case OPTION_DNS_SERVERS:
                if (optlen % 16) {
                        proto_tree_add_text(subtree, tvb, off, optlen,
@@ -490,49 +540,108 @@ dhcpv6_option(tvbuff_t *tvb, proto_tree *bp_tree, int off, int eoff,
                break;
        case OPTION_DOMAIN_LIST:
          if (optlen > 0) {
-           proto_tree_add_text(subtree, tvb, off, optlen, "Search String");
+           proto_tree_add_text(subtree, tvb, off, optlen, "DNS Domain Search List");
          }
          break;
-       case OPTION_PREFIXDEL:
-           {
-               gboolean at_end_;
-               dhcpv6_option(tvb, subtree, off, off + optlen, &at_end_);
-           }
-           break;
-       case OPTION_PREFIX_INFO:
+       case OPTION_NIS_SERVERS:
+               if (optlen % 16) {
+                       proto_tree_add_text(subtree, tvb, off, optlen,
+                               "NIS servers address: malformed option");
+                       break;
+               }
+               for (i = 0; i < optlen; i += 16) {
+                       tvb_memcpy(tvb, (guint8 *)&in6, off + i, sizeof(in6));
+                       proto_tree_add_text(subtree, tvb, off + i,
+                               sizeof(in6), "NIS servers address: %s",
+                               ip6_to_str(&in6));
+               }
+               break;
+       case OPTION_NISP_SERVERS:
+               if (optlen % 16) {
+                       proto_tree_add_text(subtree, tvb, off, optlen,
+                               "NISP servers address: malformed option");
+                       break;
+               }
+               for (i = 0; i < optlen; i += 16) {
+                       tvb_memcpy(tvb, (guint8 *)&in6, off + i, sizeof(in6));
+                       proto_tree_add_text(subtree, tvb, off + i,
+                               sizeof(in6), "NISP servers address: %s",
+                               ip6_to_str(&in6));
+               }
+               break;
+       case OPTION_NIS_DOMAIN_NAME:
+         if (optlen > 0) {
+           proto_tree_add_text(subtree, tvb, off, optlen, "nis-domain-name");
+         }
+         break;
+       case OPTION_NISP_DOMAIN_NAME:
+         if (optlen > 0) {
+           proto_tree_add_text(subtree, tvb, off, optlen, "nisp-domain-name");
+         }
+         break;
+       case OPTION_NTP_SERVERS:
+               if (optlen % 16) {
+                       proto_tree_add_text(subtree, tvb, off, optlen,
+                               "NTP servers address: malformed option");
+                       break;
+               }
+               for (i = 0; i < optlen; i += 16) {
+                       tvb_memcpy(tvb, (guint8 *)&in6, off + i, sizeof(in6));
+                       proto_tree_add_text(subtree, tvb, off + i,
+                               sizeof(in6), "NTP servers address: %s",
+                               ip6_to_str(&in6));
+               }
+               break;
+       case OPTION_TIME_ZONE:
+         if (optlen > 0) {
+           proto_tree_add_text(subtree, tvb, off, optlen, "time-zone");
+         }
+         break;
+       case OPTION_IAPREFIX:
            {
-               guint32 lease_duration;
+               guint32 preferred_lifetime, valid_lifetime;
                guint8  prefix_length;
                struct e_in6_addr in6;
-               
-               lease_duration = tvb_get_ntohl(tvb, off);
-               prefix_length  = tvb_get_guint8(tvb, off + 4);
-               if ( lease_duration == DHCPV6_LEASEDURATION_INFINITY) {
-                       proto_tree_add_text(subtree, tvb, off, 4,       
-                                   "Lease duration: infinity");
+
+                if (optlen < 25) {
+                   proto_tree_add_text(subtree, tvb, off,
+                                       optlen, "IAPREFIX: malformed option");
+                   break;
+                }
+
+               preferred_lifetime = tvb_get_ntohl(tvb, off);
+               valid_lifetime = tvb_get_ntohl(tvb, off + 4);
+               prefix_length  = tvb_get_guint8(tvb, off + 8);
+               if (preferred_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
+                       proto_tree_add_text(subtree, tvb, off, 4,
+                                   "Preferred lifetime: infinity");
                } else {
                        proto_tree_add_text(subtree, tvb, off, 4,
-                                   "Lease duration: %u", lease_duration);
+                                   "Preferred lifetime: %u", preferred_lifetime);
                }
-               proto_tree_add_text(subtree, tvb, off + 4, 1,
+               if (valid_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
+                       proto_tree_add_text(subtree, tvb, off + 4, 4,
+                                   "Valid lifetime: infinity");
+               } else {
+                       proto_tree_add_text(subtree, tvb, off + 4, 4,
+                                   "Valid lifetime: %u", valid_lifetime);
+               }
+               proto_tree_add_text(subtree, tvb, off + 8, 1,
                                    "Prefix length: %d", prefix_length);
-               tvb_memcpy(tvb, (guint8 *)&in6, off + 5 , sizeof(in6));
-               proto_tree_add_text(subtree, tvb, off + 5,
+               tvb_memcpy(tvb, (guint8 *)&in6, off + 9 , sizeof(in6));
+               proto_tree_add_text(subtree, tvb, off + 9,
                                    16, "Prefix address: %s",
                                    ip6_to_str(&in6));
-           }
-           break;
-       case OPTION_PREFIXREQ:
-           {
-               guint8  prefix_length;
-               prefix_length  = tvb_get_guint8(tvb, off);
-               proto_tree_add_text(subtree, tvb, off, 1,
-                                   "Prefix length: %d", prefix_length);
+                
+                temp_optlen = 25;
+                while ((optlen - temp_optlen) > 0) {
+                   gboolean at_end_;
+                   temp_optlen += dhcpv6_option(tvb, subtree, off+temp_optlen, off + optlen, &at_end_);
+                }
            }
            break;
        }
 
-
        return 4 + optlen;
 }
 
@@ -543,37 +652,89 @@ dissect_dhcpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
 {
        proto_tree *bp_tree = NULL;
        proto_item *ti;
-       guint8 msgtype;
+       guint8 msgtype, hop_count ;
        guint32 xid;
-       int off, eoff;
+       int off = 0;
+        int eoff;
+       struct e_in6_addr in6;
        gboolean at_end;
+        gboolean relay_msg_option = FALSE;
+        int length;
 
+       eoff = tvb_reported_length(tvb);
        downstream = 0; /* feature reserved */
        if (check_col(pinfo->cinfo, COL_PROTOCOL))
                col_set_str(pinfo->cinfo, COL_PROTOCOL, "DHCPv6");
        if (check_col(pinfo->cinfo, COL_INFO))
                col_clear(pinfo->cinfo, COL_INFO);
 
-       msgtype = tvb_get_guint8(tvb, 0);
-
-       /* XXX relay agent messages have to be decoded differently */
-
-       xid = tvb_get_ntohl(tvb, 0) & 0x00ffffff;
-
-       if (check_col(pinfo->cinfo, COL_INFO)) {
-               col_set_str(pinfo->cinfo, COL_INFO,
-                           val_to_str(msgtype,
-                                      msgtype_vals,
-                                      "Message Type %u"));
-       }
+       msgtype = tvb_get_guint8(tvb, off);
 
        if (tree) {
                ti = proto_tree_add_item(tree, proto_dhcpv6, tvb, 0, -1, FALSE);
                bp_tree = proto_item_add_subtree(ti, ett_dhcpv6);
+        }
+
+        while (msgtype == RELAY_FORW || msgtype == RELAY_REPLY) {
+           
+           if (check_col(pinfo->cinfo, COL_INFO)) {
+              col_set_str(pinfo->cinfo, COL_INFO,
+                          val_to_str(msgtype,
+                                     msgtype_vals,
+                                     "Message Type %u"));
+           }
+
+           proto_tree_add_uint(bp_tree, hf_dhcpv6_msgtype, tvb, off, 1, msgtype);
+
+           hop_count = tvb_get_guint8(tvb, off+1);
+           proto_tree_add_text(bp_tree, tvb, off+1, 1, "Hop count: %d", hop_count);
+
+           tvb_memcpy(tvb, (guint8 *)&in6, off+2, sizeof(in6));
+           proto_tree_add_text(bp_tree, tvb, off+2, sizeof(in6), 
+                               "Link-address: %s",ip6_to_str(&in6));
+
+           tvb_memcpy(tvb, (guint8 *)&in6, off+18, sizeof(in6));
+           proto_tree_add_text(bp_tree, tvb, off+18, sizeof(in6), 
+                               "Peer-address: %s",ip6_to_str(&in6));
+
+           off += 34;
+           relay_msg_option = FALSE;
+
+           while (!relay_msg_option && off < eoff) {
+              length = dhcpv6_option(tvb, bp_tree, off, eoff, &at_end);
+
+              if (tvb_get_ntohs(tvb, off) == OPTION_RELAY_MSG) {
+                 relay_msg_option = TRUE;
+                 off += 4;
+              }
+              else {
+                 if (length > 0)
+                    off += length;
+                 else {
+                    proto_tree_add_text(bp_tree, tvb, off, eoff, "Message: malformed");
+                    return;
+                 }
+              }
+           }
+           
+           msgtype = tvb_get_guint8(tvb, off);
+        }
+        
+       xid = tvb_get_ntohl(tvb, off) & 0x00ffffff;
+
+        if (!off) {
+           if (check_col(pinfo->cinfo, COL_INFO)) {
+              col_set_str(pinfo->cinfo, COL_INFO,
+                          val_to_str(msgtype,
+                                     msgtype_vals,
+                                     "Message Type %u"));
+           }
+        }
 
-               proto_tree_add_uint(bp_tree, hf_dhcpv6_msgtype, tvb, 0, 1,
+       if (tree) {
+               proto_tree_add_uint(bp_tree, hf_dhcpv6_msgtype, tvb, off, 1,
                        msgtype);
-               proto_tree_add_text(bp_tree, tvb, 1, 3, "Transaction-ID: 0x%08x", xid);
+               proto_tree_add_text(bp_tree, tvb, off+1, 3, "Transaction-ID: 0x%08x", xid);
 #if 0
                tvb_memcpy(tvb, (guint8 *)&in6, 4, sizeof(in6));
                proto_tree_add_text(bp_tree, tvb, 4, sizeof(in6),
@@ -581,8 +742,7 @@ dissect_dhcpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
 #endif
        }
 
-       off = 4;
-       eoff = tvb_reported_length(tvb);
+       off += 4;
 
        at_end = FALSE;
        while (off < eoff && !at_end)
@@ -615,7 +775,7 @@ proto_register_dhcpv6(void)
     &ett_dhcpv6,
     &ett_dhcpv6_option,
   };
-  
+
   proto_dhcpv6 = proto_register_protocol("DHCPv6", "DHCPv6", "dhcpv6");
   proto_register_field_array(proto_dhcpv6, hf, array_length(hf));
   proto_register_subtree_array(ett, array_length(ett));