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