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