From Alexis La Goutte (bug 3308):
[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 "packet-arp.h"
54
55 static int proto_dhcpv6 = -1;
56 static int hf_dhcpv6_msgtype = -1;
57 static int hf_fqdn_1 = -1;
58 static int hf_fqdn_2 = -1;
59 static int hf_fqdn_3 = -1;
60 static int hf_fqdn_4 = -1;
61
62 static gint ett_dhcpv6 = -1;
63 static gint ett_dhcpv6_option = -1;
64 static gint ett_dhcpv6_option_vsoption = -1;
65
66 #define UDP_PORT_DHCPV6_DOWNSTREAM      546
67 #define UDP_PORT_DHCPV6_UPSTREAM        547
68
69 #define DHCPV6_LEASEDURATION_INFINITY   0xffffffff
70
71 #define SOLICIT                 1
72 #define ADVERTISE               2
73 #define REQUEST                 3
74 #define CONFIRM                 4
75 #define RENEW                   5
76 #define REBIND                  6
77 #define REPLY                   7
78 #define RELEASE                 8
79 #define DECLINE                 9
80 #define RECONFIGURE             10
81 #define INFORMATION_REQUEST     11
82 #define RELAY_FORW              12
83 #define RELAY_REPLY             13
84 #define LEASEQUERY              14
85 #define LEASEQUERY_REPLY        15
86
87 #define OPTION_CLIENTID         1
88 #define OPTION_SERVERID         2
89 #define OPTION_IA_NA            3
90 #define OPTION_IA_TA            4
91 #define OPTION_IAADDR           5
92 #define OPTION_ORO              6
93 #define OPTION_PREFERENCE       7
94 #define OPTION_ELAPSED_TIME     8
95 #define OPTION_RELAY_MSG        9
96 /* #define      OPTION_SERVER_MSG       10 */
97 #define OPTION_AUTH             11
98 #define OPTION_UNICAST          12
99 #define OPTION_STATUS_CODE      13
100 #define OPTION_RAPID_COMMIT     14
101 #define OPTION_USER_CLASS       15
102 #define OPTION_VENDOR_CLASS     16
103 #define OPTION_VENDOR_OPTS      17
104 #define OPTION_INTERFACE_ID     18
105 #define OPTION_RECONF_MSG       19
106 #define OPTION_RECONF_ACCEPT    20
107 #define OPTION_SIP_SERVER_D     21
108 #define OPTION_SIP_SERVER_A     22
109 #define OPTION_DNS_SERVERS      23
110 #define OPTION_DOMAIN_LIST      24
111 #define OPTION_IA_PD            25
112 #define OPTION_IAPREFIX         26
113 #define OPTION_NIS_SERVERS      27
114 #define OPTION_NISP_SERVERS     28
115 #define OPTION_NIS_DOMAIN_NAME  29
116 #define OPTION_NISP_DOMAIN_NAME 30
117 #define OPTION_SNTP_SERVERS     31
118 #define OPTION_LIFETIME         32
119 #define OPTION_BCMCS_SERVER_D   33
120 #define OPTION_BCMCS_SERVER_A   34
121 #define OPTION_GEOCONF_CIVIC    36
122 #define OPTION_REMOTE_ID        37
123 #define OPTION_SUBSCRIBER_ID    38
124 #define OPTION_CLIENT_FQDN      39
125 #define OPTION_PANA_AGENT       40
126 #define OPTION_TIME_ZONE        41
127 #define OPTION_TZDB             42
128 #define OPTION_ERO              43
129 #define OPTION_LQ_QUERY         44
130 #define OPTION_CLIENT_DATA      45
131 #define OPTION_CLT_TIME         46
132 #define OPTION_LQ_RELAY_DATA    47
133 #define OPTION_LQ_CLIENT_LINK   48
134 #define OPTION_CAPWAP_AC_V6     52
135
136 /* temporary value until defined by IETF */
137 #define OPTION_MIP6_HA          165
138 #define OPTION_MIP6_HOA         166
139 #define OPTION_NAI              167
140
141 #define DUID_LLT                1
142 #define DUID_EN                 2
143 #define DUID_LL                 3
144 #define DUID_LL_OLD             4
145
146 static void
147 dissect_dhcpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
148     gboolean downstream, int off, int eoff);
149
150 static const value_string msgtype_vals[] = {
151         { SOLICIT,      "Solicit" },
152         { ADVERTISE,    "Advertise" },
153         { REQUEST,      "Request" },
154         { CONFIRM,      "Confirm" },
155         { RENEW,        "Renew" },
156         { REBIND,       "Rebind" },
157         { REPLY,        "Reply" },
158         { RELEASE,      "Release" },
159         { DECLINE,      "Decline" },
160         { RECONFIGURE,  "Reconfigure" },
161         { INFORMATION_REQUEST,  "Information-request" },
162         { RELAY_FORW,   "Relay-forw" },
163         { RELAY_REPLY,  "Relay-reply" },
164         { LEASEQUERY,   "Leasequery" },
165         { LEASEQUERY_REPLY,     "Leasequery-reply" },
166         { 0, NULL }
167 };
168
169 static const value_string opttype_vals[] = {
170         { OPTION_CLIENTID,      "Client Identifier" },
171         { OPTION_SERVERID,      "Server Identifier" },
172         { OPTION_IA_NA,         "Identity Association for Non-temporary Address" },
173         { OPTION_IA_TA,         "Identity Association for Temporary Address" },
174         { OPTION_IAADDR,        "IA Address" },
175         { OPTION_ORO,           "Option Request" },
176         { OPTION_PREFERENCE,    "Preference" },
177         { OPTION_ELAPSED_TIME,  "Elapsed time" },
178         { OPTION_RELAY_MSG,     "Relay Message" },
179 /*      { OPTION_SERVER_MSG,    "Server message" }, */
180         { OPTION_AUTH,          "Authentication" },
181         { OPTION_UNICAST,       "Server unicast" },
182         { OPTION_STATUS_CODE,   "Status code" },
183         { OPTION_RAPID_COMMIT,  "Rapid Commit" },
184         { OPTION_USER_CLASS,    "User Class" },
185         { OPTION_VENDOR_CLASS,  "Vendor Class" },
186         { OPTION_VENDOR_OPTS,   "Vendor-specific Information" },
187         { OPTION_INTERFACE_ID,  "Interface-Id" },
188         { OPTION_RECONF_MSG,    "Reconfigure Message" },
189         { OPTION_RECONF_ACCEPT, "Reconfigure Accept" },
190         { OPTION_SIP_SERVER_D,  "SIP Server Domain Name List" },
191         { OPTION_SIP_SERVER_A,  "SIP Servers IPv6 Address List" },
192         { OPTION_DNS_SERVERS,   "DNS recursive name server" },
193         { OPTION_DOMAIN_LIST,   "Domain Search List" },
194         { OPTION_IA_PD,         "Identity Association for Prefix Delegation" },
195         { OPTION_IAPREFIX,      "IA Prefix" },
196         { OPTION_NIS_SERVERS,   "Network Information Server" },
197         { OPTION_NISP_SERVERS,  "Network Information Server V2" },
198         { OPTION_NIS_DOMAIN_NAME, "Network Information Server Domain Name" },
199         { OPTION_NISP_DOMAIN_NAME,"Network Information Server V2 Domain Name" },
200         { OPTION_SNTP_SERVERS,  "Simple Network Time Protocol Server" },
201         { OPTION_LIFETIME,      "Lifetime" },
202         { OPTION_BCMCS_SERVER_D, "BCMCS Server Domain" },
203         { OPTION_BCMCS_SERVER_A, "BCMCS Servers IPv6 Address List" },
204         { OPTION_GEOCONF_CIVIC, "Geoconf Civic Address" },
205         { OPTION_REMOTE_ID,     "Remote Identifier" },
206         { OPTION_SUBSCRIBER_ID, "Subscriber Identifier" },
207         { OPTION_CLIENT_FQDN,   "Fully Qualified Domain Name" },
208         { OPTION_PANA_AGENT,    "PANA Agents IPv6 Address List" },
209         { OPTION_TIME_ZONE,     "Time Zone" },
210         { OPTION_TZDB,          "Time Zone Database" },
211         { OPTION_ERO,           "Echo Request Option" },
212         { OPTION_LQ_QUERY,      "Leasequery Query" },
213         { OPTION_CLIENT_DATA,   "Leasequery Client Data" },
214         { OPTION_CLT_TIME,      "Client Last Transaction Time" },
215         { OPTION_LQ_RELAY_DATA, "Leasequery Relay Data" },
216         { OPTION_LQ_CLIENT_LINK, "Leasequery Client Link Address List" },
217         { OPTION_CAPWAP_AC_V6,  "CAPWAP Access Controllers" },
218         { OPTION_MIP6_HA,       "Mobile IPv6 Home Agent" },
219         { OPTION_MIP6_HOA,      "Mobile IPv6 Home Address" },
220         { OPTION_NAI,           "Network Access Identifier" },
221         { 0,    NULL }
222 };
223
224 static const value_string statuscode_vals[] =
225 {
226         {0, "Success" },
227         {1, "UnspecFail" },
228         {2, "NoAddrAvail" },
229         {3, "NoBinding" },
230         {4, "NotOnLink" },
231         {5, "UseMulticast" },
232         {6, "NoPrefixAvail" },
233         {7, "UnknownQueryType" },
234         {8, "MalformedQuery" },
235         {9, "NotConfigured" },
236         {10, "NotAllowed" },
237         {0, NULL }
238 };
239
240 static const value_string duidtype_vals[] =
241 {
242         { DUID_LLT,     "link-layer address plus time" },
243         { DUID_EN,      "assigned by vendor based on Enterprise number" },
244         { DUID_LL,      "link-layer address" },
245         { DUID_LL_OLD,  "link-layer address (old)" },
246         { 0, NULL }
247 };
248
249 /* This FQDN draft is a mess, I've tried to understand, 
250    but N,O,S bit descriptions are really cryptic */
251 static const true_false_string fqdn_n = {
252 /*    "Client doesn't want server to perform DNS update", "" */
253     "N bit set","N bit cleared"
254 };
255
256 static const true_false_string fqdn_o = {
257     "O bit set", "O bit cleared" 
258 };
259
260 static const true_false_string fqdn_s = {
261 /*    "Forward mapping (FQDN-to-IPv6, AAAA) performed by client", 
262       "Forward mapping (FQDN-to-IPv6, AAAA) performed by server" */
263     "S bit set", "S bit cleared"
264 }; 
265
266 static void
267 dhcpv6_enterprise_number(proto_tree * subtree, tvbuff_t *tvb, int offset)
268 {
269           guint32 enterprise_number;
270           enterprise_number = tvb_get_ntohl(tvb, offset);
271           proto_tree_add_text(subtree, tvb, offset, 4,
272                               "Enterprise-number: %s (%u)",
273                               val_to_str(enterprise_number, sminmpec_values, "%u"),
274                               enterprise_number);
275 }
276
277 /* Adds domain */
278 static void
279 dhcpv6_domain(proto_tree * subtree, tvbuff_t *tvb, int offset, guint16 optlen)
280 {
281     int start_offset=offset;
282     char domain[256];
283     int pos;
284     guint8 len;
285
286     pos=0;
287     while(optlen){
288         /* this is the start of the domain name */
289         if(!pos){
290             start_offset=offset;
291         }
292         domain[pos]=0;
293
294         /* read length of the next substring */
295         len = tvb_get_guint8(tvb, offset);
296         /* Microsoft dhcpv6 clients aren't currently RFC 4704 conform: They send an
297          * ASCII string instead of a DNS record encoded domain name. Catch that case
298          * to allow us to continue after such a malformed record.
299          */
300         if ( optlen < len ) {
301                 proto_tree_add_text(subtree, tvb, start_offset, optlen, "Malformed DNS name record (MS Vista client?)");
302                 return;
303         }
304         offset++;
305         optlen--;
306         /* if len==0 and pos>0 we have read an entire domain string */
307         if(!len){
308             if(!pos){
309                 /* empty string, this must be an error? */
310                 proto_tree_add_text(subtree, tvb, start_offset, offset-start_offset, "Malformed option");
311                 return;
312             } else {
313                 proto_tree_add_text(subtree, tvb, start_offset, offset-start_offset, "Domain: %s", domain);
314                 pos=0;
315                 continue;
316             }
317         }
318
319         /* add the substring to domain */
320         if(pos){
321             domain[pos]='.';
322             pos++;
323         }
324         if(pos+len>254){
325                 /* too long string, this must be an error? */
326                 proto_tree_add_text(subtree, tvb, start_offset, offset-start_offset, "Malformed option");
327                 return;
328         }
329         tvb_memcpy(tvb, domain+pos, offset, len);
330         pos+=len;
331         offset+=len;
332         optlen-=len;
333     }        
334     
335     if(pos){
336         domain[pos]=0;
337         proto_tree_add_text(subtree, tvb, start_offset, offset-start_offset, "Domain: %s", domain);
338     }
339 }    
340
341 /* Returns the number of bytes consumed by this option. */
342 static int
343 dhcpv6_option(tvbuff_t *tvb, packet_info *pinfo, proto_tree *bp_tree, 
344                 gboolean downstream, int off, int eoff, gboolean *at_end)
345 {
346         guint8 *buf;
347         guint16 opttype;
348         guint16 optlen;
349         guint16 hwtype;
350         guint16 temp_optlen = 0;
351         proto_item *ti;
352         proto_tree *subtree;
353         proto_tree *subtree_2;
354         int i;
355         struct e_in6_addr in6;
356         guint16 duidtype;
357
358         /* option type and length must be present */
359         if (eoff - off < 4) {
360                 *at_end = TRUE;
361                 return 0;
362         }
363
364         opttype = tvb_get_ntohs(tvb, off);
365         optlen = tvb_get_ntohs(tvb, off + 2);
366
367         /* all option data must be present */
368         if (eoff - off < 4 + optlen) {
369                 *at_end = TRUE;
370                 return 0;
371         }
372
373         ti = proto_tree_add_text(bp_tree, tvb, off, 4 + optlen,
374                 "%s", val_to_str(opttype, opttype_vals, "DHCP option %u"));
375
376         subtree = proto_item_add_subtree(ti, ett_dhcpv6_option);
377         proto_tree_add_text(subtree, tvb, off, 2, "option type: %d", opttype);
378         proto_tree_add_text(subtree, tvb, off + 2, 2, "option length: %d",
379                 optlen);
380
381         off += 4;
382         switch (opttype) {
383         case OPTION_CLIENTID:
384         case OPTION_SERVERID:
385                 if (optlen < 2) {
386                         proto_tree_add_text(subtree, tvb, off, optlen,
387                                 "DUID: malformed option");
388                         break;
389                 }
390                 duidtype = tvb_get_ntohs(tvb, off);
391                 proto_tree_add_text(subtree, tvb, off, 2,
392                         "DUID type: %s (%u)",
393                                     val_to_str(duidtype,
394                                                duidtype_vals, "Unknown"),
395                                     duidtype);
396                 switch (duidtype) {
397                 case DUID_LLT:
398                         if (optlen < 8) {
399                                 proto_tree_add_text(subtree, tvb, off,
400                                         optlen, "DUID: malformed option");
401                                 break;
402                         }
403                         hwtype=tvb_get_ntohs(tvb, off + 2);
404                         proto_tree_add_text(subtree, tvb, off + 2, 2,
405                                 "Hardware type: %s (%u)", arphrdtype_to_str(hwtype, "Unknown"),
406                                 hwtype);
407                         /* XXX seconds since Jan 1 2000 */
408                         proto_tree_add_text(subtree, tvb, off + 4, 4,
409                                 "Time: %u", tvb_get_ntohl(tvb, off + 4));
410                         if (optlen > 8) {
411                                 proto_tree_add_text(subtree, tvb, off + 8,
412                                         optlen - 8, "Link-layer address: %s",
413                                         arphrdaddr_to_str(tvb_get_ptr(tvb, off+8, optlen-8), optlen-8, hwtype));
414                         }
415                         break;
416                 case DUID_EN:
417                         if (optlen < 6) {
418                                 proto_tree_add_text(subtree, tvb, off,
419                                         optlen, "DUID: malformed option");
420                                 break;
421                         }
422                         dhcpv6_enterprise_number(subtree, tvb, off + 2);
423                         if (optlen > 6) {
424                                 buf = tvb_bytes_to_str(tvb, off + 6, optlen - 6);
425                                 proto_tree_add_text(subtree, tvb, off + 6,
426                                         optlen - 6, "identifier: %s", buf);
427                         }
428                         break;
429                 case DUID_LL:
430                 case DUID_LL_OLD:
431                         if (optlen < 4) {
432                                 proto_tree_add_text(subtree, tvb, off,
433                                         optlen, "DUID: malformed option");
434                                 break;
435                         }
436                         hwtype=tvb_get_ntohs(tvb, off + 2);
437                         proto_tree_add_text(subtree, tvb, off + 2, 2,
438                                 "Hardware type: %s (%u)",
439                                 arphrdtype_to_str(hwtype, "Unknown"),
440                                 hwtype);
441                         if (optlen > 4) {
442                                 proto_tree_add_text(subtree, tvb, off + 4,
443                                         optlen - 4, "Link-layer address: %s",
444                                         arphrdaddr_to_str(tvb_get_ptr(tvb, off+4, optlen-4), optlen-4, hwtype));
445                         }
446                         break;
447                 }
448                 break;
449         case OPTION_IA_NA:
450         case OPTION_IA_PD:
451           if (optlen < 12) {
452              if (opttype == OPTION_IA_NA)
453                 proto_tree_add_text(subtree, tvb, off,
454                                     optlen, "IA_NA: malformed option");
455              else
456                 proto_tree_add_text(subtree, tvb, off,
457                                     optlen, "IA_PD: malformed option");
458              break;
459           }
460           proto_tree_add_text(subtree, tvb, off, 4,
461                               "IAID: %u",
462                               tvb_get_ntohl(tvb, off));
463           if (tvb_get_ntohl(tvb, off+4) == DHCPV6_LEASEDURATION_INFINITY) {
464               proto_tree_add_text(subtree, tvb, off+4, 4,
465                                   "T1: infinity");
466           } else {
467               proto_tree_add_text(subtree, tvb, off+4, 4,
468                                   "T1: %u", tvb_get_ntohl(tvb, off+4));
469           }
470
471           if (tvb_get_ntohl(tvb, off+8) == DHCPV6_LEASEDURATION_INFINITY) {
472               proto_tree_add_text(subtree, tvb, off+8, 4,
473                                   "T2: infinity");
474           } else {
475               proto_tree_add_text(subtree, tvb, off+8, 4,
476                                   "T2: %u", tvb_get_ntohl(tvb, off+8));
477           }
478
479           temp_optlen = 12;
480           while ((optlen - temp_optlen) > 0) {
481             temp_optlen += dhcpv6_option(tvb, pinfo, subtree, downstream,
482                             off+temp_optlen, off + optlen, at_end);
483             if (*at_end) {
484               /* Bad option - just skip to the end */
485               temp_optlen = optlen;
486             }
487           }
488           break;
489         case OPTION_IA_TA:
490           if (optlen < 4) {
491             proto_tree_add_text(subtree, tvb, off,
492                                 optlen, "IA_TA: malformed option");
493             break;
494           }
495           proto_tree_add_text(subtree, tvb, off, 4,
496                               "IAID: %u",
497                               tvb_get_ntohl(tvb, off));
498           temp_optlen = 4;
499           while ((optlen - temp_optlen) > 0) {
500             temp_optlen += dhcpv6_option(tvb, pinfo, subtree, downstream,
501                             off+temp_optlen, off + optlen, at_end);
502             if (*at_end) {
503               /* Bad option - just skip to the end */
504               temp_optlen = optlen;
505             }
506           }
507           break;
508         case OPTION_IAADDR:
509         {
510            guint32 preferred_lifetime, valid_lifetime;
511
512            if (optlen < 24) {
513               proto_tree_add_text(subtree, tvb, off,
514                                   optlen, "IAADDR: malformed option");
515               break;
516            }
517            tvb_get_ipv6(tvb, off, &in6);
518            proto_tree_add_text(subtree, tvb, off,
519                                sizeof(in6), "IPv6 address: %s",
520                                ip6_to_str(&in6));
521            
522            preferred_lifetime = tvb_get_ntohl(tvb, off + 16);
523            valid_lifetime = tvb_get_ntohl(tvb, off + 20);
524            
525            if (preferred_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
526               proto_tree_add_text(subtree, tvb, off + 16, 4,
527                                   "Preferred lifetime: infinity");
528            } else {
529               proto_tree_add_text(subtree, tvb, off + 16, 4,
530                                   "Preferred lifetime: %u", preferred_lifetime);
531            }
532            if (valid_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
533               proto_tree_add_text(subtree, tvb, off + 20, 4,
534                                   "Valid lifetime: infinity");
535            } else {
536               proto_tree_add_text(subtree, tvb, off + 20, 4,
537                                   "Valid lifetime: %u", valid_lifetime);
538            }
539            
540            temp_optlen = 24;
541            while ((optlen - temp_optlen) > 0) {
542               temp_optlen += dhcpv6_option(tvb, pinfo, subtree, downstream,
543                               off+temp_optlen, off + optlen, at_end);
544               if (*at_end) {
545                 /* Bad option - just skip to the end */
546                 temp_optlen = optlen;
547               }
548            }
549         }
550         break;
551         case OPTION_ORO:
552         case OPTION_ERO:
553                 for (i = 0; i < optlen; i += 2) {
554                     guint16 requested_opt_code;
555                     requested_opt_code = tvb_get_ntohs(tvb, off + i);
556                     proto_tree_add_text(subtree, tvb, off + i,
557                             2, "Requested Option code: %s (%d)",
558                                             val_to_str(requested_opt_code,
559                                                        opttype_vals,
560                                                        "Unknown"),
561                                             requested_opt_code);
562                 }
563                 break;
564         case OPTION_PREFERENCE:
565           if (optlen != 1) {
566             proto_tree_add_text(subtree, tvb, off,
567                                 optlen, "PREFERENCE: malformed option");
568             break;
569           }
570           proto_tree_add_text(subtree, tvb, off, 1,
571                               "pref-value: %d",
572                               (guint32)tvb_get_guint8(tvb, off));
573           break;
574         case OPTION_ELAPSED_TIME:
575           if (optlen != 2) {
576             proto_tree_add_text(subtree, tvb, off,
577                                 optlen, "ELAPSED-TIME: malformed option");
578             break;
579           }
580           proto_tree_add_text(subtree, tvb, off, 2,
581                               "elapsed-time: %u ms",
582                               10*(guint32)tvb_get_ntohs(tvb, off));
583           break;
584         case OPTION_RELAY_MSG:
585           if (optlen == 0) {
586             proto_tree_add_text(subtree, tvb, off,
587                                 optlen, "RELAY-MSG: malformed option");
588           } else {
589             /* here, we should dissect a full DHCP message */
590             dissect_dhcpv6(tvb, pinfo, subtree, downstream, off, off + optlen);
591           } 
592           break;
593         case OPTION_AUTH:
594           if (optlen < 11) {
595             proto_tree_add_text(subtree, tvb, off,
596                                 optlen, "AUTH: malformed option");
597             break;
598           }
599           proto_tree_add_text(subtree, tvb, off, 1,
600                               "Protocol: %d",
601                               (guint32)tvb_get_guint8(tvb, off));
602           proto_tree_add_text(subtree, tvb, off+1, 1,
603                               "Algorithm: %d",
604                               (guint32)tvb_get_guint8(tvb, off+1));
605           proto_tree_add_text(subtree, tvb, off+2, 1,
606                               "RDM: %d",
607                               (guint32)tvb_get_guint8(tvb, off+2));
608           proto_tree_add_text(subtree, tvb, off+3, 8,
609                               "Replay Detection");
610           if (optlen != 11)
611                 proto_tree_add_text(subtree, tvb, off+11, optlen-11,
612                                                         "Authentication Information");
613           break;
614         case OPTION_UNICAST:
615           if (optlen != 16) {
616             proto_tree_add_text(subtree, tvb, off,
617                                 optlen, "UNICAST: malformed option");
618             break;
619           }
620           tvb_get_ipv6(tvb, off, &in6);
621           proto_tree_add_text(subtree, tvb, off,
622                               sizeof(in6), "IPv6 address: %s",
623                                 ip6_to_str(&in6));
624           break;
625         case OPTION_STATUS_CODE:
626             {
627                 guint16 status_code;
628                 char *status_message = 0;
629                 status_code = tvb_get_ntohs(tvb, off);
630                 proto_tree_add_text(subtree, tvb, off, 2,
631                                     "Status Code: %s (%d)",
632                                     val_to_str(status_code, statuscode_vals,
633                                                "Unknown"),
634                                     status_code);
635
636                 if (optlen - 2 > 0) {
637                     status_message = tvb_get_ephemeral_string(tvb, off + 2, optlen - 2);
638                     proto_tree_add_text(subtree, tvb, off + 2, optlen - 2,
639                                         "Status Message: %s",
640                                         status_message);
641                 }
642             }
643             break;
644         case OPTION_VENDOR_CLASS:
645           if (optlen < 4) {
646             proto_tree_add_text(subtree, tvb, off,
647                                 optlen, "VENDOR_CLASS: malformed option");
648             break;
649           }
650           dhcpv6_enterprise_number(subtree, tvb, off);
651           if (optlen > 4) {
652             buf = tvb_bytes_to_str(tvb, off + 4, optlen - 4);
653             proto_tree_add_text(subtree, tvb, off+4, optlen-4,
654                                 "vendor-class-data: %s", buf);
655           }
656           break;
657         case OPTION_VENDOR_OPTS:
658           if (optlen < 4) {
659             proto_tree_add_text(subtree, tvb, off,
660                                 optlen, "VENDOR_OPTS: malformed option");
661             break;
662           }
663
664           dhcpv6_enterprise_number(subtree, tvb, off);
665           if (optlen >= 4)
666           {
667             int optoffset = 0;
668
669             while((optlen - 4 - optoffset) > 0)
670             {
671               int olen = tvb_get_ntohs(tvb, off + optoffset + 6);
672               ti = proto_tree_add_text(subtree, tvb, off + optoffset + 4, 4 + olen, "option");
673               subtree_2 = proto_item_add_subtree(ti, ett_dhcpv6_option_vsoption);
674
675               proto_tree_add_text(subtree_2, tvb, off + optoffset + 4, 2, "option code: %u", tvb_get_ntohs(tvb, off + optoffset + 4));
676               proto_tree_add_text(subtree_2, tvb, off + optoffset + 6, 2, "option length: %u", olen);
677               proto_tree_add_text(subtree_2, tvb, off + optoffset + 8, olen, "option-data");
678               optoffset += (4 + olen);
679             }
680           }
681           break;
682         case OPTION_INTERFACE_ID:
683           if (optlen == 0) {
684             proto_tree_add_text(subtree, tvb, off,
685                                 optlen, "INTERFACE_ID: malformed option");
686             break;
687           }
688           buf = tvb_get_ephemeral_string(tvb, off, optlen);
689           proto_tree_add_text(subtree, tvb, off, optlen, "Interface-ID: %s", buf);
690           break;
691         case OPTION_RECONF_MSG:
692           if (optlen != 1) {
693             proto_tree_add_text(subtree, tvb, off,
694                                 optlen, "RECONF_MSG: malformed option");
695             break;
696           }
697           proto_tree_add_text(subtree, tvb, off, optlen,
698                               "Reconfigure-type: %s",
699                               val_to_str(tvb_get_guint8(tvb, off),
700                                          msgtype_vals,
701                                          "Message Type %u"));
702           break;
703         case OPTION_SIP_SERVER_D:
704                 if (optlen > 0) {
705                         proto_tree_add_text(subtree, tvb, off, optlen,
706                                 "SIP Servers Domain Search List");
707                 }
708                 dhcpv6_domain(subtree,tvb, off, optlen);
709                 break;
710         case OPTION_SIP_SERVER_A:
711                 if (optlen % 16) {
712                         proto_tree_add_text(subtree, tvb, off, optlen,
713                                 "SIP servers address: malformed option");
714                         break;
715                 }
716                 for (i = 0; i < optlen; i += 16) {
717                         tvb_get_ipv6(tvb, off + i, &in6);
718                         proto_tree_add_text(subtree, tvb, off + i,
719                                 sizeof(in6), "SIP servers address: %s",
720                                 ip6_to_str(&in6));
721                 }
722                 break;
723         case OPTION_DNS_SERVERS:
724                 if (optlen % 16) {
725                         proto_tree_add_text(subtree, tvb, off, optlen,
726                                 "DNS servers address: malformed option");
727                         break;
728                 }
729                 for (i = 0; i < optlen; i += 16) {
730                         tvb_get_ipv6(tvb, off + i, &in6);
731                         proto_tree_add_text(subtree, tvb, off + i,
732                                 sizeof(in6), "DNS servers address: %s",
733                                 ip6_to_str(&in6));
734                 }
735                 break;
736         case OPTION_DOMAIN_LIST:
737           if (optlen > 0) {
738             proto_tree_add_text(subtree, tvb, off, optlen, "DNS Domain Search List");
739           }
740           dhcpv6_domain(subtree,tvb, off, optlen);
741           break;
742         case OPTION_NIS_SERVERS:
743                 if (optlen % 16) {
744                         proto_tree_add_text(subtree, tvb, off, optlen,
745                                 "NIS servers address: malformed option");
746                         break;
747                 }
748                 for (i = 0; i < optlen; i += 16) {
749                         tvb_get_ipv6(tvb, off + i, &in6);
750                         proto_tree_add_text(subtree, tvb, off + i,
751                                 sizeof(in6), "NIS servers address: %s",
752                                 ip6_to_str(&in6));
753                 }
754                 break;
755         case OPTION_NISP_SERVERS:
756                 if (optlen % 16) {
757                         proto_tree_add_text(subtree, tvb, off, optlen,
758                                 "NISP servers address: malformed option");
759                         break;
760                 }
761                 for (i = 0; i < optlen; i += 16) {
762                         tvb_get_ipv6(tvb, off + i, &in6);
763                         proto_tree_add_text(subtree, tvb, off + i,
764                                 sizeof(in6), "NISP servers address: %s",
765                                 ip6_to_str(&in6));
766                 }
767                 break;
768         case OPTION_NIS_DOMAIN_NAME:
769           if (optlen > 0) {
770             proto_tree_add_text(subtree, tvb, off, optlen, "nis-domain-name");
771           }
772           dhcpv6_domain(subtree,tvb, off, optlen);
773           break;
774         case OPTION_NISP_DOMAIN_NAME:
775           if (optlen > 0) {
776             proto_tree_add_text(subtree, tvb, off, optlen, "nisp-domain-name");
777           }
778           dhcpv6_domain(subtree,tvb, off, optlen);
779           break;
780         case OPTION_SNTP_SERVERS:
781                 if (optlen % 16) {
782                         proto_tree_add_text(subtree, tvb, off, optlen,
783                                 "SNTP servers address: malformed option");
784                         break;
785                 }
786                 for (i = 0; i < optlen; i += 16) {
787                         tvb_get_ipv6(tvb, off + i, &in6);
788                         proto_tree_add_text(subtree, tvb, off + i,
789                                 sizeof(in6), "SNTP servers address: %s",
790                                 ip6_to_str(&in6));
791                 }
792                 break;
793         case OPTION_LIFETIME:
794           if (optlen != 4) {
795             proto_tree_add_text(subtree, tvb, off,
796                                 optlen, "LIFETIME: malformed option");
797             break;
798           }
799           proto_tree_add_text(subtree, tvb, off, 4,
800                               "Lifetime: %d",
801                               (guint32)tvb_get_ntohl(tvb, off));
802           break;
803         case OPTION_BCMCS_SERVER_D:
804                 if (optlen > 0) {
805                         proto_tree_add_text(subtree, tvb, off, optlen,
806                                 "BCMCS Servers Domain Search List");
807                 }
808                 dhcpv6_domain(subtree,tvb, off, optlen);
809                 break;
810         case OPTION_BCMCS_SERVER_A:
811                 if (optlen % 16) {
812                         proto_tree_add_text(subtree, tvb, off, optlen,
813                                 "BCMCS servers address: malformed option");
814                         break;
815                 }
816                 for (i = 0; i < optlen; i += 16) {
817                         tvb_get_ipv6(tvb, off + i, &in6);
818                         proto_tree_add_text(subtree, tvb, off + i,
819                                 sizeof(in6), "BCMCS servers address: %s",
820                                 ip6_to_str(&in6));
821                 }
822                 break;
823         case OPTION_REMOTE_ID:
824           if (optlen < 4) {
825             proto_tree_add_text(subtree, tvb, off,
826                                 optlen, "REMOTE_ID: malformed option");
827             break;
828           }
829           dhcpv6_enterprise_number(subtree, tvb, off);
830           off += 4;
831           optlen -= 4;
832           buf = tvb_bytes_to_str(tvb, off, optlen);
833           proto_tree_add_text(subtree, tvb, off, optlen, "Remote-ID: %s", buf);
834           break;
835         case OPTION_SUBSCRIBER_ID:
836           if (optlen == 0) {
837             proto_tree_add_text(subtree, tvb, off,
838                                 optlen, "SUBSCRIBER_ID: malformed option");
839             break;
840           }
841           buf = tvb_get_ephemeral_string(tvb, off, optlen);
842           proto_tree_add_text(subtree, tvb, off, optlen, "Subscriber-ID: %s", buf);
843           break;
844         case OPTION_CLIENT_FQDN:
845           if (optlen < 1) {
846             proto_tree_add_text(subtree, tvb, off,
847                                 optlen, "FQDN: malformed option");
848             break;
849           }
850           /*
851            * +-----+-+-+-+
852            * | MBZ |N|O|S|
853            * +-----+-+-+-+
854            */
855           proto_tree_add_item(subtree, hf_fqdn_1, tvb, off, 1, FALSE);
856           proto_tree_add_item(subtree, hf_fqdn_2, tvb, off, 1, FALSE);
857           proto_tree_add_item(subtree, hf_fqdn_3, tvb, off, 1, FALSE);
858           proto_tree_add_item(subtree, hf_fqdn_4, tvb, off, 1, FALSE);
859 /*        proto_tree_add_text(subtree, tvb, off, 1, */
860 /*                            "flags: %d", */
861 /*                            (guint32)tvb_get_guint8(tvb, off)); */
862           dhcpv6_domain(subtree,tvb, off+1, (guint16) (optlen-1));
863           break;
864         case OPTION_PANA_AGENT:
865                 if (optlen % 16) {
866                         proto_tree_add_text(subtree, tvb, off, optlen,
867                                 "PANA agent address: malformed option");
868                         break;
869                 }
870                 for (i = 0; i < optlen; i += 16) {
871                         tvb_get_ipv6(tvb, off + i, &in6);
872                         proto_tree_add_text(subtree, tvb, off + i,
873                                 sizeof(in6), "PANA agents address: %s",
874                                 ip6_to_str(&in6));
875                 }
876                 break;
877         case OPTION_TIME_ZONE:
878           if (optlen > 0) {
879               buf = tvb_get_ephemeral_string(tvb, off, optlen);
880               proto_tree_add_text(subtree, tvb, off, optlen, "time-zone: %s", buf);
881           }
882           break;
883         case OPTION_TZDB:
884           if (optlen > 0) {
885               buf = tvb_get_ephemeral_string(tvb, off, optlen);
886               proto_tree_add_text(subtree, tvb, off, optlen, "tz-database: %s", buf);
887           }
888           break;
889         case OPTION_LQ_QUERY:
890             {
891                 guint8 query_type;
892                 struct e_in6_addr in6_local;
893
894                 if (optlen < 17) {
895                     proto_tree_add_text(subtree, tvb, off, optlen,
896                                         "LQ-QUERY: malformed option");
897                     break;
898                 }
899                 query_type = tvb_get_guint8(tvb, off);
900                 switch (query_type) {
901                 case 1:
902                      proto_tree_add_text(subtree, tvb, off, 1,
903                                          "Query-type: %s (%u)",
904                                          "by-address", query_type);
905                      break;
906                 case 2:
907                      proto_tree_add_text(subtree, tvb, off, 1,
908                                          "Query-type: %s (%u)",
909                                          "by-clientID", query_type);
910                      break;
911                 default:
912                      proto_tree_add_text(subtree, tvb, off, 1,
913                                          "Query-type: %s (%u)",
914                                          "unknown?", query_type);
915                      break;
916                 }
917                 tvb_get_ipv6(tvb, off + 1, &in6_local);
918                 proto_tree_add_text(subtree, tvb, off + 1, 16,
919                                     "Link address: %s", ip6_to_str(&in6_local));
920                 temp_optlen = 17;
921                 while ((optlen - temp_optlen) > 0) {
922                     temp_optlen += dhcpv6_option(tvb, pinfo, subtree,
923                                         downstream, off + temp_optlen,
924                                         off + optlen, at_end);
925                     if (*at_end) {
926                         /* Bad option - just skip to the end */
927                         temp_optlen = optlen;
928                     }
929                 }
930             }
931             break;
932         case OPTION_CLIENT_DATA:
933             temp_optlen = 0;
934             while ((optlen - temp_optlen) > 0) {
935                 temp_optlen += dhcpv6_option(tvb, pinfo, subtree,
936                                              downstream, off + temp_optlen,
937                                              off + optlen, at_end);
938                 if (*at_end) {
939                     /* Bad option - just skip to the end */
940                     temp_optlen = optlen;
941                 }
942             }
943             break;
944         case OPTION_CLT_TIME:
945             if (optlen != 4) {
946                 proto_tree_add_text(subtree, tvb, off, optlen,
947                                     "CLT_TIME: malformed option");
948                 break;
949             }
950             proto_tree_add_text(subtree, tvb, off, 4,
951                                 "Clt_time: %d",
952                                 (guint32)tvb_get_ntohl(tvb, off));
953             break;
954         case OPTION_LQ_RELAY_DATA:
955             if (optlen < 16) {
956                 proto_tree_add_text(subtree, tvb, off, optlen,
957                                     "LQ_RELAY_DATA: malformed option");
958                 break;
959             }
960             tvb_get_ipv6(tvb, off, &in6);
961             proto_tree_add_text(subtree, tvb, off, 16,
962                                 "Peer address: %s", ip6_to_str(&in6));
963             proto_tree_add_text(subtree, tvb, off + 16, optlen - 16,
964                                 "DHCPv6 relay message");
965             break;
966         case OPTION_LQ_CLIENT_LINK:
967                 if (optlen % 16) {
968                         proto_tree_add_text(subtree, tvb, off, optlen,
969                                 "LQ client links address: malformed option");
970                         break;
971                 }
972                 for (i = 0; i < optlen; i += 16) {
973                         tvb_get_ipv6(tvb, off + i, &in6);
974                         proto_tree_add_text(subtree, tvb, off + i,
975                                 sizeof(in6), "LQ client links address: %s",
976                                 ip6_to_str(&in6));
977                 }
978                 break;
979         case OPTION_CAPWAP_AC_V6:
980                 if (optlen % 16) {
981                         proto_tree_add_text(subtree, tvb, off, optlen,
982                                 "CAPWAP Access Controllers address: malformed option");
983                         break;
984                 }
985                 for (i = 0; i < optlen; i += 16) {
986                         tvb_get_ipv6(tvb, off + i, &in6);
987                         proto_tree_add_text(subtree, tvb, off + i,
988                                 sizeof(in6), "CAPWAP Access Controllers address: %s",
989                                 ip6_to_str(&in6));
990                 }
991                 break;
992         case OPTION_IAPREFIX:
993             {
994                 guint32 preferred_lifetime, valid_lifetime;
995                 guint8  prefix_length;
996                 struct e_in6_addr in6_local;
997
998                 if (optlen < 25) {
999                    proto_tree_add_text(subtree, tvb, off,
1000                                        optlen, "IAPREFIX: malformed option");
1001                    break;
1002                 }
1003
1004                 preferred_lifetime = tvb_get_ntohl(tvb, off);
1005                 valid_lifetime = tvb_get_ntohl(tvb, off + 4);
1006                 prefix_length  = tvb_get_guint8(tvb, off + 8);
1007                 if (preferred_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
1008                         proto_tree_add_text(subtree, tvb, off, 4,
1009                                     "Preferred lifetime: infinity");
1010                 } else {
1011                         proto_tree_add_text(subtree, tvb, off, 4,
1012                                     "Preferred lifetime: %u", preferred_lifetime);
1013                 }
1014                 if (valid_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
1015                         proto_tree_add_text(subtree, tvb, off + 4, 4,
1016                                     "Valid lifetime: infinity");
1017                 } else {
1018                         proto_tree_add_text(subtree, tvb, off + 4, 4,
1019                                     "Valid lifetime: %u", valid_lifetime);
1020                 }
1021                 proto_tree_add_text(subtree, tvb, off + 8, 1,
1022                                     "Prefix length: %d", prefix_length);
1023                 tvb_get_ipv6(tvb, off + 9, &in6_local);
1024                 proto_tree_add_text(subtree, tvb, off + 9,
1025                                     16, "Prefix address: %s",
1026                                     ip6_to_str(&in6_local));
1027                 
1028                 temp_optlen = 25;
1029                 while ((optlen - temp_optlen) > 0) {
1030                    temp_optlen += dhcpv6_option(tvb, pinfo, subtree, downstream,
1031                                    off+temp_optlen, off + optlen, at_end);
1032                    if (*at_end) {
1033                      /* Bad option - just skip to the end */
1034                      temp_optlen = optlen;
1035                    }
1036                 }
1037             }
1038             break;
1039         case OPTION_MIP6_HA:
1040                 if (optlen != 16) {
1041                         proto_tree_add_text(subtree, tvb, off, optlen,
1042                                 "MIP6_HA: malformed option");
1043                         break;
1044                 }
1045
1046                 tvb_get_ipv6(tvb, off, &in6);
1047                 proto_tree_add_text(subtree, tvb, off,
1048                         16, "Home Agent: %s", ip6_to_str(&in6));
1049                 break;
1050         case OPTION_MIP6_HOA:
1051                 if (optlen != 16) {
1052                         proto_tree_add_text(subtree, tvb, off, optlen,
1053                                 "MIP6_HOA: malformed option");
1054                         break;
1055                 }
1056
1057                 tvb_get_ipv6(tvb, off, &in6);
1058                 proto_tree_add_text(subtree, tvb, off,
1059                         16, "Home Address: %s", ip6_to_str(&in6));
1060                 break;
1061         case OPTION_NAI:
1062                 if (optlen < 4) {
1063                         proto_tree_add_text(subtree, tvb, off, optlen,
1064                                 "NAI: malformed option");
1065                         break;
1066                 }
1067                 proto_tree_add_text(subtree, tvb, off, optlen,
1068                         "NAI : %s", tvb_get_ptr(tvb, off, optlen - 2));
1069                 break;
1070         }
1071
1072         return 4 + optlen;
1073 }
1074
1075
1076 static void
1077 dissect_dhcpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
1078     gboolean downstream, int off, int eoff)
1079 {
1080         proto_tree *bp_tree = NULL;
1081         proto_item *ti;
1082         guint8 msgtype, hop_count ;
1083         guint32 xid;
1084         struct e_in6_addr in6;
1085         gboolean at_end;
1086
1087         downstream = 0; /* feature reserved */
1088
1089         msgtype = tvb_get_guint8(tvb, off);
1090
1091         if (tree) {
1092                 ti = proto_tree_add_item(tree, proto_dhcpv6, tvb, 0, -1, FALSE);
1093                 bp_tree = proto_item_add_subtree(ti, ett_dhcpv6);
1094         }
1095
1096         if (msgtype == RELAY_FORW || msgtype == RELAY_REPLY) {
1097            
1098            if (!off) {
1099               if (check_col(pinfo->cinfo, COL_INFO)) {
1100                  col_add_str(pinfo->cinfo, COL_INFO,
1101                              val_to_str(msgtype,
1102                                         msgtype_vals,
1103                                         "Message Type %u"));
1104               }
1105            }
1106
1107            proto_tree_add_uint(bp_tree, hf_dhcpv6_msgtype, tvb, off, 1, msgtype);
1108
1109            hop_count = tvb_get_guint8(tvb, off+1);
1110            proto_tree_add_text(bp_tree, tvb, off+1, 1, "Hop count: %d", hop_count);
1111
1112            tvb_get_ipv6(tvb, off+2, &in6);
1113            proto_tree_add_text(bp_tree, tvb, off+2, sizeof(in6), 
1114                                "Link-address: %s",ip6_to_str(&in6));
1115
1116            tvb_get_ipv6(tvb, off+18, &in6);
1117            proto_tree_add_text(bp_tree, tvb, off+18, sizeof(in6), 
1118                                "Peer-address: %s",ip6_to_str(&in6));
1119
1120            off += 34;
1121         } else {
1122         
1123            xid = tvb_get_ntohl(tvb, off) & 0x00ffffff;
1124
1125            if (!off) {
1126               if (check_col(pinfo->cinfo, COL_INFO)) {
1127                  col_add_str(pinfo->cinfo, COL_INFO,
1128                              val_to_str(msgtype,
1129                                         msgtype_vals,
1130                                         "Message Type %u"));
1131               }
1132            }
1133
1134            if (tree) {
1135                    proto_tree_add_uint(bp_tree, hf_dhcpv6_msgtype, tvb, off, 1,
1136                            msgtype);
1137                    proto_tree_add_text(bp_tree, tvb, off+1, 3, "Transaction-ID: 0x%08x", xid);
1138 #if 0
1139                    tvb_get_ipv6(tvb, 4, &in6);
1140                    proto_tree_add_text(bp_tree, tvb, 4, sizeof(in6),
1141                            "Server address: %s", ip6_to_str(&in6));
1142 #endif
1143            }
1144
1145            off += 4;
1146         }
1147
1148         at_end = FALSE;
1149         while (off < eoff && !at_end)
1150                 off += dhcpv6_option(tvb, pinfo, bp_tree, downstream, off, eoff, &at_end);
1151 }
1152
1153 static void
1154 dissect_dhcpv6_downstream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1155 {
1156         if (check_col(pinfo->cinfo, COL_PROTOCOL))
1157                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DHCPv6");
1158         if (check_col(pinfo->cinfo, COL_INFO))
1159                 col_clear(pinfo->cinfo, COL_INFO);
1160         dissect_dhcpv6(tvb, pinfo, tree, TRUE, 0, tvb_reported_length(tvb));
1161 }
1162
1163 static void
1164 dissect_dhcpv6_upstream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1165 {
1166         if (check_col(pinfo->cinfo, COL_PROTOCOL))
1167                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DHCPv6");
1168         if (check_col(pinfo->cinfo, COL_INFO))
1169                 col_clear(pinfo->cinfo, COL_INFO);
1170         dissect_dhcpv6(tvb, pinfo, tree, FALSE, 0, tvb_reported_length(tvb));
1171 }
1172
1173
1174 void
1175 proto_register_dhcpv6(void)
1176 {
1177   static hf_register_info hf[] = {
1178
1179     { &hf_dhcpv6_msgtype,
1180       { "Message type",                 "dhcpv6.msgtype",        FT_UINT8,
1181          BASE_DEC,                      VALS(msgtype_vals),   0x0,
1182         "", HFILL }},
1183     { &hf_fqdn_1,
1184       { "Reserved", "dhcpv6.msgtype.reserved", FT_UINT8, BASE_HEX, NULL, 0xF8, "", HFILL}},
1185     { &hf_fqdn_2,
1186       { "N", "dhcpv6.msgtype.n", FT_BOOLEAN, 8, TFS(&fqdn_n), 0x4, "", HFILL}},
1187     { &hf_fqdn_3,
1188       { "O", "dhcpv6.msgtype.o", FT_BOOLEAN, 8, TFS(&fqdn_o), 0x2, "", HFILL}},
1189     { &hf_fqdn_4,
1190       { "S", "dhcpv6.msgtype.s", FT_BOOLEAN, 8, TFS(&fqdn_s), 0x1, "", HFILL}}
1191     
1192   };
1193   static gint *ett[] = {
1194     &ett_dhcpv6,
1195     &ett_dhcpv6_option,
1196     &ett_dhcpv6_option_vsoption,
1197   };
1198
1199   proto_dhcpv6 = proto_register_protocol("DHCPv6", "DHCPv6", "dhcpv6");
1200   proto_register_field_array(proto_dhcpv6, hf, array_length(hf));
1201   proto_register_subtree_array(ett, array_length(ett));
1202
1203   /* Allow other dissectors to find this one by name.
1204      Just choose upstream version for now as they are identical. */
1205   register_dissector("dhcpv6", dissect_dhcpv6_upstream, proto_dhcpv6);
1206 }
1207
1208 void
1209 proto_reg_handoff_dhcpv6(void)
1210 {
1211   dissector_handle_t dhcpv6_handle;
1212
1213   dhcpv6_handle = create_dissector_handle(dissect_dhcpv6_downstream,
1214         proto_dhcpv6);
1215   dissector_add("udp.port", UDP_PORT_DHCPV6_DOWNSTREAM, dhcpv6_handle);
1216   dhcpv6_handle = create_dissector_handle(dissect_dhcpv6_upstream,
1217         proto_dhcpv6);
1218   dissector_add("udp.port", UDP_PORT_DHCPV6_UPSTREAM, dhcpv6_handle);
1219 }