#include <string.h> and/or #include <stdio.h> not needed.
[obnox/wireshark/wip.git] / epan / dissectors / packet-dhcpv6.c
1 /* packet-dhpcv6.c
2  * Routines for DHCPv6 packet disassembly
3  * Copyright 2004, Nicolas DICHTEL - 6WIND - <nicolas.dichtel@6wind.com>
4  * Jun-ichiro itojun Hagino <itojun@iijlab.net>
5  * IItom Tsutomu MIENO <iitom@utouto.com>
6  * SHIRASAKI Yasuhiro <yasuhiro@gnome.gr.jp>
7  * Tony Lindstrom <tony.lindstrom@ericsson.com>
8  *
9  * $Id$
10  *
11  * The information used comes from:
12  * RFC3315.txt (DHCPv6)
13  * RFC3319.txt (SIP options)
14  * RFC3633.txt (Prefix options)
15  * RFC3646.txt (DNS servers/domains)
16  * RFC3898.txt (NIS options)
17  * RFC4704.txt (Client FQDN)
18  * RFC5007.txt (DHCPv6 Leasequery)
19  * RFC5417.txt (CAPWAP Access Controller DHCP Option)
20  * draft-ietf-dhc-dhcpv6-opt-timeconfig-03.txt
21  * draft-ietf-dhc-dhcpv6-opt-lifetime-00.txt
22  *
23  * Note that protocol constants are still subject to change, based on IANA
24  * assignment decisions.
25  *
26  * Wireshark - Network traffic analyzer
27  * By Gerald Combs <gerald@wireshark.org>
28  * Copyright 1998 Gerald Combs
29  *
30  * This program is free software; you can redistribute it and/or
31  * modify it under the terms of the GNU General Public License
32  * as published by the Free Software Foundation; either version 2
33  * of the License, or (at your option) any later version.
34  *
35  * This program is distributed in the hope that it will be useful,
36  * but WITHOUT ANY WARRANTY; without even the implied warranty of
37  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
38  * GNU General Public License for more details.
39  *
40  * You should have received a copy of the GNU General Public License
41  * along with this program; if not, write to the Free Software
42  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
43  */
44
45 #ifdef HAVE_CONFIG_H
46 # include "config.h"
47 #endif
48
49 #include <glib.h>
50 #include <epan/packet.h>
51 #include <epan/sminmpec.h>
52 #include <epan/strutil.h>
53 #include "packet-arp.h"
54
55 static int proto_dhcpv6 = -1;
56 static int hf_dhcpv6_msgtype = -1;
57 static int hf_clientfqdn_reserved = -1;
58 static int hf_clientfqdn_n = -1;
59 static int hf_clientfqdn_o = -1;
60 static int hf_clientfqdn_s = -1;
61 static int hf_option_type = -1;
62 static int hf_option_length = -1;
63 static int hf_option_value = -1;
64 static int hf_remoteid_enterprise = -1;
65 static int hf_vendoropts_enterprise = -1;
66 static int hf_duiden_enterprise = -1;
67 static int hf_vendorclass_enterprise = -1;
68 static int hf_dhcpv6_hopcount = -1;
69 static int hf_dhcpv6_xid = -1;
70 static int hf_dhcpv6_peeraddr = -1;
71 static int hf_dhcpv6_linkaddr = -1;
72
73 static gint ett_dhcpv6 = -1;
74 static gint ett_dhcpv6_option = -1;
75 static gint ett_dhcpv6_option_vsoption = -1;
76 static gint ett_dhcpv6_vendor_option = -1;
77 static gint ett_dhcpv6_pkt_option = -1;
78
79 #define UDP_PORT_DHCPV6_DOWNSTREAM      546
80 #define UDP_PORT_DHCPV6_UPSTREAM        547
81
82 #define DHCPV6_LEASEDURATION_INFINITY   0xffffffff
83
84 #define SOLICIT                 1
85 #define ADVERTISE               2
86 #define REQUEST                 3
87 #define CONFIRM                 4
88 #define RENEW                   5
89 #define REBIND                  6
90 #define REPLY                   7
91 #define RELEASE                 8
92 #define DECLINE                 9
93 #define RECONFIGURE             10
94 #define INFORMATION_REQUEST     11
95 #define RELAY_FORW              12
96 #define RELAY_REPLY             13
97 #define LEASEQUERY              14
98 #define LEASEQUERY_REPLY        15
99
100 #define OPTION_CLIENTID         1
101 #define OPTION_SERVERID         2
102 #define OPTION_IA_NA            3
103 #define OPTION_IA_TA            4
104 #define OPTION_IAADDR           5
105 #define OPTION_ORO              6
106 #define OPTION_PREFERENCE       7
107 #define OPTION_ELAPSED_TIME     8
108 #define OPTION_RELAY_MSG        9
109 /* #define      OPTION_SERVER_MSG       10 */
110 #define OPTION_AUTH             11
111 #define OPTION_UNICAST          12
112 #define OPTION_STATUS_CODE      13
113 #define OPTION_RAPID_COMMIT     14
114 #define OPTION_USER_CLASS       15
115 #define OPTION_VENDOR_CLASS     16
116 #define OPTION_VENDOR_OPTS      17
117 #define OPTION_INTERFACE_ID     18
118 #define OPTION_RECONF_MSG       19
119 #define OPTION_RECONF_ACCEPT    20
120 #define OPTION_SIP_SERVER_D     21
121 #define OPTION_SIP_SERVER_A     22
122 #define OPTION_DNS_SERVERS      23
123 #define OPTION_DOMAIN_LIST      24
124 #define OPTION_IA_PD            25
125 #define OPTION_IAPREFIX         26
126 #define OPTION_NIS_SERVERS      27
127 #define OPTION_NISP_SERVERS     28
128 #define OPTION_NIS_DOMAIN_NAME  29
129 #define OPTION_NISP_DOMAIN_NAME 30
130 #define OPTION_SNTP_SERVERS     31
131 #define OPTION_LIFETIME         32
132 #define OPTION_BCMCS_SERVER_D   33
133 #define OPTION_BCMCS_SERVER_A   34
134 #define OPTION_GEOCONF_CIVIC    36
135 #define OPTION_REMOTE_ID        37
136 #define OPTION_SUBSCRIBER_ID    38
137 #define OPTION_CLIENT_FQDN      39
138 #define OPTION_PANA_AGENT       40
139 #define OPTION_TIME_ZONE        41
140 #define OPTION_TZDB             42
141 #define OPTION_ERO              43
142 #define OPTION_LQ_QUERY         44
143 #define OPTION_CLIENT_DATA      45
144 #define OPTION_CLT_TIME         46
145 #define OPTION_LQ_RELAY_DATA    47
146 #define OPTION_LQ_CLIENT_LINK   48
147 #define OPTION_CAPWAP_AC_V6     52
148
149 /* temporary value until defined by IETF */
150 #define OPTION_MIP6_HA          165
151 #define OPTION_MIP6_HOA         166
152 #define OPTION_NAI              167
153
154 #define DUID_LLT                1
155 #define DUID_EN                 2
156 #define DUID_LL                 3
157 #define DUID_LL_OLD             4
158
159 static const value_string msgtype_vals[] = {
160         { SOLICIT,      "Solicit" },
161         { ADVERTISE,    "Advertise" },
162         { REQUEST,      "Request" },
163         { CONFIRM,      "Confirm" },
164         { RENEW,        "Renew" },
165         { REBIND,       "Rebind" },
166         { REPLY,        "Reply" },
167         { RELEASE,      "Release" },
168         { DECLINE,      "Decline" },
169         { RECONFIGURE,  "Reconfigure" },
170         { INFORMATION_REQUEST,  "Information-request" },
171         { RELAY_FORW,   "Relay-forw" },
172         { RELAY_REPLY,  "Relay-reply" },
173         { LEASEQUERY,   "Leasequery" },
174         { LEASEQUERY_REPLY,     "Leasequery-reply" },
175         { 0, NULL }
176 };
177
178 static const value_string opttype_vals[] = {
179         { OPTION_CLIENTID,      "Client Identifier" },
180         { OPTION_SERVERID,      "Server Identifier" },
181         { OPTION_IA_NA,         "Identity Association for Non-temporary Address" },
182         { OPTION_IA_TA,         "Identity Association for Temporary Address" },
183         { OPTION_IAADDR,        "IA Address" },
184         { OPTION_ORO,           "Option Request" },
185         { OPTION_PREFERENCE,    "Preference" },
186         { OPTION_ELAPSED_TIME,  "Elapsed time" },
187         { OPTION_RELAY_MSG,     "Relay Message" },
188 /*      { OPTION_SERVER_MSG,    "Server message" }, */
189         { OPTION_AUTH,          "Authentication" },
190         { OPTION_UNICAST,       "Server unicast" },
191         { OPTION_STATUS_CODE,   "Status code" },
192         { OPTION_RAPID_COMMIT,  "Rapid Commit" },
193         { OPTION_USER_CLASS,    "User Class" },
194         { OPTION_VENDOR_CLASS,  "Vendor Class" },
195         { OPTION_VENDOR_OPTS,   "Vendor-specific Information" },
196         { OPTION_INTERFACE_ID,  "Interface-Id" },
197         { OPTION_RECONF_MSG,    "Reconfigure Message" },
198         { OPTION_RECONF_ACCEPT, "Reconfigure Accept" },
199         { OPTION_SIP_SERVER_D,  "SIP Server Domain Name List" },
200         { OPTION_SIP_SERVER_A,  "SIP Servers IPv6 Address List" },
201         { OPTION_DNS_SERVERS,   "DNS recursive name server" },
202         { OPTION_DOMAIN_LIST,   "Domain Search List" },
203         { OPTION_IA_PD,         "Identity Association for Prefix Delegation" },
204         { OPTION_IAPREFIX,      "IA Prefix" },
205         { OPTION_NIS_SERVERS,   "Network Information Server" },
206         { OPTION_NISP_SERVERS,  "Network Information Server V2" },
207         { OPTION_NIS_DOMAIN_NAME, "Network Information Server Domain Name" },
208         { OPTION_NISP_DOMAIN_NAME,"Network Information Server V2 Domain Name" },
209         { OPTION_SNTP_SERVERS,  "Simple Network Time Protocol Server" },
210         { OPTION_LIFETIME,      "Lifetime" },
211         { OPTION_BCMCS_SERVER_D, "BCMCS Server Domain" },
212         { OPTION_BCMCS_SERVER_A, "BCMCS Servers IPv6 Address List" },
213         { OPTION_GEOCONF_CIVIC, "Geoconf Civic Address" },
214         { OPTION_REMOTE_ID,     "Remote Identifier" },
215         { OPTION_SUBSCRIBER_ID, "Subscriber Identifier" },
216         { OPTION_CLIENT_FQDN,   "Fully Qualified Domain Name" },
217         { OPTION_PANA_AGENT,    "PANA Agents IPv6 Address List" },
218         { OPTION_TIME_ZONE,     "Time Zone" },
219         { OPTION_TZDB,          "Time Zone Database" },
220         { OPTION_ERO,           "Echo Request Option" },
221         { OPTION_LQ_QUERY,      "Leasequery Query" },
222         { OPTION_CLIENT_DATA,   "Leasequery Client Data" },
223         { OPTION_CLT_TIME,      "Client Last Transaction Time" },
224         { OPTION_LQ_RELAY_DATA, "Leasequery Relay Data" },
225         { OPTION_LQ_CLIENT_LINK, "Leasequery Client Link Address List" },
226         { OPTION_CAPWAP_AC_V6,  "CAPWAP Access Controllers" },
227         { OPTION_MIP6_HA,       "Mobile IPv6 Home Agent" },
228         { OPTION_MIP6_HOA,      "Mobile IPv6 Home Address" },
229         { OPTION_NAI,           "Network Access Identifier" },
230         { 0,    NULL }
231 };
232
233 static const value_string statuscode_vals[] =
234 {
235         {0, "Success" },
236         {1, "UnspecFail" },
237         {2, "NoAddrAvail" },
238         {3, "NoBinding" },
239         {4, "NotOnLink" },
240         {5, "UseMulticast" },
241         {6, "NoPrefixAvail" },
242         {7, "UnknownQueryType" },
243         {8, "MalformedQuery" },
244         {9, "NotConfigured" },
245         {10, "NotAllowed" },
246         {0, NULL }
247 };
248
249 static const value_string duidtype_vals[] =
250 {
251         { DUID_LLT,     "link-layer address plus time" },
252         { DUID_EN,      "assigned by vendor based on Enterprise number" },
253         { DUID_LL,      "link-layer address" },
254         { DUID_LL_OLD,  "link-layer address (old)" },
255         { 0, NULL }
256 };
257
258 /* This FQDN draft is a mess, I've tried to understand, 
259    but N,O,S bit descriptions are really cryptic */
260 static const true_false_string fqdn_n = {
261 /*    "Client doesn't want server to perform DNS update", "" */
262     "N bit set","N bit cleared"
263 };
264
265 static const true_false_string fqdn_o = {
266     "O bit set", "O bit cleared" 
267 };
268
269 static const true_false_string fqdn_s = {
270 /*    "Forward mapping (FQDN-to-IPv6, AAAA) performed by client", 
271       "Forward mapping (FQDN-to-IPv6, AAAA) performed by server" */
272     "S bit set", "S bit cleared"
273 }; 
274
275 /* CableLabs Common Vendor Specific Options */
276 #define CL_OPTION_ORO 0x0001  /* 1 */
277 #define CL_OPTION_DEVICE_TYPE 0x0002 /* 2 */
278 #define CL_OPTION_EMBEDDED_COMPONENT_LIST 0x0003 /* 3 */
279 #define CL_OPTION_DEVICE_SERIAL_NUMBER 0x0004 /* 4 */
280 #define CL_OPTION_HARDWARE_VERSION_NUMBER 0x0005 /* 5 */
281 #define CL_OPTION_SOFTWARE_VERSION_NUMBER 0x0006 /* 6 */
282 #define CL_OPTION_BOOT_ROM_VERSION 0x0007 /* 7 */
283 #define CL_OPTION_VENDOR_OUI 0x0008 /* 8 */
284 #define CL_OPTION_MODEL_NUMBER 0x0009 /* 9 */
285 #define CL_OPTION_VENDOR_NAME 0x000a /* 10 */
286 /* 11-32 is currently reserved */
287 #define CL_OPTION_TFTP_SERVERS 0x0020 /* 32 */
288 #define CL_OPTION_CONFIG_FILE_NAME 0x0021 /* 33 */
289 #define CL_OPTION_SYSLOG_SERVERS 0x0022 /* 34 */
290 #define CL_OPTION_TLV5 0x0023 /* 35 */
291 #define CL_OPTION_DEVICE_ID 0x0024 /* 36 */
292 #define CL_OPTION_RFC868_SERVERS 0x0025 /* 37 */
293 #define CL_OPTION_TIME_OFFSET 0x0026 /* 38 */
294
295 /** CableLabs DOCSIS Project Vendor Specific Options */
296 #define CL_OPTION_DOCS_CMTS_CAP 0x0401  /* 1025 */
297 #define CL_CM_MAC_ADDR 0x0402 /* 1026 */
298 #define CL_EROUTER_CONTAINER_OPTION 0x403 /* 1027 */
299
300 /** CableLabs PacketCable Project Vendor Specific Options **/
301 #define CL_OPTION_CCC 0x087a  /* 2170 */
302
303 /** CableLabs TLVs for DOCS_CMTS_CAP Vendor Option **/
304 #define CL_OPTION_DOCS_CMTS_TLV_VERS_NUM 0x01 /* 1 */
305
306 static const value_string cl_vendor_subopt_values[] = {
307 /* 1 */ { CL_OPTION_ORO, "Option Request = " }, 
308 /* 2 */ { CL_OPTION_DEVICE_TYPE, "Device Type = " },
309 /* 3 */ { CL_OPTION_EMBEDDED_COMPONENT_LIST, "Embedded Components = " },
310 /* 4 */ { CL_OPTION_DEVICE_SERIAL_NUMBER, "Serial Number = " },
311 /* 5 */ { CL_OPTION_HARDWARE_VERSION_NUMBER, "Hardware Version = " },
312 /* 6 */ { CL_OPTION_SOFTWARE_VERSION_NUMBER, "Software Version = " },
313 /* 7 */ { CL_OPTION_BOOT_ROM_VERSION, "Boot ROM Version = " },
314 /* 8 */ { CL_OPTION_VENDOR_OUI, "Organization Unique Identifier = " },
315 /* 9 */ { CL_OPTION_MODEL_NUMBER, "Model Number = " },
316 /* 10 */ { CL_OPTION_VENDOR_NAME, "Vendor Name = " },
317 /* 32 */ { CL_OPTION_TFTP_SERVERS, "TFTP Server Addresses : " },
318 /* 33 */ { CL_OPTION_CONFIG_FILE_NAME, "Configuration File Name = " },
319 /* 34 */ { CL_OPTION_SYSLOG_SERVERS, "Syslog Servers : " },
320 /* 35 */ { CL_OPTION_TLV5, "TLV5 = " },
321 /* 36 */ { CL_OPTION_DEVICE_ID, "Device Identifier = " },
322 /* 37 */ { CL_OPTION_RFC868_SERVERS, "Time Protocol Servers : " },
323 /* 38 */ { CL_OPTION_TIME_OFFSET, "Time Offset = " },
324 /* 1025 */ {CL_OPTION_DOCS_CMTS_CAP, "CMTS Capabilities Option : " },
325 /* 1026 */ {CL_CM_MAC_ADDR, "CM MAC Address Option = " }, 
326 /* 1027 */ {CL_EROUTER_CONTAINER_OPTION, "eRouter Container Option : " },
327 /* 2170 */ {CL_OPTION_CCC, "CableLabs Client Configuration : " },
328 { 0, NULL}
329 /* 1 */ };
330
331 #define PKT_CCC_PRI_DHCP       0x0001
332 #define PKT_CCC_SEC_DHCP       0x0002
333 #define PKT_CCC_IETF_PROV_SRV  0x0003
334 #define PKT_CCC_IETF_AS_KRB    0x0004
335 #define PKT_CCC_IETF_AP_KRB    0x0005
336 #define PKT_CCC_KRB_REALM      0x0006
337 #define PKT_CCC_TGT_FLAG       0x0007
338 #define PKT_CCC_PROV_TIMER     0x0008
339 #define PKT_CCC_IETF_SEC_TKT   0x0009
340 /** 10 -255 Reservered for future extensions **/ 
341
342 static const value_string pkt_ccc_opt_vals[] = {
343         { PKT_CCC_PRI_DHCP,              "TSP's Primary DHCP Server" },
344         { PKT_CCC_SEC_DHCP,              "TSP's Secondary DHCP Server" },
345         { PKT_CCC_IETF_PROV_SRV, "TSP's Provisioning Server" },
346         { PKT_CCC_IETF_AS_KRB,   "TSP's AS-REQ/AS-REP Backoff and Retry" },
347         { PKT_CCC_IETF_AP_KRB,   "TSP's AP-REQ/AP-REP Backoff and Retry" },
348         { PKT_CCC_KRB_REALM,     "TSP's Kerberos Realm Name" },
349         { PKT_CCC_TGT_FLAG,      "TSP's Ticket Granting Server Utilization" },
350         { PKT_CCC_PROV_TIMER,    "TSP's Provisioning Timer Value" },
351         { PKT_CCC_IETF_SEC_TKT,  "PacketCable Security Ticket Control" },
352         { 0, NULL },
353 }; 
354
355 static const value_string sec_tcm_vals[] = {
356         { 1 << 0, "PacketCable Provisioning Server" },
357         { 1 << 1, "PacketCable Call Manager Servers" },
358         { 0, NULL },
359 };
360
361 /* May be called recursively */
362 static void
363 dissect_dhcpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
364     gboolean downstream, int off, int eoff);
365
366 static int
367 dissect_packetcable_ccc_option(proto_tree *v_tree, tvbuff_t *tvb, int optoff,
368     int optend)
369 {
370         /** THE ENCODING OF THIS SUBOPTION HAS CHANGED FROM DHCPv4
371       the code and length fields have grown from a single octet to
372       two octets each. **/
373   int suboptoff = optoff;
374         guint16 subopt, subopt_len, sec_tcm;
375   guint8 fetch_tgt, timer_val, type; 
376         proto_item *vti;
377   proto_tree *pkt_s_tree;
378   guint32 ipv4_address;
379   guchar kr_name;       /** A character in the kerberos realm name option */
380   guint8 kr_value;      /* The integer value of the character currently being tested */
381   int kr_fail_flag = 0; /* Flag indicating an invalid character was found */
382   int kr_pos = 0;       /* The position of the first invalid character */
383   int i = 0;
384   char bit_fld[24];
385
386         subopt = tvb_get_ntohs(tvb, optoff);
387         suboptoff += 2;
388
389         subopt_len = tvb_get_ntohs(tvb, suboptoff);
390         suboptoff += 2;
391
392         /* There must be at least five octets left to be a valid sub element */
393         if (optend <= 0) {
394                 proto_tree_add_text(v_tree, tvb, optoff, 1,
395                         "Sub element %d: no room left in option for suboption length",
396                         subopt);
397                 return (optend);
398         } 
399         /* g_print("dissect packetcable ccc option subopt_len=%d optend=%d\n\n", subopt_len, optend); */
400
401         vti = proto_tree_add_text(v_tree, tvb, optoff, subopt_len + 4,
402             "Sub element %u: %s: ", subopt,
403             val_to_str(subopt, pkt_ccc_opt_vals, "unknown/reserved") );
404
405         switch (subopt) {
406                 case PKT_CCC_PRI_DHCP:  /* IPv4 address values */
407                 case PKT_CCC_SEC_DHCP:
408       if (subopt_len == 4) {
409         ipv4_address = tvb_get_ipv4(tvb, suboptoff);
410         proto_item_append_text(vti, "%s (%u byte%s%s)",
411                ip_to_str((guint8 *)&ipv4_address), subopt_len,
412                plurality(subopt_len, "", "s"),
413                subopt_len != 4 ? " [Invalid]" : "");
414       }
415       else {
416           proto_tree_add_text(vti, tvb, suboptoff, subopt_len, 
417               "Bogus length: %d", subopt_len);
418
419       }
420
421       suboptoff += subopt_len;
422       break;
423     case PKT_CCC_IETF_PROV_SRV :
424       type = tvb_get_guint8(tvb, suboptoff);
425       /** Type 0 is FQDN **/
426       if (type == 0) {
427                            proto_item_append_text(vti, "%s (%u byte%s)",
428                                         tvb_format_stringzpad(tvb, suboptoff+1, subopt_len-1),
429                                            subopt_len,
430                                            plurality(subopt_len-1, "", "s") ); 
431       }
432       /** Type 0 is IPv4 **/
433       else if (type == 1) {
434          if (subopt_len == 5) {
435             ipv4_address = tvb_get_ipv4(tvb, suboptoff+1);
436             proto_item_append_text(vti, "%s (%u byte%s%s)",
437                    ip_to_str((guint8 *)&ipv4_address), subopt_len,
438                    plurality(subopt_len, "", "s"),
439                    subopt_len != 5 ? " [Invalid]" : "");
440          }
441          else {
442              proto_item_append_text(vti, "Bogus length: %d", subopt_len);
443          }
444       }
445       else {
446           proto_item_append_text(vti, "Invalid type: %u (%u byte%s)",
447                 type, subopt_len, plurality(subopt_len, "", "s")); 
448       }
449       suboptoff += subopt_len;
450       break;
451        
452     case PKT_CCC_IETF_AS_KRB :
453     case PKT_CCC_IETF_AP_KRB :
454       if (subopt_len == 12) {
455          pkt_s_tree = proto_item_add_subtree(vti, ett_dhcpv6_pkt_option);
456          proto_tree_add_text(pkt_s_tree, tvb, suboptoff, 4, 
457             "Nominal Timeout : %u", tvb_get_ntohl(tvb, suboptoff));
458          proto_tree_add_text(pkt_s_tree, tvb, suboptoff+4, 4, 
459             "Maximum Timeout : %u", tvb_get_ntohl(tvb, suboptoff+4));
460          proto_tree_add_text(pkt_s_tree, tvb, suboptoff+8, 4, 
461             "Maximum Retry Count : %u", tvb_get_ntohl(tvb, suboptoff+8));
462
463       }
464       else {
465           proto_item_append_text(vti, "Bogus length: %d", subopt_len);
466       }
467       suboptoff += subopt_len;
468       break;
469                 case PKT_CCC_KRB_REALM:
470                         if (subopt_len > 0) {
471          /** The only allowable characters are 
472              A-Z (upper case only) 65-90
473              '.', 46
474              '/', 47 
475              '\', 92 
476              '=', 61 
477              '"', 34 
478              ',', 44 
479            and 
480              ':' 58
481              so loop through and 
482              make sure it conforms to the expected syntax.
483           **/
484          for (i=0; i < subopt_len; i++) {
485             kr_name = tvb_get_guint8(tvb, suboptoff + i);
486             kr_value = (int)kr_name;
487             if ((kr_value >= 65 && kr_value <= 90) || 
488                   kr_value == 34 || 
489                   kr_value == 44 || 
490                   kr_value == 46 || 
491                   kr_value == 47 || 
492                   kr_value == 58 || 
493                   kr_value == 61 || 
494                   kr_value == 92)   {
495             }
496             else if (!kr_fail_flag) {
497                kr_pos = i;
498                kr_fail_flag = 1;
499             }
500             proto_item_append_text(vti, "%c", kr_name);
501          }
502      
503          if (kr_fail_flag) {
504             proto_item_append_text(vti, " (%u byte%s [Invalid at byte=%d]) ",
505                                         subopt_len,
506                                         plurality(subopt_len, "", "s"),  
507                 kr_pos);
508          }
509          else {
510             proto_item_append_text(vti, " (%u byte%s%s) ",
511                                         subopt_len,
512                                         plurality(subopt_len, "", "s"),  
513                 kr_fail_flag != 0 ? " [Invalid]" : "");
514          }
515       } 
516                         suboptoff += subopt_len;
517                         break;
518
519                 case PKT_CCC_TGT_FLAG:
520                         fetch_tgt = tvb_get_guint8(tvb, suboptoff);
521                         proto_item_append_text(vti, "%s (%u byte%s%s)",
522                                         fetch_tgt == 1 ? "True" : "False",
523                                         subopt_len,
524                                         plurality(subopt_len, "", "s"),
525                                         subopt_len != 1 ? " [Invalid]" : "");
526                         suboptoff += subopt_len;
527                         break;
528
529                 case PKT_CCC_PROV_TIMER:
530                         timer_val = tvb_get_guint8(tvb, suboptoff);
531                         /* proto_item_append_text(vti, "%u%s (%u byte%s%s)", timer_val,
532                                         timer_val > 30 ? " [Invalid]" : "", */
533                         proto_item_append_text(vti, "%u (%u byte%s%s)", timer_val,
534                                         subopt_len,
535                                         plurality(subopt_len, "", "s"),
536                                         subopt_len != 1 ? " [Invalid]" : "");
537                         suboptoff += subopt_len;
538                         break;
539     
540     case PKT_CCC_IETF_SEC_TKT :
541                         sec_tcm = tvb_get_ntohs(tvb, suboptoff);
542       proto_item_append_text(vti, "0x%04x (%u byte%s%s)",
543           sec_tcm, subopt_len, plurality(subopt_len, "", "s"),
544           subopt_len != 2 ? " [Invalid]" : "");     
545       
546       if (subopt_len == 2) {
547          pkt_s_tree = proto_item_add_subtree(vti, ett_dhcpv6_pkt_option);
548          for (i=0; i< 2; i++) {
549             if (sec_tcm & sec_tcm_vals[i].value) {
550               decode_bitfield_value(bit_fld, sec_tcm, sec_tcm_vals[i].value, 16);
551                   proto_tree_add_text(pkt_s_tree, tvb, suboptoff, 2, "%s %s",
552                   bit_fld, sec_tcm_vals[i].strptr);
553             }
554          }
555       }
556       suboptoff += subopt_len;
557                         break;
558
559
560                 default:
561                         suboptoff += subopt_len;
562                         break;
563
564         }
565         
566   /** Return the number of bytes processed **/
567   return (suboptoff - optoff);
568 }
569
570 static void 
571 dissect_cablelabs_specific_opts(proto_tree *v_tree, tvbuff_t *tvb, int voff, int len)
572 {
573     guint16 type;
574     guint16 tlv_len; /* holds the number of elements in the tlv */
575     guint16 opt_len; /* holds the length of the suboption */
576     guint16 sub_value;
577     int off = voff;
578     int sub_off; /** The offset for the sub-option */
579     proto_item *ti;
580     int i;
581     int field_len; /* holds the lenght of one occurrence of a field */
582     proto_tree *subtree;
583     struct e_in6_addr in6; 
584
585     if (len > 4) {
586       while (off - voff < len) {
587
588                     /* Type */
589         type = tvb_get_ntohs(tvb, off);
590         ti = proto_tree_add_text(v_tree, tvb, off, 2,
591                 "Suboption %d: %s", type, val_to_str(type, 
592                 cl_vendor_subopt_values, "unknown"));
593         /* Length */
594         tlv_len = tvb_get_ntohs(tvb, off+2);
595
596         /* Values */
597         sub_off = off + 4;
598
599         switch(type) {
600             /* String types */
601             case CL_OPTION_DEVICE_TYPE :
602             case CL_OPTION_DEVICE_SERIAL_NUMBER :
603             case CL_OPTION_HARDWARE_VERSION_NUMBER :
604             case CL_OPTION_SOFTWARE_VERSION_NUMBER :
605             case CL_OPTION_BOOT_ROM_VERSION :
606             case CL_OPTION_MODEL_NUMBER :
607             case CL_OPTION_VENDOR_NAME :
608             case CL_OPTION_CONFIG_FILE_NAME :
609             case CL_OPTION_EMBEDDED_COMPONENT_LIST :
610                 opt_len = tlv_len;
611                 field_len = tlv_len;
612                 proto_item_append_text(ti, "\"%s\"", 
613                         tvb_format_stringzpad(tvb, sub_off, field_len));
614              break;
615             case CL_OPTION_ORO : 
616                 field_len = 2;
617                 opt_len = tlv_len;
618                 if (opt_len > 0) {
619                   for (i = 0; i < tlv_len; i += field_len) {
620                     sub_value = tvb_get_ntohs(tvb, sub_off);
621                     proto_item_append_text(ti, " %d", sub_value);
622                    sub_off += field_len;
623                 }
624                 }
625                 break;
626
627             /* List of IPv6 Address */
628             case CL_OPTION_TFTP_SERVERS :
629             case CL_OPTION_SYSLOG_SERVERS :
630             case CL_OPTION_RFC868_SERVERS :
631                 field_len = 16;
632                 opt_len = tlv_len;
633                 subtree = proto_item_add_subtree(ti, ett_dhcpv6_vendor_option);
634
635                 if ((tlv_len % field_len) == 0) {
636                    for (i = 0; i < tlv_len/field_len; i++) {
637                       tvb_get_ipv6(tvb, sub_off, &in6);
638                       proto_tree_add_text(subtree, tvb, sub_off,
639                                   sizeof(in6), "IPv6 address %d: %s",
640                                   i+1, ip6_to_str(&in6));
641                       sub_off += field_len;
642                    }
643                 }
644                 break;
645
646             case CL_OPTION_VENDOR_OUI :
647             case CL_OPTION_DEVICE_ID :
648                 opt_len = tlv_len;
649                 field_len = tlv_len;
650                 if (tlv_len != 6) {
651                    proto_item_append_text(ti, "Bogus value length=%d", 
652                         tlv_len);
653                 }
654                 else {
655                    proto_item_append_text(ti, "%s", 
656                         tvb_bytes_to_str(tvb, sub_off, field_len));
657                 }
658                 break;
659
660             case CL_OPTION_TLV5 :
661                 opt_len = tlv_len;
662                 field_len = tlv_len;
663                 proto_item_append_text(ti, "%s", 
664                         tvb_bytes_to_str(tvb, sub_off, field_len));
665                 break;
666
667             case CL_OPTION_TIME_OFFSET :
668                 opt_len = tlv_len;
669                 proto_item_append_text(ti, "%d", tvb_get_ntohl(tvb, sub_off));
670                 break;
671
672             case CL_OPTION_DOCS_CMTS_CAP :
673                 opt_len = tlv_len;
674                 field_len = 0;
675                 subtree = proto_item_add_subtree(ti, ett_dhcpv6_vendor_option);
676
677                 /* tlv_len contains the total length of all the TLVs for this
678                    option */
679                 if (tlv_len > 0) {
680                     for (i = 0; field_len < opt_len; i++) {
681                        int tagLen = 0;
682                        int tag = 0;
683                        tag = tvb_get_guint8(tvb, sub_off);
684                        sub_off++;
685                        tagLen = tvb_get_guint8(tvb, sub_off);
686                        sub_off++;
687                        if (tag == CL_OPTION_DOCS_CMTS_TLV_VERS_NUM &&
688                            tagLen == 2) {
689                           int major = 0;
690                           int minor = 0;
691                           major = tvb_get_guint8(tvb, sub_off);
692                           sub_off++;
693                           minor = tvb_get_guint8(tvb, sub_off);
694                           sub_off++;
695                           proto_tree_add_text(subtree, tvb, sub_off,
696                                   sizeof(4), "DOCSIS Version Number %d.%d",
697                                   major, minor);
698                        }
699                        else
700                           sub_off += tagLen;
701                        
702                        field_len += tagLen + 2;
703                    }
704                 }
705                 else 
706                    proto_tree_add_text(subtree, tvb, sub_off,
707                         sizeof(0), "empty");
708                 break;
709             
710             case CL_CM_MAC_ADDR :
711                 opt_len = tlv_len;
712                 field_len = tlv_len;
713                 if (tlv_len != 6) {
714                     proto_item_append_text(ti, "Bogus value length=%d", 
715                          tlv_len);
716                  }
717                  else {
718                     /*proto_item_append_text(ti, "CM MAC Address Option = %s", */
719                     proto_item_append_text(ti, "%s", 
720                          bytes_to_str_punct(tvb_get_ptr(tvb, sub_off, opt_len), opt_len, ':'));
721                          /* tvb_bytes_to_str(tvb, sub_off, opt_len)); */
722                  }
723                  sub_off += field_len;
724                  break;
725             
726             case CL_EROUTER_CONTAINER_OPTION :
727                  opt_len = tlv_len;
728                  field_len = tlv_len;
729                  proto_item_append_text(ti, " %s (len=%d)",
730                          tvb_bytes_to_str(tvb, sub_off, opt_len), tlv_len);
731                  sub_off += field_len;
732                  break;
733             
734             case CL_OPTION_CCC :
735                  opt_len = tlv_len;
736                  field_len = 0;
737                  subtree = proto_item_add_subtree(ti, ett_dhcpv6_vendor_option);
738                  proto_item_append_text(ti, " (%d bytes)", opt_len);
739                  while (field_len < opt_len) {
740                      sub_value = dissect_packetcable_ccc_option(subtree, tvb, 
741                          sub_off, (opt_len - field_len));
742                      sub_off += sub_value;
743                      field_len += sub_value;
744                  }
745                  sub_off += field_len;
746                  
747             default:
748                 opt_len = tlv_len;
749             break;
750         }
751         off += (opt_len + 4);
752
753       }
754     }
755     else {
756       proto_tree_add_text(v_tree, tvb, off, len-off, 
757               "Bogus length: %d", len);
758     }
759 }
760
761 /* Adds domain */
762 static void
763 dhcpv6_domain(proto_tree * subtree, tvbuff_t *tvb, int offset, guint16 optlen)
764 {
765     int start_offset=offset;
766     char domain[256];
767     int pos;
768     guint8 len;
769
770     pos=0;
771     while(optlen){
772         /* this is the start of the domain name */
773         if(!pos){
774             start_offset=offset;
775         }
776         domain[pos]=0;
777
778         /* read length of the next substring */
779         len = tvb_get_guint8(tvb, offset);
780         /* Microsoft dhcpv6 clients aren't currently RFC 4704 conform: They send an
781          * ASCII string instead of a DNS record encoded domain name. Catch that case
782          * to allow us to continue after such a malformed record.
783          */
784         if ( optlen < len ) {
785                 proto_tree_add_text(subtree, tvb, start_offset, optlen, "Malformed DNS name record (MS Vista client?)");
786                 return;
787         }
788         offset++;
789         optlen--;
790         /* if len==0 and pos>0 we have read an entire domain string */
791         if(!len){
792             if(!pos){
793                 /* empty string, this must be an error? */
794                 proto_tree_add_text(subtree, tvb, start_offset, offset-start_offset, "Malformed option");
795                 return;
796             } else {
797                 proto_tree_add_text(subtree, tvb, start_offset, offset-start_offset, "Domain: %s", domain);
798                 pos=0;
799                 continue;
800             }
801         }
802
803         /* add the substring to domain */
804         if(pos){
805             domain[pos]='.';
806             pos++;
807         }
808         if(pos+len>254){
809                 /* too long string, this must be an error? */
810                 proto_tree_add_text(subtree, tvb, start_offset, offset-start_offset, "Malformed option");
811                 return;
812         }
813         tvb_memcpy(tvb, domain+pos, offset, len);
814         pos+=len;
815         offset+=len;
816         optlen-=len;
817     }        
818     
819     if(pos){
820         domain[pos]=0;
821         proto_tree_add_text(subtree, tvb, start_offset, offset-start_offset, "Domain: %s", domain);
822     }
823 }    
824
825 /* Returns the number of bytes consumed by this option. */
826 static int
827 dhcpv6_option(tvbuff_t *tvb, packet_info *pinfo, proto_tree *bp_tree, 
828                 gboolean downstream, int off, int eoff, gboolean *at_end)
829 {
830         guint8 *buf;
831         guint16 opttype;
832         guint16 optlen;
833         guint16 hwtype;
834         guint16 temp_optlen = 0;
835         proto_item *ti;
836         proto_tree *subtree;
837         proto_tree *subtree_2;
838         int i;
839         struct e_in6_addr in6;
840         guint16 duidtype;
841         guint32 enterprise_no;
842
843         /* option type and length must be present */
844         if (eoff - off < 4) {
845                 *at_end = TRUE;
846                 return 0;
847         }
848
849         opttype = tvb_get_ntohs(tvb, off);
850         optlen = tvb_get_ntohs(tvb, off + 2);
851
852         /* all option data must be present */
853         if (eoff - off < 4 + optlen) {
854                 *at_end = TRUE;
855                 return 0;
856         }
857
858         ti = proto_tree_add_text(bp_tree, tvb, off, 4 + optlen,
859                 "%s", val_to_str(opttype, opttype_vals, "DHCP option %u"));
860
861         subtree = proto_item_add_subtree(ti, ett_dhcpv6_option);
862         proto_tree_add_item(subtree, hf_option_type, tvb, off, 2, FALSE);
863         proto_tree_add_item(subtree, hf_option_length, tvb, off + 2, 2, FALSE);
864         off += 4;
865         /* Right now, none of the options can be filtered at, so provide a hex
866            array for minimalistic filtering */
867         proto_tree_add_item(subtree, hf_option_value, tvb, off, optlen, FALSE);
868
869         switch (opttype) {
870         case OPTION_CLIENTID:
871                 col_append_fstr(pinfo->cinfo, COL_INFO, "CID: %s ", tvb_bytes_to_str(tvb, off, optlen));
872                 /* Fall through */
873         case OPTION_SERVERID:
874                 if (optlen < 2) {
875                         proto_tree_add_text(subtree, tvb, off, optlen,
876                                 "DUID: malformed option");
877                         break;
878                 }
879                 proto_item_append_text(ti, ": %s", tvb_bytes_to_str(tvb, off, optlen));
880                 duidtype = tvb_get_ntohs(tvb, off);
881                 proto_tree_add_text(subtree, tvb, off, 2,
882                         "DUID type: %s (%u)",
883                                     val_to_str(duidtype,
884                                                duidtype_vals, "Unknown"),
885                                     duidtype);
886                 switch (duidtype) {
887                 case DUID_LLT:
888                         if (optlen < 8) {
889                                 proto_tree_add_text(subtree, tvb, off,
890                                         optlen, "DUID: malformed option");
891                                 break;
892                         }
893                         hwtype=tvb_get_ntohs(tvb, off + 2);
894                         proto_tree_add_text(subtree, tvb, off + 2, 2,
895                                 "Hardware type: %s (%u)", arphrdtype_to_str(hwtype, "Unknown"),
896                                 hwtype);
897                         /* XXX seconds since Jan 1 2000 */
898                         proto_tree_add_text(subtree, tvb, off + 4, 4,
899                                 "Time: %u", tvb_get_ntohl(tvb, off + 4));
900                         if (optlen > 8) {
901                                 proto_tree_add_text(subtree, tvb, off + 8,
902                                         optlen - 8, "Link-layer address: %s",
903                                         arphrdaddr_to_str(tvb_get_ptr(tvb, off+8, optlen-8), optlen-8, hwtype));
904                         }
905                         break;
906                 case DUID_EN:
907                         if (optlen < 6) {
908                                 proto_tree_add_text(subtree, tvb, off,
909                                         optlen, "DUID: malformed option");
910                                 break;
911                         }
912                         proto_tree_add_item(subtree, hf_duiden_enterprise, tvb, off + 2, 4, FALSE);
913                         if (optlen > 6) {
914                                 buf = tvb_bytes_to_str(tvb, off + 6, optlen - 6);
915                                 proto_tree_add_text(subtree, tvb, off + 6,
916                                         optlen - 6, "identifier: %s", buf);
917                         }
918                         break;
919                 case DUID_LL:
920                 case DUID_LL_OLD:
921                         if (optlen < 4) {
922                                 proto_tree_add_text(subtree, tvb, off,
923                                         optlen, "DUID: malformed option");
924                                 break;
925                         }
926                         hwtype=tvb_get_ntohs(tvb, off + 2);
927                         proto_tree_add_text(subtree, tvb, off + 2, 2,
928                                 "Hardware type: %s (%u)",
929                                 arphrdtype_to_str(hwtype, "Unknown"),
930                                 hwtype);
931                         if (optlen > 4) {
932                                 proto_tree_add_text(subtree, tvb, off + 4,
933                                         optlen - 4, "Link-layer address: %s",
934                                         arphrdaddr_to_str(tvb_get_ptr(tvb, off+4, optlen-4), optlen-4, hwtype));
935                         }
936                         break;
937                 }
938                 break;
939         case OPTION_IA_NA:
940         case OPTION_IA_PD:
941           if (optlen < 12) {
942              if (opttype == OPTION_IA_NA)
943                 proto_tree_add_text(subtree, tvb, off,
944                                     optlen, "IA_NA: malformed option");
945              else
946                 proto_tree_add_text(subtree, tvb, off,
947                                     optlen, "IA_PD: malformed option");
948              break;
949           }
950           proto_tree_add_text(subtree, tvb, off, 4,
951                               "IAID: %u",
952                               tvb_get_ntohl(tvb, off));
953           if (tvb_get_ntohl(tvb, off+4) == DHCPV6_LEASEDURATION_INFINITY) {
954               proto_tree_add_text(subtree, tvb, off+4, 4,
955                                   "T1: infinity");
956           } else {
957               proto_tree_add_text(subtree, tvb, off+4, 4,
958                                   "T1: %u", tvb_get_ntohl(tvb, off+4));
959           }
960
961           if (tvb_get_ntohl(tvb, off+8) == DHCPV6_LEASEDURATION_INFINITY) {
962               proto_tree_add_text(subtree, tvb, off+8, 4,
963                                   "T2: infinity");
964           } else {
965               proto_tree_add_text(subtree, tvb, off+8, 4,
966                                   "T2: %u", tvb_get_ntohl(tvb, off+8));
967           }
968
969           temp_optlen = 12;
970           while ((optlen - temp_optlen) > 0) {
971             temp_optlen += dhcpv6_option(tvb, pinfo, subtree, downstream,
972                             off+temp_optlen, off + optlen, at_end);
973             if (*at_end) {
974               /* Bad option - just skip to the end */
975               temp_optlen = optlen;
976             }
977           }
978           break;
979         case OPTION_IA_TA:
980           if (optlen < 4) {
981             proto_tree_add_text(subtree, tvb, off,
982                                 optlen, "IA_TA: malformed option");
983             break;
984           }
985           proto_tree_add_text(subtree, tvb, off, 4,
986                               "IAID: %u",
987                               tvb_get_ntohl(tvb, off));
988           temp_optlen = 4;
989           while ((optlen - temp_optlen) > 0) {
990             temp_optlen += dhcpv6_option(tvb, pinfo, subtree, downstream,
991                             off+temp_optlen, off + optlen, at_end);
992             if (*at_end) {
993               /* Bad option - just skip to the end */
994               temp_optlen = optlen;
995             }
996           }
997           break;
998         case OPTION_IAADDR:
999         {
1000            guint32 preferred_lifetime, valid_lifetime;
1001
1002            if (optlen < 24) {
1003               proto_tree_add_text(subtree, tvb, off,
1004                                   optlen, "IAADDR: malformed option");
1005               break;
1006            }
1007            tvb_get_ipv6(tvb, off, &in6);
1008            proto_tree_add_text(subtree, tvb, off,
1009                                sizeof(in6), "IPv6 address: %s",
1010                                ip6_to_str(&in6));
1011            col_append_fstr(pinfo->cinfo, COL_INFO, "IAA: %s ", ip6_to_str(&in6));
1012            proto_item_append_text(ti, ":  %s", ip6_to_str(&in6));
1013            
1014            preferred_lifetime = tvb_get_ntohl(tvb, off + 16);
1015            valid_lifetime = tvb_get_ntohl(tvb, off + 20);
1016            
1017            if (preferred_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
1018               proto_tree_add_text(subtree, tvb, off + 16, 4,
1019                                   "Preferred lifetime: infinity");
1020            } else {
1021               proto_tree_add_text(subtree, tvb, off + 16, 4,
1022                                   "Preferred lifetime: %u", preferred_lifetime);
1023            }
1024            if (valid_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
1025               proto_tree_add_text(subtree, tvb, off + 20, 4,
1026                                   "Valid lifetime: infinity");
1027            } else {
1028               proto_tree_add_text(subtree, tvb, off + 20, 4,
1029                                   "Valid lifetime: %u", valid_lifetime);
1030            }
1031            
1032            temp_optlen = 24;
1033            while ((optlen - temp_optlen) > 0) {
1034               temp_optlen += dhcpv6_option(tvb, pinfo, subtree, downstream,
1035                               off+temp_optlen, off + optlen, at_end);
1036               if (*at_end) {
1037                 /* Bad option - just skip to the end */
1038                 temp_optlen = optlen;
1039               }
1040            }
1041         }
1042         break;
1043         case OPTION_ORO:
1044         case OPTION_ERO:
1045                 for (i = 0; i < optlen; i += 2) {
1046                     guint16 requested_opt_code;
1047                     requested_opt_code = tvb_get_ntohs(tvb, off + i);
1048                     proto_tree_add_text(subtree, tvb, off + i,
1049                             2, "Requested Option code: %s (%d)",
1050                                             val_to_str(requested_opt_code,
1051                                                        opttype_vals,
1052                                                        "Unknown"),
1053                                             requested_opt_code);
1054                 }
1055                 break;
1056         case OPTION_PREFERENCE:
1057           if (optlen != 1) {
1058             proto_tree_add_text(subtree, tvb, off,
1059                                 optlen, "PREFERENCE: malformed option");
1060             break;
1061           }
1062           proto_tree_add_text(subtree, tvb, off, 1,
1063                               "pref-value: %d",
1064                               (guint32)tvb_get_guint8(tvb, off));
1065           break;
1066         case OPTION_ELAPSED_TIME:
1067           if (optlen != 2) {
1068             proto_tree_add_text(subtree, tvb, off,
1069                                 optlen, "ELAPSED-TIME: malformed option");
1070             break;
1071           }
1072           proto_tree_add_text(subtree, tvb, off, 2,
1073                               "elapsed-time: %u ms",
1074                               10*(guint32)tvb_get_ntohs(tvb, off));
1075           break;
1076         case OPTION_RELAY_MSG:
1077           if (optlen == 0) {
1078             proto_tree_add_text(subtree, tvb, off,
1079                                 optlen, "RELAY-MSG: malformed option");
1080           } else {
1081             /* here, we should dissect a full DHCP message */
1082             dissect_dhcpv6(tvb, pinfo, subtree, downstream, off, off + optlen);
1083           } 
1084           break;
1085         case OPTION_AUTH:
1086           if (optlen < 11) {
1087             proto_tree_add_text(subtree, tvb, off,
1088                                 optlen, "AUTH: malformed option");
1089             break;
1090           }
1091           proto_tree_add_text(subtree, tvb, off, 1,
1092                               "Protocol: %d",
1093                               (guint32)tvb_get_guint8(tvb, off));
1094           proto_tree_add_text(subtree, tvb, off+1, 1,
1095                               "Algorithm: %d",
1096                               (guint32)tvb_get_guint8(tvb, off+1));
1097           proto_tree_add_text(subtree, tvb, off+2, 1,
1098                               "RDM: %d",
1099                               (guint32)tvb_get_guint8(tvb, off+2));
1100           proto_tree_add_text(subtree, tvb, off+3, 8,
1101                               "Replay Detection");
1102           if (optlen != 11)
1103                 proto_tree_add_text(subtree, tvb, off+11, optlen-11,
1104                                                         "Authentication Information");
1105           break;
1106         case OPTION_UNICAST:
1107           if (optlen != 16) {
1108             proto_tree_add_text(subtree, tvb, off,
1109                                 optlen, "UNICAST: malformed option");
1110             break;
1111           }
1112           tvb_get_ipv6(tvb, off, &in6);
1113           proto_tree_add_text(subtree, tvb, off,
1114                               sizeof(in6), "IPv6 address: %s",
1115                                 ip6_to_str(&in6));
1116           break;
1117         case OPTION_STATUS_CODE:
1118             {
1119                 guint16 status_code;
1120                 char *status_message = 0;
1121                 status_code = tvb_get_ntohs(tvb, off);
1122                 proto_tree_add_text(subtree, tvb, off, 2,
1123                                     "Status Code: %s (%d)",
1124                                     val_to_str(status_code, statuscode_vals,
1125                                                "Unknown"),
1126                                     status_code);
1127
1128                 if (optlen - 2 > 0) {
1129                     status_message = tvb_get_ephemeral_string(tvb, off + 2, optlen - 2);
1130                     proto_tree_add_text(subtree, tvb, off + 2, optlen - 2,
1131                                         "Status Message: %s",
1132                                         status_message);
1133                 }
1134             }
1135             break;
1136         case OPTION_VENDOR_CLASS:
1137           if (optlen < 4) {
1138             proto_tree_add_text(subtree, tvb, off,
1139                                 optlen, "VENDOR_CLASS: malformed option");
1140             break;
1141           }
1142           proto_tree_add_item(subtree, hf_vendorclass_enterprise, tvb, off, 4, FALSE);
1143           if (optlen > 4) {
1144             buf = tvb_bytes_to_str(tvb, off + 4, optlen - 4);
1145             proto_tree_add_text(subtree, tvb, off+4, optlen-4,
1146                                 "vendor-class-data: %s", buf);
1147           }
1148           break;
1149         case OPTION_VENDOR_OPTS:
1150           if (optlen < 4) {
1151             proto_tree_add_text(subtree, tvb, off,
1152                                 optlen, "VENDOR_OPTS: malformed option");
1153             break;
1154           }
1155
1156           enterprise_no = tvb_get_ntohl(tvb, off);
1157           proto_tree_add_item(subtree, hf_vendoropts_enterprise, tvb, off, 4, FALSE);
1158           
1159           if (optlen >= 4) {
1160                                 if (enterprise_no == 4491) {
1161                                                 dissect_cablelabs_specific_opts(subtree, tvb, off+4, optlen-4);
1162                 } else {
1163                                 int optoffset = 0;
1164
1165                 while((optlen - 4 - optoffset) > 0)  {
1166                                                                 int olen = tvb_get_ntohs(tvb, off + optoffset + 6);
1167                                                                 ti = proto_tree_add_text(subtree, tvb, off + optoffset + 4, 
1168                                                                                                                 4 + olen, "option");
1169                                                                 subtree_2 = proto_item_add_subtree(ti, ett_dhcpv6_option_vsoption);
1170
1171                                                                 proto_tree_add_text(subtree_2, tvb, off + optoffset + 4, 2, 
1172                                                                                                         "option code: %u", tvb_get_ntohs(tvb, off + optoffset + 4));
1173                                                                 proto_tree_add_text(subtree_2, tvb, off + optoffset + 6, 2, 
1174                                                                                                         "option length: %u", olen);
1175                                                                 proto_tree_add_text(subtree_2, tvb, off + optoffset + 8, olen, 
1176                                                                                                         "option-data");
1177                                                                 optoffset += (4 + olen);
1178                                                 }
1179                                 }
1180           }
1181           break;
1182         case OPTION_INTERFACE_ID:
1183           if (optlen == 0) {
1184             proto_tree_add_text(subtree, tvb, off,
1185                                 optlen, "INTERFACE_ID: malformed option");
1186             break;
1187           }
1188           buf = tvb_get_ephemeral_string(tvb, off, optlen);
1189           proto_tree_add_text(subtree, tvb, off, optlen, "Interface-ID: %s", buf);
1190           break;
1191         case OPTION_RECONF_MSG:
1192           if (optlen != 1) {
1193             proto_tree_add_text(subtree, tvb, off,
1194                                 optlen, "RECONF_MSG: malformed option");
1195             break;
1196           }
1197           proto_tree_add_text(subtree, tvb, off, optlen,
1198                               "Reconfigure-type: %s",
1199                               val_to_str(tvb_get_guint8(tvb, off),
1200                                          msgtype_vals,
1201                                          "Message Type %u"));
1202           break;
1203         case OPTION_SIP_SERVER_D:
1204                 if (optlen > 0) {
1205                         proto_tree_add_text(subtree, tvb, off, optlen,
1206                                 "SIP Servers Domain Search List");
1207                 }
1208                 dhcpv6_domain(subtree,tvb, off, optlen);
1209                 break;
1210         case OPTION_SIP_SERVER_A:
1211                 if (optlen % 16) {
1212                         proto_tree_add_text(subtree, tvb, off, optlen,
1213                                 "SIP servers address: malformed option");
1214                         break;
1215                 }
1216                 for (i = 0; i < optlen; i += 16) {
1217                         tvb_get_ipv6(tvb, off + i, &in6);
1218                         proto_tree_add_text(subtree, tvb, off + i,
1219                                 sizeof(in6), "SIP servers address: %s",
1220                                 ip6_to_str(&in6));
1221                 }
1222                 break;
1223         case OPTION_DNS_SERVERS:
1224                 if (optlen % 16) {
1225                         proto_tree_add_text(subtree, tvb, off, optlen,
1226                                 "DNS servers address: malformed option");
1227                         break;
1228                 }
1229                 for (i = 0; i < optlen; i += 16) {
1230                         tvb_get_ipv6(tvb, off + i, &in6);
1231                         proto_tree_add_text(subtree, tvb, off + i,
1232                                 sizeof(in6), "DNS servers address: %s",
1233                                 ip6_to_str(&in6));
1234                 }
1235                 break;
1236         case OPTION_DOMAIN_LIST:
1237           if (optlen > 0) {
1238             proto_tree_add_text(subtree, tvb, off, optlen, "DNS Domain Search List");
1239           }
1240           dhcpv6_domain(subtree,tvb, off, optlen);
1241           break;
1242         case OPTION_NIS_SERVERS:
1243                 if (optlen % 16) {
1244                         proto_tree_add_text(subtree, tvb, off, optlen,
1245                                 "NIS servers address: malformed option");
1246                         break;
1247                 }
1248                 for (i = 0; i < optlen; i += 16) {
1249                         tvb_get_ipv6(tvb, off + i, &in6);
1250                         proto_tree_add_text(subtree, tvb, off + i,
1251                                 sizeof(in6), "NIS servers address: %s",
1252                                 ip6_to_str(&in6));
1253                 }
1254                 break;
1255         case OPTION_NISP_SERVERS:
1256                 if (optlen % 16) {
1257                         proto_tree_add_text(subtree, tvb, off, optlen,
1258                                 "NISP servers address: malformed option");
1259                         break;
1260                 }
1261                 for (i = 0; i < optlen; i += 16) {
1262                         tvb_get_ipv6(tvb, off + i, &in6);
1263                         proto_tree_add_text(subtree, tvb, off + i,
1264                                 sizeof(in6), "NISP servers address: %s",
1265                                 ip6_to_str(&in6));
1266                 }
1267                 break;
1268         case OPTION_NIS_DOMAIN_NAME:
1269           if (optlen > 0) {
1270             proto_tree_add_text(subtree, tvb, off, optlen, "nis-domain-name");
1271           }
1272           dhcpv6_domain(subtree,tvb, off, optlen);
1273           break;
1274         case OPTION_NISP_DOMAIN_NAME:
1275           if (optlen > 0) {
1276             proto_tree_add_text(subtree, tvb, off, optlen, "nisp-domain-name");
1277           }
1278           dhcpv6_domain(subtree,tvb, off, optlen);
1279           break;
1280         case OPTION_SNTP_SERVERS:
1281                 if (optlen % 16) {
1282                         proto_tree_add_text(subtree, tvb, off, optlen,
1283                                 "SNTP servers address: malformed option");
1284                         break;
1285                 }
1286                 for (i = 0; i < optlen; i += 16) {
1287                         tvb_get_ipv6(tvb, off + i, &in6);
1288                         proto_tree_add_text(subtree, tvb, off + i,
1289                                 sizeof(in6), "SNTP servers address: %s",
1290                                 ip6_to_str(&in6));
1291                 }
1292                 break;
1293         case OPTION_LIFETIME:
1294           if (optlen != 4) {
1295             proto_tree_add_text(subtree, tvb, off,
1296                                 optlen, "LIFETIME: malformed option");
1297             break;
1298           }
1299           proto_tree_add_text(subtree, tvb, off, 4,
1300                               "Lifetime: %d",
1301                               (guint32)tvb_get_ntohl(tvb, off));
1302           break;
1303         case OPTION_BCMCS_SERVER_D:
1304                 if (optlen > 0) {
1305                         proto_tree_add_text(subtree, tvb, off, optlen,
1306                                 "BCMCS Servers Domain Search List");
1307                 }
1308                 dhcpv6_domain(subtree,tvb, off, optlen);
1309                 break;
1310         case OPTION_BCMCS_SERVER_A:
1311                 if (optlen % 16) {
1312                         proto_tree_add_text(subtree, tvb, off, optlen,
1313                                 "BCMCS servers address: malformed option");
1314                         break;
1315                 }
1316                 for (i = 0; i < optlen; i += 16) {
1317                         tvb_get_ipv6(tvb, off + i, &in6);
1318                         proto_tree_add_text(subtree, tvb, off + i,
1319                                 sizeof(in6), "BCMCS servers address: %s",
1320                                 ip6_to_str(&in6));
1321                 }
1322                 break;
1323         case OPTION_REMOTE_ID:
1324           if (optlen < 4) {
1325             proto_tree_add_text(subtree, tvb, off,
1326                                 optlen, "REMOTE_ID: malformed option");
1327             break;
1328           }
1329           proto_tree_add_item(subtree, hf_remoteid_enterprise, tvb, off, 4, FALSE);
1330           off += 4;
1331           optlen -= 4;
1332           buf = tvb_bytes_to_str(tvb, off, optlen);
1333           proto_tree_add_text(subtree, tvb, off, optlen, "Remote-ID: %s", buf);
1334           break;
1335         case OPTION_SUBSCRIBER_ID:
1336           if (optlen == 0) {
1337             proto_tree_add_text(subtree, tvb, off,
1338                                 optlen, "SUBSCRIBER_ID: malformed option");
1339             break;
1340           }
1341           buf = tvb_get_ephemeral_string(tvb, off, optlen);
1342           proto_tree_add_text(subtree, tvb, off, optlen, "Subscriber-ID: %s", buf);
1343           break;
1344         case OPTION_CLIENT_FQDN:
1345           if (optlen < 1) {
1346             proto_tree_add_text(subtree, tvb, off,
1347                                 optlen, "FQDN: malformed option");
1348           } else {
1349             /*
1350              * +-----+-+-+-+
1351              * | MBZ |N|O|S|
1352              * +-----+-+-+-+
1353              */
1354             proto_tree_add_item(subtree, hf_clientfqdn_reserved, tvb, off, 1, FALSE);
1355             proto_tree_add_item(subtree, hf_clientfqdn_n, tvb, off, 1, FALSE);
1356             proto_tree_add_item(subtree, hf_clientfqdn_o, tvb, off, 1, FALSE);
1357             proto_tree_add_item(subtree, hf_clientfqdn_s, tvb, off, 1, FALSE);
1358 #if 0
1359             proto_tree_add_text(subtree, tvb, off, 1,
1360                               "flags: %d",
1361                               (guint32)tvb_get_guint8(tvb, off));
1362 #endif
1363             dhcpv6_domain(subtree, tvb, off + 1, optlen - 1);
1364           }
1365           break;
1366         case OPTION_PANA_AGENT:
1367                 if (optlen % 16) {
1368                         proto_tree_add_text(subtree, tvb, off, optlen,
1369                                 "PANA agent address: malformed option");
1370                         break;
1371                 }
1372                 for (i = 0; i < optlen; i += 16) {
1373                         tvb_get_ipv6(tvb, off + i, &in6);
1374                         proto_tree_add_text(subtree, tvb, off + i,
1375                                 sizeof(in6), "PANA agents address: %s",
1376                                 ip6_to_str(&in6));
1377                 }
1378                 break;
1379         case OPTION_TIME_ZONE:
1380           if (optlen > 0) {
1381               buf = tvb_get_ephemeral_string(tvb, off, optlen);
1382               proto_tree_add_text(subtree, tvb, off, optlen, "time-zone: %s", buf);
1383           }
1384           break;
1385         case OPTION_TZDB:
1386           if (optlen > 0) {
1387               buf = tvb_get_ephemeral_string(tvb, off, optlen);
1388               proto_tree_add_text(subtree, tvb, off, optlen, "tz-database: %s", buf);
1389           }
1390           break;
1391         case OPTION_LQ_QUERY:
1392             {
1393                 guint8 query_type;
1394                 struct e_in6_addr in6_local;
1395
1396                 if (optlen < 17) {
1397                     proto_tree_add_text(subtree, tvb, off, optlen,
1398                                         "LQ-QUERY: malformed option");
1399                     break;
1400                 }
1401                 query_type = tvb_get_guint8(tvb, off);
1402                 switch (query_type) {
1403                 case 1:
1404                      proto_tree_add_text(subtree, tvb, off, 1,
1405                                          "Query-type: %s (%u)",
1406                                          "by-address", query_type);
1407                      break;
1408                 case 2:
1409                      proto_tree_add_text(subtree, tvb, off, 1,
1410                                          "Query-type: %s (%u)",
1411                                          "by-clientID", query_type);
1412                      break;
1413                 default:
1414                      proto_tree_add_text(subtree, tvb, off, 1,
1415                                          "Query-type: %s (%u)",
1416                                          "unknown?", query_type);
1417                      break;
1418                 }
1419                 tvb_get_ipv6(tvb, off + 1, &in6_local);
1420                 proto_tree_add_text(subtree, tvb, off + 1, 16,
1421                                     "Link address: %s", ip6_to_str(&in6_local));
1422                 temp_optlen = 17;
1423                 while ((optlen - temp_optlen) > 0) {
1424                     temp_optlen += dhcpv6_option(tvb, pinfo, subtree,
1425                                         downstream, off + temp_optlen,
1426                                         off + optlen, at_end);
1427                     if (*at_end) {
1428                         /* Bad option - just skip to the end */
1429                         temp_optlen = optlen;
1430                     }
1431                 }
1432             }
1433             break;
1434         case OPTION_CLIENT_DATA:
1435             temp_optlen = 0;
1436             while ((optlen - temp_optlen) > 0) {
1437                 temp_optlen += dhcpv6_option(tvb, pinfo, subtree,
1438                                              downstream, off + temp_optlen,
1439                                              off + optlen, at_end);
1440                 if (*at_end) {
1441                     /* Bad option - just skip to the end */
1442                     temp_optlen = optlen;
1443                 }
1444             }
1445             break;
1446         case OPTION_CLT_TIME:
1447             if (optlen != 4) {
1448                 proto_tree_add_text(subtree, tvb, off, optlen,
1449                                     "CLT_TIME: malformed option");
1450                 break;
1451             }
1452             proto_tree_add_text(subtree, tvb, off, 4,
1453                                 "Clt_time: %d",
1454                                 (guint32)tvb_get_ntohl(tvb, off));
1455             break;
1456         case OPTION_LQ_RELAY_DATA:
1457             if (optlen < 16) {
1458                 proto_tree_add_text(subtree, tvb, off, optlen,
1459                                     "LQ_RELAY_DATA: malformed option");
1460                 break;
1461             }
1462             tvb_get_ipv6(tvb, off, &in6);
1463             proto_tree_add_text(subtree, tvb, off, 16,
1464                                 "Peer address: %s", ip6_to_str(&in6));
1465             proto_tree_add_text(subtree, tvb, off + 16, optlen - 16,
1466                                 "DHCPv6 relay message");
1467             break;
1468         case OPTION_LQ_CLIENT_LINK:
1469                 if (optlen % 16) {
1470                         proto_tree_add_text(subtree, tvb, off, optlen,
1471                                 "LQ client links address: malformed option");
1472                         break;
1473                 }
1474                 for (i = 0; i < optlen; i += 16) {
1475                         tvb_get_ipv6(tvb, off + i, &in6);
1476                         proto_tree_add_text(subtree, tvb, off + i,
1477                                 sizeof(in6), "LQ client links address: %s",
1478                                 ip6_to_str(&in6));
1479                 }
1480                 break;
1481         case OPTION_CAPWAP_AC_V6:
1482                 if (optlen % 16) {
1483                         proto_tree_add_text(subtree, tvb, off, optlen,
1484                                 "CAPWAP Access Controllers address: malformed option");
1485                         break;
1486                 }
1487                 for (i = 0; i < optlen; i += 16) {
1488                         tvb_get_ipv6(tvb, off + i, &in6);
1489                         proto_tree_add_text(subtree, tvb, off + i,
1490                                 sizeof(in6), "CAPWAP Access Controllers address: %s",
1491                                 ip6_to_str(&in6));
1492                 }
1493                 break;
1494         case OPTION_IAPREFIX:
1495             {
1496                 guint32 preferred_lifetime, valid_lifetime;
1497                 guint8  prefix_length;
1498                 struct e_in6_addr in6_local;
1499
1500                 if (optlen < 25) {
1501                    proto_tree_add_text(subtree, tvb, off,
1502                                        optlen, "IAPREFIX: malformed option");
1503                    break;
1504                 }
1505
1506                 preferred_lifetime = tvb_get_ntohl(tvb, off);
1507                 valid_lifetime = tvb_get_ntohl(tvb, off + 4);
1508                 prefix_length  = tvb_get_guint8(tvb, off + 8);
1509                 if (preferred_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
1510                         proto_tree_add_text(subtree, tvb, off, 4,
1511                                     "Preferred lifetime: infinity");
1512                 } else {
1513                         proto_tree_add_text(subtree, tvb, off, 4,
1514                                     "Preferred lifetime: %u", preferred_lifetime);
1515                 }
1516                 if (valid_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
1517                         proto_tree_add_text(subtree, tvb, off + 4, 4,
1518                                     "Valid lifetime: infinity");
1519                 } else {
1520                         proto_tree_add_text(subtree, tvb, off + 4, 4,
1521                                     "Valid lifetime: %u", valid_lifetime);
1522                 }
1523                 proto_tree_add_text(subtree, tvb, off + 8, 1,
1524                                     "Prefix length: %d", prefix_length);
1525                 tvb_get_ipv6(tvb, off + 9, &in6_local);
1526                 proto_tree_add_text(subtree, tvb, off + 9,
1527                                     16, "Prefix address: %s",
1528                                     ip6_to_str(&in6_local));
1529                 
1530                 temp_optlen = 25;
1531                 while ((optlen - temp_optlen) > 0) {
1532                    temp_optlen += dhcpv6_option(tvb, pinfo, subtree, downstream,
1533                                    off+temp_optlen, off + optlen, at_end);
1534                    if (*at_end) {
1535                      /* Bad option - just skip to the end */
1536                      temp_optlen = optlen;
1537                    }
1538                 }
1539             }
1540             break;
1541         case OPTION_MIP6_HA:
1542                 if (optlen != 16) {
1543                         proto_tree_add_text(subtree, tvb, off, optlen,
1544                                 "MIP6_HA: malformed option");
1545                         break;
1546                 }
1547
1548                 tvb_get_ipv6(tvb, off, &in6);
1549                 proto_tree_add_text(subtree, tvb, off,
1550                         16, "Home Agent: %s", ip6_to_str(&in6));
1551                 break;
1552         case OPTION_MIP6_HOA:
1553                 if (optlen != 16) {
1554                         proto_tree_add_text(subtree, tvb, off, optlen,
1555                                 "MIP6_HOA: malformed option");
1556                         break;
1557                 }
1558
1559                 tvb_get_ipv6(tvb, off, &in6);
1560                 proto_tree_add_text(subtree, tvb, off,
1561                         16, "Home Address: %s", ip6_to_str(&in6));
1562                 break;
1563         case OPTION_NAI:
1564                 if (optlen < 4) {
1565                         proto_tree_add_text(subtree, tvb, off, optlen,
1566                                 "NAI: malformed option");
1567                         break;
1568                 }
1569                 proto_tree_add_text(subtree, tvb, off, optlen,
1570                         "NAI : %s", tvb_get_ptr(tvb, off, optlen - 2));
1571                 break;
1572         }
1573
1574         return 4 + optlen;
1575 }
1576
1577
1578 static void
1579 dissect_dhcpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
1580     gboolean downstream, int off, int eoff)
1581 {
1582         proto_tree *bp_tree = NULL;
1583         proto_item *ti;
1584         guint8 msgtype;
1585         gboolean at_end;
1586         struct e_in6_addr in6; 
1587
1588         msgtype = tvb_get_guint8(tvb, off);
1589
1590         col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str(msgtype, msgtype_vals, "Message Type %u"));
1591
1592         if (tree) {
1593                 ti = proto_tree_add_item(tree, proto_dhcpv6, tvb, off, eoff - off, FALSE);
1594                 bp_tree = proto_item_add_subtree(ti, ett_dhcpv6);
1595         }
1596
1597
1598         if (msgtype == RELAY_FORW || msgtype == RELAY_REPLY) {
1599            if (tree) {
1600                 proto_tree_add_item(bp_tree, hf_dhcpv6_msgtype, tvb, off, 1, FALSE);
1601                 proto_tree_add_item(bp_tree, hf_dhcpv6_hopcount, tvb, off + 1, 1, FALSE);
1602                 proto_tree_add_item(bp_tree, hf_dhcpv6_linkaddr, tvb, off + 2, 16, FALSE);
1603                 tvb_get_ipv6(tvb, off + 2, &in6);
1604                 col_append_fstr(pinfo->cinfo, COL_INFO, "L: %s ", ip6_to_str(&in6));
1605                 proto_tree_add_item(bp_tree, hf_dhcpv6_peeraddr, tvb, off + 18, 16, FALSE);
1606            }
1607            off += 34;
1608         } else {
1609            if (tree) {
1610                 proto_tree_add_item(bp_tree, hf_dhcpv6_msgtype, tvb, off, 1, FALSE);
1611                 proto_tree_add_item(bp_tree, hf_dhcpv6_xid, tvb, off + 1, 3, FALSE);
1612            }
1613            col_append_fstr(pinfo->cinfo, COL_INFO, "XID: 0x%x ", tvb_get_ntoh24(tvb, off + 1));
1614            off += 4;
1615         }
1616
1617         at_end = FALSE;
1618         while (off < eoff && !at_end)
1619                 off += dhcpv6_option(tvb, pinfo, bp_tree, downstream, off, eoff, &at_end);
1620 }
1621
1622 static void
1623 dissect_dhcpv6_downstream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1624 {
1625         col_set_str(pinfo->cinfo, COL_PROTOCOL, "DHCPv6");
1626         col_clear(pinfo->cinfo, COL_INFO);
1627         dissect_dhcpv6(tvb, pinfo, tree, TRUE, 0, tvb_reported_length(tvb));
1628 }
1629
1630 static void
1631 dissect_dhcpv6_upstream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1632 {
1633         col_set_str(pinfo->cinfo, COL_PROTOCOL, "DHCPv6");
1634         col_clear(pinfo->cinfo, COL_INFO);
1635         dissect_dhcpv6(tvb, pinfo, tree, FALSE, 0, tvb_reported_length(tvb));
1636 }
1637
1638
1639 void
1640 proto_register_dhcpv6(void)
1641 {
1642   static hf_register_info hf[] = {
1643
1644   /* DHCPv6 header */
1645     { &hf_dhcpv6_msgtype,
1646       { "Message type", "dhcpv6.msgtype", FT_UINT8, BASE_DEC, VALS(msgtype_vals), 0x0, NULL, HFILL }},
1647     { &hf_dhcpv6_hopcount,
1648       { "Hopcount", "dhcpv6.hopcount", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}},
1649     { &hf_dhcpv6_xid,
1650       { "Transaction ID", "dhcpv6.xid", FT_UINT24, BASE_HEX, NULL, 0, NULL, HFILL}},
1651     { &hf_dhcpv6_linkaddr,
1652       { "Link address", "dhcpv6.linkaddr", FT_IPv6, BASE_NONE, NULL, 0, NULL, HFILL}},
1653     { &hf_dhcpv6_peeraddr,
1654       { "Peer address", "dhcpv6.peeraddr", FT_IPv6, BASE_NONE, NULL, 0, NULL, HFILL}},
1655   /* Generic option stuff */
1656     { &hf_option_type,
1657       { "Option", "dhcpv6.option.type", FT_UINT16, BASE_DEC, VALS(opttype_vals), 0x0, NULL, HFILL}},
1658     { &hf_option_length,
1659       { "Length", "dhcpv6.option.length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL}},
1660     { &hf_option_value,
1661       { "Value", "dhcpv6.option.value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL}},
1662   /* Individual options */
1663     { &hf_clientfqdn_reserved,
1664       { "Reserved", "dhcpv6.clientfqdn.reserved", FT_UINT8, BASE_HEX, NULL, 0xF8, NULL, HFILL}},
1665     { &hf_clientfqdn_n,
1666       { "N", "dhcpv6.clientfqdn.n", FT_BOOLEAN, 8, TFS(&fqdn_n), 0x4, NULL, HFILL}},
1667     { &hf_clientfqdn_o,
1668       { "O", "dhcpv6.clientfqdn.o", FT_BOOLEAN, 8, TFS(&fqdn_o), 0x2, NULL, HFILL}},
1669     { &hf_clientfqdn_s,
1670       { "S", "dhcpv6.clientfqdn.s", FT_BOOLEAN, 8, TFS(&fqdn_s), 0x1, NULL, HFILL}},
1671     { &hf_remoteid_enterprise,
1672       { "Enterprise ID", "dhvpv6.remoteid.enterprise", FT_UINT32, BASE_DEC,  VALS(sminmpec_values), 0, "RemoteID Enterprise Number", HFILL }},
1673     { &hf_vendoropts_enterprise,
1674       { "Enterprise ID", "dhvpv6.vendoropts.enterprise", FT_UINT32, BASE_DEC,  VALS(sminmpec_values), 0, "Vendor opts Enterprise Number", HFILL }},
1675     { &hf_vendorclass_enterprise,
1676       { "Enterprise ID", "dhvpv6.vendorclass.enterprise", FT_UINT32, BASE_DEC,  VALS(sminmpec_values), 0, "Vendor Class Enterprise Number", HFILL }},
1677     { &hf_duiden_enterprise,
1678       { "Enterprise ID", "dhvpv6.duiden.enterprise", FT_UINT32, BASE_DEC,  VALS(sminmpec_values), 0, "DUID EN Enterprise Number", HFILL }},
1679
1680   };
1681   static gint *ett[] = {
1682     &ett_dhcpv6,
1683     &ett_dhcpv6_option,
1684     &ett_dhcpv6_option_vsoption,
1685     &ett_dhcpv6_vendor_option,
1686     &ett_dhcpv6_pkt_option,
1687   };
1688
1689   proto_dhcpv6 = proto_register_protocol("DHCPv6", "DHCPv6", "dhcpv6");
1690   proto_register_field_array(proto_dhcpv6, hf, array_length(hf));
1691   proto_register_subtree_array(ett, array_length(ett));
1692
1693   /* Allow other dissectors to find this one by name.
1694      Just choose upstream version for now as they are identical. */
1695   register_dissector("dhcpv6", dissect_dhcpv6_upstream, proto_dhcpv6);
1696 }
1697
1698 void
1699 proto_reg_handoff_dhcpv6(void)
1700 {
1701   dissector_handle_t dhcpv6_handle;
1702
1703   dhcpv6_handle = create_dissector_handle(dissect_dhcpv6_downstream,
1704         proto_dhcpv6);
1705   dissector_add("udp.port", UDP_PORT_DHCPV6_DOWNSTREAM, dhcpv6_handle);
1706   dhcpv6_handle = create_dissector_handle(dissect_dhcpv6_upstream,
1707         proto_dhcpv6);
1708   dissector_add("udp.port", UDP_PORT_DHCPV6_UPSTREAM, dhcpv6_handle);
1709 }
1710