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