5291e6091377ff5a0ab4510cfa0e041001ea8560
[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  * draft-ietf-dhc-dhcpv6-opt-timeconfig-03.txt
18  * draft-ietf-dhc-dhcpv6-opt-fqdn-00.txt
19  * draft-ietf-dhc-dhcpv6-opt-lifetime-00.txt
20  *
21  * Note that protocol constants are still subject to change, based on IANA
22  * assignment decisions.
23  *
24  * Wireshark - Network traffic analyzer
25  * By Gerald Combs <gerald@wireshark.org>
26  * Copyright 1998 Gerald Combs
27  *
28  * This program is free software; you can redistribute it and/or
29  * modify it under the terms of the GNU General Public License
30  * as published by the Free Software Foundation; either version 2
31  * of the License, or (at your option) any later version.
32  *
33  * This program is distributed in the hope that it will be useful,
34  * but WITHOUT ANY WARRANTY; without even the implied warranty of
35  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36  * GNU General Public License for more details.
37  *
38  * You should have received a copy of the GNU General Public License
39  * along with this program; if not, write to the Free Software
40  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
41  */
42
43 #ifdef HAVE_CONFIG_H
44 # include "config.h"
45 #endif
46
47 #include <string.h>
48 #include <glib.h>
49 #include <epan/packet.h>
50 #include "packet-arp.h"
51
52 static int proto_dhcpv6 = -1;
53 static int hf_dhcpv6_msgtype = -1;
54 static int hf_fqdn_1 = -1;
55 static int hf_fqdn_2 = -1;
56 static int hf_fqdn_3 = -1;
57 static int hf_fqdn_4 = -1;
58
59 static gint ett_dhcpv6 = -1;
60 static gint ett_dhcpv6_option = -1;
61
62 #define UDP_PORT_DHCPV6_DOWNSTREAM      546
63 #define UDP_PORT_DHCPV6_UPSTREAM        547
64
65 #define DHCPV6_LEASEDURATION_INFINITY   0xffffffff
66
67 #define SOLICIT                 1
68 #define ADVERTISE               2
69 #define REQUEST                 3
70 #define CONFIRM                 4
71 #define RENEW                   5
72 #define REBIND                  6
73 #define REPLY                   7
74 #define RELEASE                 8
75 #define DECLINE                 9
76 #define RECONFIGURE             10
77 #define INFORMATION_REQUEST     11
78 #define RELAY_FORW              12
79 #define RELAY_REPLY             13
80
81 #define OPTION_CLIENTID         1
82 #define OPTION_SERVERID         2
83 #define OPTION_IA_NA            3
84 #define OPTION_IA_TA            4
85 #define OPTION_IAADDR           5
86 #define OPTION_ORO              6
87 #define OPTION_PREFERENCE       7
88 #define OPTION_ELAPSED_TIME     8
89 #define OPTION_RELAY_MSG        9
90 /* #define      OPTION_SERVER_MSG       10 */
91 #define OPTION_AUTH             11
92 #define OPTION_UNICAST          12
93 #define OPTION_STATUS_CODE      13
94 #define OPTION_RAPID_COMMIT     14
95 #define OPTION_USER_CLASS       15
96 #define OPTION_VENDOR_CLASS     16
97 #define OPTION_VENDOR_OPTS      17
98 #define OPTION_INTERFACE_ID     18
99 #define OPTION_RECONF_MSG       19
100 #define OPTION_RECONF_ACCEPT    20
101 #define OPTION_SIP_SERVER_D     21
102 #define OPTION_SIP_SERVER_A     22
103 #define OPTION_DNS_SERVERS      23
104 #define OPTION_DOMAIN_LIST      24
105 #define OPTION_IA_PD            25
106 #define OPTION_IAPREFIX         26
107 #define OPTION_NIS_SERVERS      27
108 #define OPTION_NISP_SERVERS     28
109 #define OPTION_NIS_DOMAIN_NAME  29
110 #define OPTION_NISP_DOMAIN_NAME 30
111
112 /*
113  * The followings are unassigned numbers.
114  */
115 #define OPTION_CLIENT_FQDN      34
116 #define OPTION_SNTP_SERVERS     40
117 #define OPTION_TIME_ZONE        41
118 #define OPTION_LIFETIME         42
119
120 /* temporary value until defined by IETF */
121 #define OPTION_MIP6_HA          165
122 #define OPTION_MIP6_HOA         166
123 #define OPTION_NAI              167
124
125 #define DUID_LLT                1
126 #define DUID_EN                 2
127 #define DUID_LL                 3
128 #define DUID_LL_OLD             4
129
130 static void
131 dissect_dhcpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
132     gboolean downstream, int off, int eoff);
133
134 static const value_string msgtype_vals[] = {
135         { SOLICIT,      "Solicit" },
136         { ADVERTISE,    "Advertise" },
137         { REQUEST,      "Request" },
138         { CONFIRM,      "Confirm" },
139         { RENEW,        "Renew" },
140         { REBIND,       "Rebind" },
141         { REPLY,        "Reply" },
142         { RELEASE,      "Release" },
143         { DECLINE,      "Decline" },
144         { RECONFIGURE,  "Reconfigure" },
145         { INFORMATION_REQUEST,  "Information-request" },
146         { RELAY_FORW,   "Relay-forw" },
147         { RELAY_REPLY,  "Relay-reply" },
148         { 0, NULL }
149 };
150
151 static const value_string opttype_vals[] = {
152         { OPTION_CLIENTID,      "Client Identifier" },
153         { OPTION_SERVERID,      "Server Identifier" },
154         { OPTION_IA_NA,         "Identity Association for Non-temporary Address" },
155         { OPTION_IA_TA,         "Identity Association for Temporary Address" },
156         { OPTION_IAADDR,        "IA Address" },
157         { OPTION_ORO,           "Option Request" },
158         { OPTION_PREFERENCE,    "Preference" },
159         { OPTION_ELAPSED_TIME,  "Elapsed time" },
160         { OPTION_RELAY_MSG,     "Relay Message" },
161 /*      { OPTION_SERVER_MSG,    "Server message" }, */
162         { OPTION_AUTH,          "Authentication" },
163         { OPTION_UNICAST,       "Server unicast" },
164         { OPTION_STATUS_CODE,   "Status code" },
165         { OPTION_RAPID_COMMIT,  "Rapid Commit" },
166         { OPTION_USER_CLASS,    "User Class" },
167         { OPTION_VENDOR_CLASS,  "Vendor Class" },
168         { OPTION_VENDOR_OPTS,   "Vendor-specific Information" },
169         { OPTION_INTERFACE_ID,  "Interface-Id" },
170         { OPTION_RECONF_MSG,    "Reconfigure Message" },
171         { OPTION_RECONF_ACCEPT, "Reconfigure Accept" },
172         { OPTION_SIP_SERVER_D,  "SIP Server Domain Name List" },
173         { OPTION_SIP_SERVER_A,  "SIP Servers IPv6 Address List" },
174         { OPTION_DNS_SERVERS,   "DNS recursive name server" },
175         { OPTION_DOMAIN_LIST,   "Domain Search List" },
176         { OPTION_IA_PD,         "Identity Association for Prefix Delegation" },
177         { OPTION_IAPREFIX,      "IA Prefix" },
178         { OPTION_NIS_SERVERS,   "Network Information Server" },
179         { OPTION_NISP_SERVERS,  "Network Information Server V2" },
180         { OPTION_NIS_DOMAIN_NAME, "Network Information Server Domain Name" },
181         { OPTION_NISP_DOMAIN_NAME,"Network Information Server V2 Domain Name" },
182         { OPTION_SNTP_SERVERS,  "Simple Network Time Protocol Server" },
183         { OPTION_TIME_ZONE,     "Time zone" },
184         { OPTION_LIFETIME,      "Lifetime" },
185         { OPTION_CLIENT_FQDN,   "Fully Qualified Domain Name" },
186         { OPTION_MIP6_HA,       "Mobile IPv6 Home Agent" },
187         { OPTION_MIP6_HOA,      "Mobile IPv6 Home Address" },
188         { OPTION_NAI,           "Network Access Identifier" },
189         { 0,    NULL }
190 };
191
192 static const value_string statuscode_vals[] =
193 {
194         {0, "Success" },
195         {1, "UnspecFail" },
196         {2, "NoAddrAvail" },
197         {3, "NoBinding" },
198         {4, "NotOnLink" },
199         {5, "UseMulticast" },
200         {6, "NoPrefixAvail" },
201         {0, NULL }
202 };
203
204 static const value_string duidtype_vals[] =
205 {
206         { DUID_LLT,     "link-layer address plus time" },
207         { DUID_EN,      "assigned by vendor based on Enterprise number" },
208         { DUID_LL,      "link-layer address" },
209         { DUID_LL_OLD,  "link-layer address (old)" },
210         { 0, NULL }
211 };
212
213 /* This FQDN draft is a mess, I've tried to understand, 
214    but N,O,S bit descriptions are really cryptic */
215 static const true_false_string fqdn_n = {
216 /*    "Client doesn't want server to perform DNS update", "" */
217     "N bit set","N bit cleared"
218 };
219
220 static const true_false_string fqdn_o = {
221     "O bit set", "O bit cleared" 
222 };
223
224 static const true_false_string fqdn_s = {
225 /*    "Forward mapping (FQDN-to-IPv6, AAAA) performed by client", 
226       "Forward mapping (FQDN-to-IPv6, AAAA) performed by server" */
227     "S bit set", "S bit cleared"
228 }; 
229
230 /* Adds domain */
231 static void
232 dhcpv6_domain(proto_tree * subtree, tvbuff_t *tvb, int offset, guint16 optlen)
233 {
234     int start_offset=offset;
235     char domain[256];
236     int pos;
237     guint8 len;
238
239     pos=0;
240     while(optlen){
241         /* this is the start of the domain name */
242         if(!pos){
243             start_offset=offset;
244         }
245         domain[pos]=0;
246
247         /* read length of the next substring */
248         len = tvb_get_guint8(tvb, offset);
249         offset++;
250         optlen--;
251
252         /* if len==0 and pos>0 we have read an entire domain string */
253         if(!len){
254             if(!pos){
255                 /* empty string, this must be an error? */
256                 proto_tree_add_text(subtree, tvb, start_offset, offset-start_offset, "Malformed option");
257                 return;
258             } else {
259                 proto_tree_add_text(subtree, tvb, start_offset, offset-start_offset, "Domain: %s", domain);
260                 pos=0;
261                 continue;
262             }
263         }
264
265         /* add the substring to domain */
266         if(pos){
267             domain[pos]='.';
268             pos++;
269         }
270         if(pos+len>254){
271                 /* too long string, this must be an error? */
272                 proto_tree_add_text(subtree, tvb, start_offset, offset-start_offset, "Malformed option");
273                 return;
274         }
275         tvb_memcpy(tvb, domain+pos, offset, len);
276         pos+=len;
277         offset+=len;
278         optlen-=len;
279     }        
280     
281     if(pos){
282         domain[pos]=0;
283         proto_tree_add_text(subtree, tvb, start_offset, offset-start_offset, "Domain: %s", domain);
284     }
285 }    
286
287 /* Returns the number of bytes consumed by this option. */
288 static int
289 dhcpv6_option(tvbuff_t *tvb, packet_info *pinfo, proto_tree *bp_tree, 
290                 gboolean downstream, int off, int eoff, gboolean *at_end)
291 {
292         guint8 *buf;
293         guint16 opttype;
294         guint16 optlen;
295         guint16 hwtype;
296         guint16 temp_optlen = 0;
297         proto_item *ti;
298         proto_tree *subtree;
299         int i;
300         struct e_in6_addr in6;
301         guint16 duidtype;
302
303         /* option type and length must be present */
304         if (eoff - off < 4) {
305                 *at_end = TRUE;
306                 return 0;
307         }
308
309         opttype = tvb_get_ntohs(tvb, off);
310         optlen = tvb_get_ntohs(tvb, off + 2);
311
312         /* all option data must be present */
313         if (eoff - off < 4 + optlen) {
314                 *at_end = TRUE;
315                 return 0;
316         }
317
318         ti = proto_tree_add_text(bp_tree, tvb, off, 4 + optlen,
319                 "%s", val_to_str(opttype, opttype_vals, "DHCP option %u"));
320
321         subtree = proto_item_add_subtree(ti, ett_dhcpv6_option);
322         proto_tree_add_text(subtree, tvb, off, 2, "option type: %d", opttype);
323         proto_tree_add_text(subtree, tvb, off + 2, 2, "option length: %d",
324                 optlen);
325
326         off += 4;
327         switch (opttype) {
328         case OPTION_CLIENTID:
329         case OPTION_SERVERID:
330                 if (optlen < 2) {
331                         proto_tree_add_text(subtree, tvb, off, optlen,
332                                 "DUID: malformed option");
333                         break;
334                 }
335                 duidtype = tvb_get_ntohs(tvb, off);
336                 proto_tree_add_text(subtree, tvb, off, 2,
337                         "DUID type: %s (%u)",
338                                     val_to_str(duidtype,
339                                                duidtype_vals, "Unknown"),
340                                     duidtype);
341                 switch (duidtype) {
342                 case DUID_LLT:
343                         if (optlen < 8) {
344                                 proto_tree_add_text(subtree, tvb, off,
345                                         optlen, "DUID: malformed option");
346                                 break;
347                         }
348                         hwtype=tvb_get_ntohs(tvb, off + 2);
349                         proto_tree_add_text(subtree, tvb, off + 2, 2,
350                                 "Hardware type: %s (%u)",
351                                 arphrdtype_to_str(hwtype, "Unknown"),
352                                 hwtype);
353                         /* XXX seconds since Jan 1 2000 */
354                         proto_tree_add_text(subtree, tvb, off + 4, 4,
355                                 "Time: %u", tvb_get_ntohl(tvb, off + 4));
356                         if (optlen > 8) {
357                                 proto_tree_add_text(subtree, tvb, off + 8,
358                                         optlen - 8, "Link-layer address: %s",
359                                         arphrdaddr_to_str(tvb_get_ptr(tvb, off+8, optlen-8), optlen-8, hwtype));
360                         }
361                         break;
362                 case DUID_EN:
363                         if (optlen < 6) {
364                                 proto_tree_add_text(subtree, tvb, off,
365                                         optlen, "DUID: malformed option");
366                                 break;
367                         }
368                         proto_tree_add_text(subtree, tvb, off + 2, 4,
369                                             "enterprise-number");
370                         if (optlen > 6) {
371                                 proto_tree_add_text(subtree, tvb, off + 6,
372                                         optlen - 6, "identifier");
373                         }
374                         break;
375                 case DUID_LL:
376                 case DUID_LL_OLD:
377                         if (optlen < 4) {
378                                 proto_tree_add_text(subtree, tvb, off,
379                                         optlen, "DUID: malformed option");
380                                 break;
381                         }
382                         hwtype=tvb_get_ntohs(tvb, off + 2);
383                         proto_tree_add_text(subtree, tvb, off + 2, 2,
384                                 "Hardware type: %s (%u)",
385                                 arphrdtype_to_str(hwtype, "Unknown"),
386                                 hwtype);
387                         if (optlen > 4) {
388                                 proto_tree_add_text(subtree, tvb, off + 4,
389                                         optlen - 4, "Link-layer address: %s",
390                                         arphrdaddr_to_str(tvb_get_ptr(tvb, off+4, optlen-4), optlen-4, hwtype));
391                         }
392                         break;
393                 }
394                 break;
395         case OPTION_IA_NA:
396         case OPTION_IA_PD:
397           if (optlen < 12) {
398              if (opttype == OPTION_IA_NA)
399                 proto_tree_add_text(subtree, tvb, off,
400                                     optlen, "IA_NA: malformed option");
401              else
402                 proto_tree_add_text(subtree, tvb, off,
403                                     optlen, "IA_PD: malformed option");
404              break;
405           }
406           proto_tree_add_text(subtree, tvb, off, 4,
407                               "IAID: %u",
408                               tvb_get_ntohl(tvb, off));
409           if (tvb_get_ntohl(tvb, off+4) == DHCPV6_LEASEDURATION_INFINITY) {
410               proto_tree_add_text(subtree, tvb, off+4, 4,
411                                   "T1: infinity");
412           } else {
413               proto_tree_add_text(subtree, tvb, off+4, 4,
414                                   "T1: %u", tvb_get_ntohl(tvb, off+4));
415           }
416
417           if (tvb_get_ntohl(tvb, off+8) == DHCPV6_LEASEDURATION_INFINITY) {
418               proto_tree_add_text(subtree, tvb, off+8, 4,
419                                   "T2: infinity");
420           } else {
421               proto_tree_add_text(subtree, tvb, off+8, 4,
422                                   "T2: %u", tvb_get_ntohl(tvb, off+8));
423           }
424
425           temp_optlen = 12;
426           while ((optlen - temp_optlen) > 0) {
427             temp_optlen += dhcpv6_option(tvb, pinfo, subtree, downstream,
428                             off+temp_optlen, off + optlen, at_end);
429             if (*at_end) {
430               /* Bad option - just skip to the end */
431               temp_optlen = optlen;
432             }
433           }
434           break;
435         case OPTION_IA_TA:
436           if (optlen < 4) {
437             proto_tree_add_text(subtree, tvb, off,
438                                 optlen, "IA_TA: malformed option");
439             break;
440           }
441           proto_tree_add_text(subtree, tvb, off, 4,
442                               "IAID: %u",
443                               tvb_get_ntohl(tvb, off));
444           temp_optlen = 4;
445           while ((optlen - temp_optlen) > 0) {
446             temp_optlen += dhcpv6_option(tvb, pinfo, subtree, downstream,
447                             off+temp_optlen, off + optlen, at_end);
448             if (*at_end) {
449               /* Bad option - just skip to the end */
450               temp_optlen = optlen;
451             }
452           }
453           break;
454         case OPTION_IAADDR:
455         {
456            guint32 preferred_lifetime, valid_lifetime;
457
458            if (optlen < 24) {
459               proto_tree_add_text(subtree, tvb, off,
460                                   optlen, "IAADDR: malformed option");
461               break;
462            }
463            tvb_get_ipv6(tvb, off, &in6);
464            proto_tree_add_text(subtree, tvb, off,
465                                sizeof(in6), "IPv6 address: %s",
466                                ip6_to_str(&in6));
467            
468            preferred_lifetime = tvb_get_ntohl(tvb, off + 16);
469            valid_lifetime = tvb_get_ntohl(tvb, off + 20);
470            
471            if (preferred_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
472               proto_tree_add_text(subtree, tvb, off + 16, 4,
473                                   "Preferred lifetime: infinity");
474            } else {
475               proto_tree_add_text(subtree, tvb, off + 16, 4,
476                                   "Preferred lifetime: %u", preferred_lifetime);
477            }
478            if (valid_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
479               proto_tree_add_text(subtree, tvb, off + 20, 4,
480                                   "Valid lifetime: infinity");
481            } else {
482               proto_tree_add_text(subtree, tvb, off + 20, 4,
483                                   "Valid lifetime: %u", valid_lifetime);
484            }
485            
486            temp_optlen = 24;
487            while ((optlen - temp_optlen) > 0) {
488               temp_optlen += dhcpv6_option(tvb, pinfo, subtree, downstream,
489                               off+temp_optlen, off + optlen, at_end);
490               if (*at_end) {
491                 /* Bad option - just skip to the end */
492                 temp_optlen = optlen;
493               }
494            }
495         }
496         break;
497         case OPTION_ORO:
498                 for (i = 0; i < optlen; i += 2) {
499                     guint16 requested_opt_code;
500                     requested_opt_code = tvb_get_ntohs(tvb, off + i);
501                     proto_tree_add_text(subtree, tvb, off + i,
502                             2, "Requested Option code: %s (%d)",
503                                             val_to_str(requested_opt_code,
504                                                        opttype_vals,
505                                                        "Unknown"),
506                                             requested_opt_code);
507                 }
508                 break;
509         case OPTION_PREFERENCE:
510           if (optlen != 1) {
511             proto_tree_add_text(subtree, tvb, off,
512                                 optlen, "PREFERENCE: malformed option");
513             break;
514           }
515           proto_tree_add_text(subtree, tvb, off, 1,
516                               "pref-value: %d",
517                               (guint32)tvb_get_guint8(tvb, off));
518           break;
519         case OPTION_ELAPSED_TIME:
520           if (optlen != 2) {
521             proto_tree_add_text(subtree, tvb, off,
522                                 optlen, "ELAPSED-TIME: malformed option");
523             break;
524           }
525           proto_tree_add_text(subtree, tvb, off, 2,
526                               "elapsed-time: %d sec",
527                               (guint32)tvb_get_ntohs(tvb, off));
528           break;
529         case OPTION_RELAY_MSG:
530           if (optlen == 0) {
531             proto_tree_add_text(subtree, tvb, off,
532                                 optlen, "RELAY-MSG: malformed option");
533           } else {
534             /* here, we should dissect a full DHCP message */
535             dissect_dhcpv6(tvb, pinfo, subtree, downstream, off, off + optlen);
536           } 
537           break;
538         case OPTION_AUTH:
539           if (optlen < 11) {
540             proto_tree_add_text(subtree, tvb, off,
541                                 optlen, "AUTH: malformed option");
542             break;
543           }
544           proto_tree_add_text(subtree, tvb, off, 1,
545                               "Protocol: %d",
546                               (guint32)tvb_get_guint8(tvb, off));
547           proto_tree_add_text(subtree, tvb, off+1, 1,
548                               "Algorithm: %d",
549                               (guint32)tvb_get_guint8(tvb, off+1));
550           proto_tree_add_text(subtree, tvb, off+2, 1,
551                               "RDM: %d",
552                               (guint32)tvb_get_guint8(tvb, off+2));
553           proto_tree_add_text(subtree, tvb, off+3, 8,
554                               "Replay Detection");
555           if (optlen != 11)
556                 proto_tree_add_text(subtree, tvb, off+11, optlen-11,
557                                                         "Authentication Information");
558           break;
559         case OPTION_UNICAST:
560           if (optlen != 16) {
561             proto_tree_add_text(subtree, tvb, off,
562                                 optlen, "UNICAST: malformed option");
563             break;
564           }
565           tvb_get_ipv6(tvb, off, &in6);
566           proto_tree_add_text(subtree, tvb, off,
567                               sizeof(in6), "IPv6 address: %s",
568                                 ip6_to_str(&in6));
569           break;
570         case OPTION_STATUS_CODE:
571             {
572                 guint16 status_code;
573                 char *status_message = 0;
574                 status_code = tvb_get_ntohs(tvb, off);
575                 proto_tree_add_text(subtree, tvb, off, 2,
576                                     "Status Code: %s (%d)",
577                                     val_to_str(status_code, statuscode_vals,
578                                                "Unknown"),
579                                     status_code);
580
581                 if (optlen - 2 > 0) {
582                     status_message = tvb_get_ephemeral_string(tvb, off + 2, optlen - 2);
583                     proto_tree_add_text(subtree, tvb, off + 2, optlen - 2,
584                                         "Status Message: %s",
585                                         status_message);
586                 }
587             }
588             break;
589         case OPTION_VENDOR_CLASS:
590           if (optlen < 4) {
591             proto_tree_add_text(subtree, tvb, off,
592                                 optlen, "VENDOR_CLASS: malformed option");
593             break;
594           }
595           proto_tree_add_text(subtree, tvb, off, 4,
596                               "enterprise-number: %u",
597                               tvb_get_ntohl(tvb, off));
598           if (optlen > 4) {
599             proto_tree_add_text(subtree, tvb, off+4, optlen-4,
600                                 "vendor-class-data");
601           }
602           break;
603         case OPTION_VENDOR_OPTS:
604           if (optlen < 4) {
605             proto_tree_add_text(subtree, tvb, off,
606                                 optlen, "VENDOR_OPTS: malformed option");
607             break;
608           }
609           proto_tree_add_text(subtree, tvb, off, 4,
610                               "enterprise-number: %u",
611                               tvb_get_ntohl(tvb, off));
612           if (optlen > 4) {
613             proto_tree_add_text(subtree, tvb, off+4, optlen-4,
614                                 "option-data");
615           }
616           break;
617         case OPTION_INTERFACE_ID:
618           if (optlen == 0) {
619             proto_tree_add_text(subtree, tvb, off,
620                                 optlen, "INTERFACE_ID: malformed option");
621             break;
622           }
623           proto_tree_add_text(subtree, tvb, off, optlen, "Interface-ID");
624           break;
625         case OPTION_RECONF_MSG:
626           if (optlen != 1) {
627             proto_tree_add_text(subtree, tvb, off,
628                                 optlen, "RECONF_MSG: malformed option");
629             break;
630           }
631           proto_tree_add_text(subtree, tvb, off, optlen,
632                               "Reconfigure-type: %s",
633                               val_to_str(tvb_get_guint8(tvb, off),
634                                          msgtype_vals,
635                                          "Message Type %u"));
636           break;
637         case OPTION_SIP_SERVER_D:
638                 if (optlen > 0) {
639                         proto_tree_add_text(subtree, tvb, off, optlen,
640                                 "SIP Servers Domain Search List");
641                 }
642                 dhcpv6_domain(subtree,tvb, off, optlen);
643                 break;
644         case OPTION_SIP_SERVER_A:
645                 if (optlen % 16) {
646                         proto_tree_add_text(subtree, tvb, off, optlen,
647                                 "SIP servers address: malformed option");
648                         break;
649                 }
650                 for (i = 0; i < optlen; i += 16) {
651                         tvb_get_ipv6(tvb, off + i, &in6);
652                         proto_tree_add_text(subtree, tvb, off + i,
653                                 sizeof(in6), "SIP servers address: %s",
654                                 ip6_to_str(&in6));
655                 }
656                 break;
657         case OPTION_DNS_SERVERS:
658                 if (optlen % 16) {
659                         proto_tree_add_text(subtree, tvb, off, optlen,
660                                 "DNS servers address: malformed option");
661                         break;
662                 }
663                 for (i = 0; i < optlen; i += 16) {
664                         tvb_get_ipv6(tvb, off + i, &in6);
665                         proto_tree_add_text(subtree, tvb, off + i,
666                                 sizeof(in6), "DNS servers address: %s",
667                                 ip6_to_str(&in6));
668                 }
669                 break;
670         case OPTION_DOMAIN_LIST:
671           if (optlen > 0) {
672             proto_tree_add_text(subtree, tvb, off, optlen, "DNS Domain Search List");
673           }
674           dhcpv6_domain(subtree,tvb, off, optlen);
675           break;
676         case OPTION_NIS_SERVERS:
677                 if (optlen % 16) {
678                         proto_tree_add_text(subtree, tvb, off, optlen,
679                                 "NIS servers address: malformed option");
680                         break;
681                 }
682                 for (i = 0; i < optlen; i += 16) {
683                         tvb_get_ipv6(tvb, off + i, &in6);
684                         proto_tree_add_text(subtree, tvb, off + i,
685                                 sizeof(in6), "NIS servers address: %s",
686                                 ip6_to_str(&in6));
687                 }
688                 break;
689         case OPTION_NISP_SERVERS:
690                 if (optlen % 16) {
691                         proto_tree_add_text(subtree, tvb, off, optlen,
692                                 "NISP servers address: malformed option");
693                         break;
694                 }
695                 for (i = 0; i < optlen; i += 16) {
696                         tvb_get_ipv6(tvb, off + i, &in6);
697                         proto_tree_add_text(subtree, tvb, off + i,
698                                 sizeof(in6), "NISP servers address: %s",
699                                 ip6_to_str(&in6));
700                 }
701                 break;
702         case OPTION_NIS_DOMAIN_NAME:
703           if (optlen > 0) {
704             proto_tree_add_text(subtree, tvb, off, optlen, "nis-domain-name");
705           }
706           dhcpv6_domain(subtree,tvb, off, optlen);
707           break;
708         case OPTION_NISP_DOMAIN_NAME:
709           if (optlen > 0) {
710             proto_tree_add_text(subtree, tvb, off, optlen, "nisp-domain-name");
711           }
712           dhcpv6_domain(subtree,tvb, off, optlen);
713           break;
714         case OPTION_SNTP_SERVERS:
715                 if (optlen % 16) {
716                         proto_tree_add_text(subtree, tvb, off, optlen,
717                                 "SNTP servers address: malformed option");
718                         break;
719                 }
720                 for (i = 0; i < optlen; i += 16) {
721                         tvb_get_ipv6(tvb, off + i, &in6);
722                         proto_tree_add_text(subtree, tvb, off + i,
723                                 sizeof(in6), "SNTP servers address: %s",
724                                 ip6_to_str(&in6));
725                 }
726                 break;
727         case OPTION_TIME_ZONE:
728           if (optlen > 0) {
729               buf = tvb_get_ephemeral_string(tvb, off, optlen);
730               proto_tree_add_text(subtree, tvb, off, optlen, "time-zone: %s", buf);
731           }
732           break;
733         case OPTION_LIFETIME:
734           if (optlen != 4) {
735             proto_tree_add_text(subtree, tvb, off,
736                                 optlen, "LIFETIME: malformed option");
737             break;
738           }
739           proto_tree_add_text(subtree, tvb, off, 4,
740                               "Lifetime: %d",
741                               (guint32)tvb_get_ntohl(tvb, off));
742           break;
743         case OPTION_CLIENT_FQDN:
744           if (optlen < 1) {
745             proto_tree_add_text(subtree, tvb, off,
746                                 optlen, "FQDN: malformed option");
747             break;
748           }
749           /*
750            * +-----+-+-+-+
751            * | MBZ |N|O|S|
752            * +-----+-+-+-+
753            */
754           proto_tree_add_item(subtree, hf_fqdn_1, tvb, off, 1, FALSE);
755           proto_tree_add_item(subtree, hf_fqdn_2, tvb, off, 1, FALSE);
756           proto_tree_add_item(subtree, hf_fqdn_3, tvb, off, 1, FALSE);
757           proto_tree_add_item(subtree, hf_fqdn_4, tvb, off, 1, FALSE);
758 /*        proto_tree_add_text(subtree, tvb, off, 1, */
759 /*                            "flags: %d", */
760 /*                            (guint32)tvb_get_guint8(tvb, off)); */
761           dhcpv6_domain(subtree,tvb, off+1, (guint16) (optlen-1));
762           break;
763
764         case OPTION_IAPREFIX:
765             {
766                 guint32 preferred_lifetime, valid_lifetime;
767                 guint8  prefix_length;
768                 struct e_in6_addr in6;
769
770                 if (optlen < 25) {
771                    proto_tree_add_text(subtree, tvb, off,
772                                        optlen, "IAPREFIX: malformed option");
773                    break;
774                 }
775
776                 preferred_lifetime = tvb_get_ntohl(tvb, off);
777                 valid_lifetime = tvb_get_ntohl(tvb, off + 4);
778                 prefix_length  = tvb_get_guint8(tvb, off + 8);
779                 if (preferred_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
780                         proto_tree_add_text(subtree, tvb, off, 4,
781                                     "Preferred lifetime: infinity");
782                 } else {
783                         proto_tree_add_text(subtree, tvb, off, 4,
784                                     "Preferred lifetime: %u", preferred_lifetime);
785                 }
786                 if (valid_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
787                         proto_tree_add_text(subtree, tvb, off + 4, 4,
788                                     "Valid lifetime: infinity");
789                 } else {
790                         proto_tree_add_text(subtree, tvb, off + 4, 4,
791                                     "Valid lifetime: %u", valid_lifetime);
792                 }
793                 proto_tree_add_text(subtree, tvb, off + 8, 1,
794                                     "Prefix length: %d", prefix_length);
795                 tvb_get_ipv6(tvb, off + 9, &in6);
796                 proto_tree_add_text(subtree, tvb, off + 9,
797                                     16, "Prefix address: %s",
798                                     ip6_to_str(&in6));
799                 
800                 temp_optlen = 25;
801                 while ((optlen - temp_optlen) > 0) {
802                    temp_optlen += dhcpv6_option(tvb, pinfo, subtree, downstream,
803                                    off+temp_optlen, off + optlen, at_end);
804                    if (*at_end) {
805                      /* Bad option - just skip to the end */
806                      temp_optlen = optlen;
807                    }
808                 }
809             }
810             break;
811         case OPTION_MIP6_HA:
812                 if (optlen != 16) {
813                         proto_tree_add_text(subtree, tvb, off, optlen,
814                                 "MIP6_HA: malformed option");
815                         break;
816                 }
817
818                 tvb_get_ipv6(tvb, off, &in6);
819                 proto_tree_add_text(subtree, tvb, off,
820                         16, "Home Agent: %s", ip6_to_str(&in6));
821                 break;
822         case OPTION_MIP6_HOA:
823                 if (optlen != 16) {
824                         proto_tree_add_text(subtree, tvb, off, optlen,
825                                 "MIP6_HOA: malformed option");
826                         break;
827                 }
828
829                 tvb_get_ipv6(tvb, off, &in6);
830                 proto_tree_add_text(subtree, tvb, off,
831                         16, "Home Address: %s", ip6_to_str(&in6));
832                 break;
833         case OPTION_NAI:
834                 if (optlen < 4) {
835                         proto_tree_add_text(subtree, tvb, off, optlen,
836                                 "NAI: malformed option");
837                         break;
838                 }
839                 proto_tree_add_text(subtree, tvb, off, optlen,
840                         "NAI : %s", tvb_get_ptr(tvb, off, optlen - 2));
841                 break;
842         }
843
844         return 4 + optlen;
845 }
846
847
848 static void
849 dissect_dhcpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
850     gboolean downstream, int off, int eoff)
851 {
852         proto_tree *bp_tree = NULL;
853         proto_item *ti;
854         guint8 msgtype, hop_count ;
855         guint32 xid;
856         struct e_in6_addr in6;
857         gboolean at_end;
858
859         downstream = 0; /* feature reserved */
860
861         msgtype = tvb_get_guint8(tvb, off);
862
863         if (tree) {
864                 ti = proto_tree_add_item(tree, proto_dhcpv6, tvb, 0, -1, FALSE);
865                 bp_tree = proto_item_add_subtree(ti, ett_dhcpv6);
866         }
867
868         if (msgtype == RELAY_FORW || msgtype == RELAY_REPLY) {
869            
870            if (!off) {
871               if (check_col(pinfo->cinfo, COL_INFO)) {
872                  col_set_str(pinfo->cinfo, COL_INFO,
873                              val_to_str(msgtype,
874                                         msgtype_vals,
875                                         "Message Type %u"));
876               }
877            }
878
879            proto_tree_add_uint(bp_tree, hf_dhcpv6_msgtype, tvb, off, 1, msgtype);
880
881            hop_count = tvb_get_guint8(tvb, off+1);
882            proto_tree_add_text(bp_tree, tvb, off+1, 1, "Hop count: %d", hop_count);
883
884            tvb_get_ipv6(tvb, off+2, &in6);
885            proto_tree_add_text(bp_tree, tvb, off+2, sizeof(in6), 
886                                "Link-address: %s",ip6_to_str(&in6));
887
888            tvb_get_ipv6(tvb, off+18, &in6);
889            proto_tree_add_text(bp_tree, tvb, off+18, sizeof(in6), 
890                                "Peer-address: %s",ip6_to_str(&in6));
891
892            off += 34;
893         } else {
894         
895            xid = tvb_get_ntohl(tvb, off) & 0x00ffffff;
896
897            if (!off) {
898               if (check_col(pinfo->cinfo, COL_INFO)) {
899                  col_set_str(pinfo->cinfo, COL_INFO,
900                              val_to_str(msgtype,
901                                         msgtype_vals,
902                                         "Message Type %u"));
903               }
904            }
905
906            if (tree) {
907                    proto_tree_add_uint(bp_tree, hf_dhcpv6_msgtype, tvb, off, 1,
908                            msgtype);
909                    proto_tree_add_text(bp_tree, tvb, off+1, 3, "Transaction-ID: 0x%08x", xid);
910 #if 0
911                    tvb_get_ipv6(tvb, 4, &in6);
912                    proto_tree_add_text(bp_tree, tvb, 4, sizeof(in6),
913                            "Server address: %s", ip6_to_str(&in6));
914 #endif
915            }
916
917            off += 4;
918         }
919
920         at_end = FALSE;
921         while (off < eoff && !at_end)
922                 off += dhcpv6_option(tvb, pinfo, bp_tree, downstream, off, eoff, &at_end);
923 }
924
925 static void
926 dissect_dhcpv6_downstream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
927 {
928         if (check_col(pinfo->cinfo, COL_PROTOCOL))
929                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DHCPv6");
930         if (check_col(pinfo->cinfo, COL_INFO))
931                 col_clear(pinfo->cinfo, COL_INFO);
932         dissect_dhcpv6(tvb, pinfo, tree, TRUE, 0, tvb_reported_length(tvb));
933 }
934
935 static void
936 dissect_dhcpv6_upstream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
937 {
938         if (check_col(pinfo->cinfo, COL_PROTOCOL))
939                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DHCPv6");
940         if (check_col(pinfo->cinfo, COL_INFO))
941                 col_clear(pinfo->cinfo, COL_INFO);
942         dissect_dhcpv6(tvb, pinfo, tree, FALSE, 0, tvb_reported_length(tvb));
943 }
944
945
946 void
947 proto_register_dhcpv6(void)
948 {
949   static hf_register_info hf[] = {
950
951     { &hf_dhcpv6_msgtype,
952       { "Message type",                 "dhcpv6.msgtype",        FT_UINT8,
953          BASE_DEC,                      VALS(msgtype_vals),   0x0,
954         "", HFILL }},
955     { &hf_fqdn_1,
956       { "Reserved", "", FT_UINT8, BASE_HEX, NULL, 0xF8, "", HFILL}},
957     { &hf_fqdn_2,
958       { "N", "dhcpv6.msgtype.n", FT_BOOLEAN, 8, TFS(&fqdn_n), 0x4, "", HFILL}},
959     { &hf_fqdn_3,
960       { "O", "dhcpv6.msgtype.o", FT_BOOLEAN, 8, TFS(&fqdn_o), 0x2, "", HFILL}},
961     { &hf_fqdn_4,
962       { "S", "dhcpv6.msgtype.s", FT_BOOLEAN, 8, TFS(&fqdn_s), 0x1, "", HFILL}}
963     
964   };
965   static gint *ett[] = {
966     &ett_dhcpv6,
967     &ett_dhcpv6_option,
968   };
969
970   proto_dhcpv6 = proto_register_protocol("DHCPv6", "DHCPv6", "dhcpv6");
971   proto_register_field_array(proto_dhcpv6, hf, array_length(hf));
972   proto_register_subtree_array(ett, array_length(ett));
973 }
974
975 void
976 proto_reg_handoff_dhcpv6(void)
977 {
978   dissector_handle_t dhcpv6_handle;
979
980   dhcpv6_handle = create_dissector_handle(dissect_dhcpv6_downstream,
981         proto_dhcpv6);
982   dissector_add("udp.port", UDP_PORT_DHCPV6_DOWNSTREAM, dhcpv6_handle);
983   dhcpv6_handle = create_dissector_handle(dissect_dhcpv6_upstream,
984         proto_dhcpv6);
985   dissector_add("udp.port", UDP_PORT_DHCPV6_UPSTREAM, dhcpv6_handle);
986 }