various string related changes, mainly replace sprintf/snprintf by g_snprintf
[obnox/wireshark/wip.git] / packet-dhcpv6.c
1 /* packet-dhpcv6.c
2  * Routines for DHCPv6 packet disassembly
3  * Jun-ichiro itojun Hagino <itojun@iijlab.net>
4  * IItom Tsutomu MIENO <iitom@utouto.com>
5  * SHIRASAKI Yasuhiro <yasuhiro@gnome.gr.jp>
6  * Tony Lindstrom <tony.lindstrom@ericsson.com>
7  *
8  * $Id: packet-dhcpv6.c,v 1.11 2004/02/25 09:31:05 guy Exp $
9  *
10  * The information used comes from:
11  * RFC3315.txt
12  * RFC3319.txt
13  * RFC3633.txt
14  * RFC3646.txt
15  * draft-ietf-dhc-dhcpv6-opt-nisconfig-02.txt
16  * draft-ietf-dhc-dhcpv6-opt-timeconfig-02.txt
17  * Note that protocol constants are still subject to change, based on IANA
18  * assignment decisions.
19  *
20  * Ethereal - Network traffic analyzer
21  * By Gerald Combs <gerald@ethereal.com>
22  * Copyright 1998 Gerald Combs
23  *
24  * This program is free software; you can redistribute it and/or
25  * modify it under the terms of the GNU General Public License
26  * as published by the Free Software Foundation; either version 2
27  * of the License, or (at your option) any later version.
28  *
29  * This program is distributed in the hope that it will be useful,
30  * but WITHOUT ANY WARRANTY; without even the implied warranty of
31  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32  * GNU General Public License for more details.
33  *
34  * You should have received a copy of the GNU General Public License
35  * along with this program; if not, write to the Free Software
36  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
37  */
38
39 #ifdef HAVE_CONFIG_H
40 # include "config.h"
41 #endif
42
43 #include <string.h>
44 #include <glib.h>
45 #include <epan/int-64bit.h>
46 #include <epan/packet.h>
47 #include <epan/ipv6-utils.h>
48
49 static int proto_dhcpv6 = -1;
50 static int hf_dhcpv6_msgtype = -1;
51
52 static guint ett_dhcpv6 = -1;
53 static guint ett_dhcpv6_option = -1;
54
55 #define UDP_PORT_DHCPV6_DOWNSTREAM      546
56 #define UDP_PORT_DHCPV6_UPSTREAM        547
57
58 #define DHCPV6_LEASEDURATION_INFINITY   0xffffffff
59
60 #define SOLICIT                 1
61 #define ADVERTISE               2
62 #define REQUEST                 3
63 #define CONFIRM                 4
64 #define RENEW                   5
65 #define REBIND                  6
66 #define REPLY                   7
67 #define RELEASE                 8
68 #define DECLINE                 9
69 #define RECONFIGURE             10
70 #define INFORMATION_REQUEST     11
71 #define RELAY_FORW              12
72 #define RELAY_REPLY             13
73
74 #define OPTION_CLIENTID         1
75 #define OPTION_SERVERID         2
76 #define OPTION_IA_NA            3
77 #define OPTION_IA_TA            4
78 #define OPTION_IAADDR           5
79 #define OPTION_ORO              6
80 #define OPTION_PREFERENCE       7
81 #define OPTION_ELAPSED_TIME     8
82 #define OPTION_RELAY_MSG        9
83 /* #define      OPTION_SERVER_MSG       10 */
84 #define OPTION_AUTH             11
85 #define OPTION_UNICAST          12
86 #define OPTION_STATUS_CODE      13
87 #define OPTION_RAPID_COMMIT     14
88 #define OPTION_USER_CLASS       15
89 #define OPTION_VENDOR_CLASS     16
90 #define OPTION_VENDOR_OPTS      17
91 #define OPTION_INTERFACE_ID     18
92 #define OPTION_RECONF_MSG       19
93 #define OPTION_RECONF_ACCEPT    20
94 #define OPTION_SIP_SERVER_D     21
95 #define OPTION_SIP_SERVER_A     22
96 #define OPTION_DNS_SERVERS      23
97 #define OPTION_DOMAIN_LIST      24
98 #define OPTION_IA_PD            25
99 #define OPTION_IAPREFIX         26
100
101 /*
102  * The followings are unassigned numbers.
103  */
104 #define OPTION_NIS_SERVERS      35
105 #define OPTION_NISP_SERVERS     36
106 #define OPTION_NIS_DOMAIN_NAME  37
107 #define OPTION_NISP_DOMAIN_NAME 38
108 #define OPTION_NTP_SERVERS      40
109 #define OPTION_TIME_ZONE        41
110
111 #define DUID_LLT                1
112 #define DUID_EN                 2
113 #define DUID_LL                 3
114 #define DUID_LL_OLD             4
115
116 static const value_string msgtype_vals[] = {
117         { SOLICIT,      "Solicit" },
118         { ADVERTISE,    "Advertise" },
119         { REQUEST,      "Request" },
120         { CONFIRM,      "Confirm" },
121         { RENEW,        "Renew" },
122         { REBIND,       "Rebind" },
123         { REPLY,        "Reply" },
124         { RELEASE,      "Release" },
125         { DECLINE,      "Decline" },
126         { RECONFIGURE,  "Reconfigure" },
127         { INFORMATION_REQUEST,  "Information-request" },
128         { RELAY_FORW,   "Relay-forw" },
129         { RELAY_REPLY,  "Relay-reply" },
130         { 0, NULL }
131 };
132
133 static const value_string opttype_vals[] = {
134         { OPTION_CLIENTID,      "Client Identifier" },
135         { OPTION_SERVERID,      "Server Identifier" },
136         { OPTION_IA_NA,         "Identify Association" },
137         { OPTION_IA_TA,         "Identify Association for Temporary Address" },
138         { OPTION_IAADDR,        "IA Address" },
139         { OPTION_ORO,           "Option Request" },
140         { OPTION_PREFERENCE,    "Preference" },
141         { OPTION_ELAPSED_TIME,  "Elapsed time" },
142         { OPTION_RELAY_MSG,     "Relay Message" },
143 /*      { OPTION_SERVER_MSG,    "Server message" }, */
144         { OPTION_AUTH,          "Authentication" },
145         { OPTION_UNICAST,       "Server unicast" },
146         { OPTION_STATUS_CODE,   "Status code" },
147         { OPTION_RAPID_COMMIT,  "Rapid Commit" },
148         { OPTION_USER_CLASS,    "User Class" },
149         { OPTION_VENDOR_CLASS,  "Vendor Class" },
150         { OPTION_VENDOR_OPTS,   "Vendor-specific Information" },
151         { OPTION_INTERFACE_ID,  "Interface-Id" },
152         { OPTION_RECONF_MSG,    "Reconfigure Message" },
153         { OPTION_RECONF_ACCEPT, "Reconfigure Accept" },
154         { OPTION_SIP_SERVER_D,  "SIP Server Domain Name List" },
155         { OPTION_SIP_SERVER_A,  "SIP Servers IPv6 Address List" },
156         { OPTION_DNS_SERVERS,   "DNS recursive name server" },
157         { OPTION_DOMAIN_LIST,   "Domain Search List" },
158         { OPTION_IA_PD,         "Identify Association for Prefix Delegation" },
159         { OPTION_IAPREFIX,      "IA Prefix" },
160         { OPTION_NIS_SERVERS,   "Network Information Server" },
161         { OPTION_NISP_SERVERS,  "Network Information Server V2" },
162         { OPTION_NIS_DOMAIN_NAME, "Network Information Server Domain Name" },
163         { OPTION_NISP_DOMAIN_NAME,"Network Information Server V2 Domain Name" },
164         { OPTION_NTP_SERVERS,   "Network Time Protocol Server" },
165         { OPTION_TIME_ZONE,     "Time zone" },
166         { 0,    NULL }
167 };
168
169 static const value_string statuscode_vals[] =
170 {
171         {0, "Success" },
172         {1, "UnspecFail" },
173         {2, "NoAddrAvail" },
174         {3, "NoBinding" },
175         {4, "NotOnLink" },
176         {5, "UseMulticast" },
177         {6, "NoPrefixAvail" },
178         {0, NULL }
179 };
180
181 static const value_string duidtype_vals[] =
182 {
183         { DUID_LLT,     "link-layer address plus time" },
184         { DUID_EN,      "assigned by vendor based on Enterprise number" },
185         { DUID_LL,      "link-layer address" },
186         { DUID_LL_OLD,  "link-layer address (old)" },
187         { 0, NULL }
188 };
189
190 /* Returns the number of bytes consumed by this option. */
191 static int
192 dhcpv6_option(tvbuff_t *tvb, proto_tree *bp_tree, int off, int eoff,
193     gboolean *at_end)
194 {
195         guint16 opttype;
196         guint16 optlen;
197         guint16 temp_optlen = 0;
198         proto_item *ti;
199         proto_tree *subtree;
200         int i;
201         struct e_in6_addr in6;
202         guint16 duidtype;
203
204         /* option type and length must be present */
205         if (eoff - off < 4) {
206                 *at_end = TRUE;
207                 return 0;
208         }
209
210         opttype = tvb_get_ntohs(tvb, off);
211         optlen = tvb_get_ntohs(tvb, off + 2);
212
213         /* truncated case */
214         if (eoff - off < 4 + optlen) {
215                 *at_end = TRUE;
216                 return 0;
217         }
218
219         ti = proto_tree_add_text(bp_tree, tvb, off, 4 + optlen,
220                 "%s", val_to_str(opttype, opttype_vals, "DHCP option %u"));
221
222         subtree = proto_item_add_subtree(ti, ett_dhcpv6_option);
223         proto_tree_add_text(subtree, tvb, off, 2, "option type: %d", opttype);
224         proto_tree_add_text(subtree, tvb, off + 2, 2, "option length: %d",
225                 optlen);
226
227         off += 4;
228         switch (opttype) {
229         case OPTION_CLIENTID:
230         case OPTION_SERVERID:
231                 if (optlen < 2) {
232                         proto_tree_add_text(subtree, tvb, off, optlen,
233                                 "DUID: malformed option");
234                         break;
235                 }
236                 duidtype = tvb_get_ntohs(tvb, off);
237                 proto_tree_add_text(subtree, tvb, off, 2,
238                         "DUID type: %s (%u)",
239                                     val_to_str(duidtype,
240                                                duidtype_vals, "Unknown"),
241                                     duidtype);
242                 switch (duidtype) {
243                 case DUID_LLT:
244                         if (optlen < 8) {
245                                 proto_tree_add_text(subtree, tvb, off,
246                                         optlen, "DUID: malformed option");
247                                 break;
248                         }
249                         /* XXX seconds since Jan 1 2000 */
250                         proto_tree_add_text(subtree, tvb, off + 2, 2,
251                                 "Hardware type: %u",
252                                 tvb_get_ntohs(tvb, off + 2));
253                         proto_tree_add_text(subtree, tvb, off + 4, 4,
254                                 "Time: %u", tvb_get_ntohl(tvb, off + 4));
255                         if (optlen > 8) {
256                                 proto_tree_add_text(subtree, tvb, off + 8,
257                                         optlen - 8, "Link-layer address");
258                         }
259                         break;
260                 case DUID_EN:
261                         if (optlen < 6) {
262                                 proto_tree_add_text(subtree, tvb, off,
263                                         optlen, "DUID: malformed option");
264                                 break;
265                         }
266                         proto_tree_add_text(subtree, tvb, off + 2, 4,
267                                             "enterprise-number");
268                         if (optlen > 6) {
269                                 proto_tree_add_text(subtree, tvb, off + 6,
270                                         optlen - 6, "identifier");
271                         }
272                         break;
273                 case DUID_LL:
274                 case DUID_LL_OLD:
275                         if (optlen < 4) {
276                                 proto_tree_add_text(subtree, tvb, off,
277                                         optlen, "DUID: malformed option");
278                                 break;
279                         }
280                         proto_tree_add_text(subtree, tvb, off + 2, 2,
281                                 "Hardware type: %u",
282                                 tvb_get_ntohs(tvb, off + 2));
283                         if (optlen > 4) {
284                                 proto_tree_add_text(subtree, tvb, off + 4,
285                                         optlen - 4, "Link-layer address");
286                         }
287                         break;
288                 }
289                 break;
290         case OPTION_IA_NA:
291         case OPTION_IA_PD:
292           if (optlen < 12) {
293              if (opttype == OPTION_IA_NA)
294                 proto_tree_add_text(subtree, tvb, off,
295                                     optlen, "IA_NA: malformed option");
296              else
297                 proto_tree_add_text(subtree, tvb, off,
298                                     optlen, "IA_PD: malformed option");
299              break;
300           }
301           proto_tree_add_text(subtree, tvb, off, 4,
302                               "IAID: %u",
303                               tvb_get_ntohl(tvb, off));
304           proto_tree_add_text(subtree, tvb, off+4, 4,
305                               "T1: %u", tvb_get_ntohl(tvb, off+4));
306           proto_tree_add_text(subtree, tvb, off+8, 4,
307                               "T2: %u", tvb_get_ntohl(tvb, off+8));
308
309           temp_optlen = 12;
310           while ((optlen - temp_optlen) > 0) {
311             gboolean at_end_ = FALSE;
312             temp_optlen += dhcpv6_option(tvb, subtree, off+temp_optlen, off + optlen, &at_end_);
313           }
314           break;
315         case OPTION_IA_TA:
316           if (optlen < 4) {
317             proto_tree_add_text(subtree, tvb, off,
318                                 optlen, "IA_TA: malformed option");
319             break;
320           }
321           proto_tree_add_text(subtree, tvb, off, 4,
322                               "IAID: %u",
323                               tvb_get_ntohl(tvb, off));
324           temp_optlen = 4;
325           while ((optlen - temp_optlen) > 0) {
326             gboolean at_end_;
327             temp_optlen += dhcpv6_option(tvb, subtree, off+temp_optlen, off + optlen, &at_end_);
328           }
329           break;
330         case OPTION_IAADDR:
331         {
332            guint32 preferred_lifetime, valid_lifetime;
333
334            if (optlen < 24) {
335               proto_tree_add_text(subtree, tvb, off,
336                                   optlen, "IAADDR: malformed option");
337               break;
338            }
339            tvb_memcpy(tvb, (guint8 *)&in6, off, sizeof(in6));
340            proto_tree_add_text(subtree, tvb, off,
341                                sizeof(in6), "IPv6 address: %s",
342                                ip6_to_str(&in6));
343            
344            preferred_lifetime = tvb_get_ntohl(tvb, off + 16);
345            valid_lifetime = tvb_get_ntohl(tvb, off + 20);
346            
347            if (preferred_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
348               proto_tree_add_text(subtree, tvb, off + 16, 4,
349                                   "Preferred lifetime: infinity");
350            } else {
351               proto_tree_add_text(subtree, tvb, off + 16, 4,
352                                   "Preferred lifetime: %u", preferred_lifetime);
353            }
354            if (valid_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
355               proto_tree_add_text(subtree, tvb, off + 20, 4,
356                                   "Valid lifetime: infinity");
357            } else {
358               proto_tree_add_text(subtree, tvb, off + 20, 4,
359                                   "Valid lifetime: %u", valid_lifetime);
360            }
361            
362            temp_optlen = 24;
363            while ((optlen - temp_optlen) > 0) {
364               gboolean at_end_;
365               temp_optlen += dhcpv6_option(tvb, subtree, off+temp_optlen, off + optlen, &at_end_);
366            }
367         }
368         break;
369         case OPTION_ORO:
370                 for (i = 0; i < optlen; i += 2) {
371                     guint16 requested_opt_code;
372                     requested_opt_code = tvb_get_ntohs(tvb, off + i);
373                     proto_tree_add_text(subtree, tvb, off + i,
374                             2, "Requested Option code: %s (%d)",
375                                             val_to_str(requested_opt_code,
376                                                        opttype_vals,
377                                                        "Unknown"),
378                                             requested_opt_code);
379                 }
380                 break;
381         case OPTION_PREFERENCE:
382           if (optlen != 1) {
383             proto_tree_add_text(subtree, tvb, off,
384                                 optlen, "PREFERENCE: malformed option");
385             break;
386           }
387           proto_tree_add_text(subtree, tvb, off, 1,
388                               "pref-value: %d",
389                               (guint32)tvb_get_guint8(tvb, off));
390           break;
391         case OPTION_ELAPSED_TIME:
392           if (optlen != 2) {
393             proto_tree_add_text(subtree, tvb, off,
394                                 optlen, "ELAPSED-TIME: malformed option");
395             break;
396           }
397           proto_tree_add_text(subtree, tvb, off, 2,
398                               "elapsed-time: %d sec",
399                               (guint32)tvb_get_ntohs(tvb, off));
400           break;
401         case OPTION_RELAY_MSG:
402           if (optlen == 0) {
403             proto_tree_add_text(subtree, tvb, off,
404                                 optlen, "RELAY-MSG: malformed option");
405             break;
406           } else {
407             gboolean at_end_;
408             dhcpv6_option(tvb, subtree, off, off + optlen, &at_end_);
409           } 
410           break;
411         case OPTION_AUTH:
412           if (optlen < 15) {
413             proto_tree_add_text(subtree, tvb, off,
414                                 optlen, "AUTH: malformed option");
415             break;
416           }
417           proto_tree_add_text(subtree, tvb, off, 1,
418                               "Protocol: %d",
419                               (guint32)tvb_get_guint8(tvb, off));
420           proto_tree_add_text(subtree, tvb, off+1, 1,
421                               "Algorithm: %d",
422                               (guint32)tvb_get_guint8(tvb, off+1));
423           proto_tree_add_text(subtree, tvb, off+2, 1,
424                               "RDM: %d",
425                               (guint32)tvb_get_guint8(tvb, off+2));
426           proto_tree_add_text(subtree, tvb, off+3, 8,
427                               "Reply Detection");
428           proto_tree_add_text(subtree, tvb, off+11, optlen-11,
429                               "Authentication Information");
430           break;
431         case OPTION_UNICAST:
432           if (optlen != 16) {
433             proto_tree_add_text(subtree, tvb, off,
434                                 optlen, "UNICAST: malformed option");
435             break;
436           }
437           tvb_memcpy(tvb, (guint8 *)&in6, off, sizeof(in6));
438           proto_tree_add_text(subtree, tvb, off,
439                               sizeof(in6), "IPv6 address: %s",
440                                 ip6_to_str(&in6));
441           break;
442         case OPTION_STATUS_CODE:
443             {
444                 guint16 status_code;
445                 char *status_message = 0;
446                 status_code = tvb_get_ntohs(tvb, off);
447                 proto_tree_add_text(subtree, tvb, off, 2,
448                                     "Status Code: %s (%d)",
449                                     val_to_str(status_code, statuscode_vals,
450                                                "Unknown"),
451                                     status_code);
452
453                 if (optlen - 2 > 0) {
454                     status_message = tvb_get_string(tvb, off + 2, optlen - 2);
455                     proto_tree_add_text(subtree, tvb, off + 2, optlen - 2,
456                                         "Status Message: %s",
457                                         status_message);
458                     g_free(status_message);
459                 }
460             }
461             break;
462         case OPTION_VENDOR_CLASS:
463           if (optlen < 4) {
464             proto_tree_add_text(subtree, tvb, off,
465                                 optlen, "VENDOR_CLASS: malformed option");
466             break;
467           }
468           proto_tree_add_text(subtree, tvb, off, 4,
469                               "enterprise-number: %u",
470                               tvb_get_ntohl(tvb, off));
471           if (optlen > 4) {
472             proto_tree_add_text(subtree, tvb, off+4, optlen-4,
473                                 "vendor-class-data");
474           }
475           break;
476         case OPTION_VENDOR_OPTS:
477           if (optlen < 4) {
478             proto_tree_add_text(subtree, tvb, off,
479                                 optlen, "VENDOR_OPTS: malformed option");
480             break;
481           }
482           proto_tree_add_text(subtree, tvb, off, 4,
483                               "enterprise-number: %u",
484                               tvb_get_ntohl(tvb, off));
485           if (optlen > 4) {
486             proto_tree_add_text(subtree, tvb, off+4, optlen-4,
487                                 "option-data");
488           }
489           break;
490         case OPTION_INTERFACE_ID:
491           if (optlen == 0) {
492             proto_tree_add_text(subtree, tvb, off,
493                                 optlen, "INTERFACE_ID: malformed option");
494             break;
495           }
496           proto_tree_add_text(subtree, tvb, off, optlen, "Interface-ID");
497           break;
498         case OPTION_RECONF_MSG:
499           if (optlen != 1) {
500             proto_tree_add_text(subtree, tvb, off,
501                                 optlen, "RECONF_MSG: malformed option");
502             break;
503           }
504           proto_tree_add_text(subtree, tvb, off, optlen,
505                               "Reconfigure-type: %s",
506                               val_to_str(tvb_get_guint8(tvb, off),
507                                          msgtype_vals,
508                                          "Message Type %u"));
509           break;
510         case OPTION_SIP_SERVER_D:
511                 if (optlen > 0) {
512                         proto_tree_add_text(subtree, tvb, off, optlen,
513                                 "SIP Servers Domain Search List");
514                 }
515         case OPTION_SIP_SERVER_A:
516                 if (optlen % 16) {
517                         proto_tree_add_text(subtree, tvb, off, optlen,
518                                 "SIP servers address: malformed option");
519                         break;
520                 }
521                 for (i = 0; i < optlen; i += 16) {
522                         tvb_memcpy(tvb, (guint8 *)&in6, off + i, sizeof(in6));
523                         proto_tree_add_text(subtree, tvb, off + i,
524                                 sizeof(in6), "SIP servers address: %s",
525                                 ip6_to_str(&in6));
526                 }
527                 break;
528         case OPTION_DNS_SERVERS:
529                 if (optlen % 16) {
530                         proto_tree_add_text(subtree, tvb, off, optlen,
531                                 "DNS servers address: malformed option");
532                         break;
533                 }
534                 for (i = 0; i < optlen; i += 16) {
535                         tvb_memcpy(tvb, (guint8 *)&in6, off + i, sizeof(in6));
536                         proto_tree_add_text(subtree, tvb, off + i,
537                                 sizeof(in6), "DNS servers address: %s",
538                                 ip6_to_str(&in6));
539                 }
540                 break;
541         case OPTION_DOMAIN_LIST:
542           if (optlen > 0) {
543             proto_tree_add_text(subtree, tvb, off, optlen, "DNS Domain Search List");
544           }
545           break;
546         case OPTION_NIS_SERVERS:
547                 if (optlen % 16) {
548                         proto_tree_add_text(subtree, tvb, off, optlen,
549                                 "NIS servers address: malformed option");
550                         break;
551                 }
552                 for (i = 0; i < optlen; i += 16) {
553                         tvb_memcpy(tvb, (guint8 *)&in6, off + i, sizeof(in6));
554                         proto_tree_add_text(subtree, tvb, off + i,
555                                 sizeof(in6), "NIS servers address: %s",
556                                 ip6_to_str(&in6));
557                 }
558                 break;
559         case OPTION_NISP_SERVERS:
560                 if (optlen % 16) {
561                         proto_tree_add_text(subtree, tvb, off, optlen,
562                                 "NISP servers address: malformed option");
563                         break;
564                 }
565                 for (i = 0; i < optlen; i += 16) {
566                         tvb_memcpy(tvb, (guint8 *)&in6, off + i, sizeof(in6));
567                         proto_tree_add_text(subtree, tvb, off + i,
568                                 sizeof(in6), "NISP servers address: %s",
569                                 ip6_to_str(&in6));
570                 }
571                 break;
572         case OPTION_NIS_DOMAIN_NAME:
573           if (optlen > 0) {
574             proto_tree_add_text(subtree, tvb, off, optlen, "nis-domain-name");
575           }
576           break;
577         case OPTION_NISP_DOMAIN_NAME:
578           if (optlen > 0) {
579             proto_tree_add_text(subtree, tvb, off, optlen, "nisp-domain-name");
580           }
581           break;
582         case OPTION_NTP_SERVERS:
583                 if (optlen % 16) {
584                         proto_tree_add_text(subtree, tvb, off, optlen,
585                                 "NTP servers address: malformed option");
586                         break;
587                 }
588                 for (i = 0; i < optlen; i += 16) {
589                         tvb_memcpy(tvb, (guint8 *)&in6, off + i, sizeof(in6));
590                         proto_tree_add_text(subtree, tvb, off + i,
591                                 sizeof(in6), "NTP servers address: %s",
592                                 ip6_to_str(&in6));
593                 }
594                 break;
595         case OPTION_TIME_ZONE:
596           if (optlen > 0) {
597             proto_tree_add_text(subtree, tvb, off, optlen, "time-zone");
598           }
599           break;
600         case OPTION_IAPREFIX:
601             {
602                 guint32 preferred_lifetime, valid_lifetime;
603                 guint8  prefix_length;
604                 struct e_in6_addr in6;
605
606                 if (optlen < 25) {
607                    proto_tree_add_text(subtree, tvb, off,
608                                        optlen, "IAPREFIX: malformed option");
609                    break;
610                 }
611
612                 preferred_lifetime = tvb_get_ntohl(tvb, off);
613                 valid_lifetime = tvb_get_ntohl(tvb, off + 4);
614                 prefix_length  = tvb_get_guint8(tvb, off + 8);
615                 if (preferred_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
616                         proto_tree_add_text(subtree, tvb, off, 4,
617                                     "Preferred lifetime: infinity");
618                 } else {
619                         proto_tree_add_text(subtree, tvb, off, 4,
620                                     "Preferred lifetime: %u", preferred_lifetime);
621                 }
622                 if (valid_lifetime == DHCPV6_LEASEDURATION_INFINITY) {
623                         proto_tree_add_text(subtree, tvb, off + 4, 4,
624                                     "Valid lifetime: infinity");
625                 } else {
626                         proto_tree_add_text(subtree, tvb, off + 4, 4,
627                                     "Valid lifetime: %u", valid_lifetime);
628                 }
629                 proto_tree_add_text(subtree, tvb, off + 8, 1,
630                                     "Prefix length: %d", prefix_length);
631                 tvb_memcpy(tvb, (guint8 *)&in6, off + 9 , sizeof(in6));
632                 proto_tree_add_text(subtree, tvb, off + 9,
633                                     16, "Prefix address: %s",
634                                     ip6_to_str(&in6));
635                 
636                 temp_optlen = 25;
637                 while ((optlen - temp_optlen) > 0) {
638                    gboolean at_end_;
639                    temp_optlen += dhcpv6_option(tvb, subtree, off+temp_optlen, off + optlen, &at_end_);
640                 }
641             }
642             break;
643         }
644
645         return 4 + optlen;
646 }
647
648
649 static void
650 dissect_dhcpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
651     gboolean downstream)
652 {
653         proto_tree *bp_tree = NULL;
654         proto_item *ti;
655         guint8 msgtype, hop_count ;
656         guint32 xid;
657         int off = 0;
658         int eoff;
659         struct e_in6_addr in6;
660         gboolean at_end;
661         gboolean relay_msg_option = FALSE;
662         int length;
663
664         eoff = tvb_reported_length(tvb);
665         downstream = 0; /* feature reserved */
666         if (check_col(pinfo->cinfo, COL_PROTOCOL))
667                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DHCPv6");
668         if (check_col(pinfo->cinfo, COL_INFO))
669                 col_clear(pinfo->cinfo, COL_INFO);
670
671         msgtype = tvb_get_guint8(tvb, off);
672
673         if (tree) {
674                 ti = proto_tree_add_item(tree, proto_dhcpv6, tvb, 0, -1, FALSE);
675                 bp_tree = proto_item_add_subtree(ti, ett_dhcpv6);
676         }
677
678         while (msgtype == RELAY_FORW || msgtype == RELAY_REPLY) {
679            
680            if (check_col(pinfo->cinfo, COL_INFO)) {
681               col_set_str(pinfo->cinfo, COL_INFO,
682                           val_to_str(msgtype,
683                                      msgtype_vals,
684                                      "Message Type %u"));
685            }
686
687            proto_tree_add_uint(bp_tree, hf_dhcpv6_msgtype, tvb, off, 1, msgtype);
688
689            hop_count = tvb_get_guint8(tvb, off+1);
690            proto_tree_add_text(bp_tree, tvb, off+1, 1, "Hop count: %d", hop_count);
691
692            tvb_memcpy(tvb, (guint8 *)&in6, off+2, sizeof(in6));
693            proto_tree_add_text(bp_tree, tvb, off+2, sizeof(in6), 
694                                "Link-address: %s",ip6_to_str(&in6));
695
696            tvb_memcpy(tvb, (guint8 *)&in6, off+18, sizeof(in6));
697            proto_tree_add_text(bp_tree, tvb, off+18, sizeof(in6), 
698                                "Peer-address: %s",ip6_to_str(&in6));
699
700            off += 34;
701            relay_msg_option = FALSE;
702
703            while (!relay_msg_option && off < eoff) {
704               length = dhcpv6_option(tvb, bp_tree, off, eoff, &at_end);
705
706               if (tvb_get_ntohs(tvb, off) == OPTION_RELAY_MSG) {
707                  relay_msg_option = TRUE;
708                  off += 4;
709               }
710               else {
711                  if (length > 0)
712                     off += length;
713                  else {
714                     proto_tree_add_text(bp_tree, tvb, off, eoff, "Message: malformed");
715                     return;
716                  }
717               }
718            }
719            
720            msgtype = tvb_get_guint8(tvb, off);
721         }
722         
723         xid = tvb_get_ntohl(tvb, off) & 0x00ffffff;
724
725         if (!off) {
726            if (check_col(pinfo->cinfo, COL_INFO)) {
727               col_set_str(pinfo->cinfo, COL_INFO,
728                           val_to_str(msgtype,
729                                      msgtype_vals,
730                                      "Message Type %u"));
731            }
732         }
733
734         if (tree) {
735                 proto_tree_add_uint(bp_tree, hf_dhcpv6_msgtype, tvb, off, 1,
736                         msgtype);
737                 proto_tree_add_text(bp_tree, tvb, off+1, 3, "Transaction-ID: 0x%08x", xid);
738 #if 0
739                 tvb_memcpy(tvb, (guint8 *)&in6, 4, sizeof(in6));
740                 proto_tree_add_text(bp_tree, tvb, 4, sizeof(in6),
741                         "Server address: %s", ip6_to_str(&in6));
742 #endif
743         }
744
745         off += 4;
746
747         at_end = FALSE;
748         while (off < eoff && !at_end)
749                 off += dhcpv6_option(tvb, bp_tree, off, eoff, &at_end);
750 }
751
752 static void
753 dissect_dhcpv6_downstream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
754 {
755         dissect_dhcpv6(tvb, pinfo, tree, TRUE);
756 }
757
758 static void
759 dissect_dhcpv6_upstream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
760 {
761         dissect_dhcpv6(tvb, pinfo, tree, FALSE);
762 }
763
764
765 void
766 proto_register_dhcpv6(void)
767 {
768   static hf_register_info hf[] = {
769     { &hf_dhcpv6_msgtype,
770       { "Message type",                 "dhcpv6.msgtype",        FT_UINT8,
771          BASE_DEC,                      VALS(msgtype_vals),   0x0,
772         "", HFILL }},
773   };
774   static gint *ett[] = {
775     &ett_dhcpv6,
776     &ett_dhcpv6_option,
777   };
778
779   proto_dhcpv6 = proto_register_protocol("DHCPv6", "DHCPv6", "dhcpv6");
780   proto_register_field_array(proto_dhcpv6, hf, array_length(hf));
781   proto_register_subtree_array(ett, array_length(ett));
782 }
783
784 void
785 proto_reg_handoff_dhcpv6(void)
786 {
787   dissector_handle_t dhcpv6_handle;
788
789   dhcpv6_handle = create_dissector_handle(dissect_dhcpv6_downstream,
790         proto_dhcpv6);
791   dissector_add("udp.port", UDP_PORT_DHCPV6_DOWNSTREAM, dhcpv6_handle);
792   dhcpv6_handle = create_dissector_handle(dissect_dhcpv6_upstream,
793         proto_dhcpv6);
794   dissector_add("udp.port", UDP_PORT_DHCPV6_UPSTREAM, dhcpv6_handle);
795 }