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