CRLDP support, and assorted byg fixes, from Michael Rozhavsky.
[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  *
5  * $Id: packet-dhcpv6.c,v 1.4 2002/01/24 09:20:47 guy Exp $
6  *
7  * The information used comes from:
8  * draft-ietf-dhc-dhcpv6-22.txt
9  * Note that protocol constants are still subject to change, based on IANA
10  * assignment decisions.
11  *
12  * Ethereal - Network traffic analyzer
13  * By Gerald Combs <gerald@ethereal.com>
14  * Copyright 1998 Gerald Combs
15  * 
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License
18  * as published by the Free Software Foundation; either version 2
19  * of the License, or (at your option) any later version.
20  * 
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  * 
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
29  */
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #ifdef HAVE_SYS_TYPES_H
36 # include <sys/types.h>
37 #endif
38
39 #include <string.h>
40 #include <glib.h>
41 #include <epan/int-64bit.h>
42 #include <epan/packet.h>
43 #include <epan/ipv6-utils.h>
44
45 static int proto_dhcpv6 = -1;
46 static int hf_dhcpv6_msgtype = -1;
47
48 static guint ett_dhcpv6 = -1;
49 static guint ett_dhcpv6_option = -1;
50
51 #define UDP_PORT_DHCPV6_DOWNSTREAM      546
52 #define UDP_PORT_DHCPV6_UPSTREAM        547
53
54 static const value_string msgtype_vals[] = {
55         { 7,    "Reply" },
56         { 11,   "Information request" },
57         { 0,    NULL }
58 };
59
60 static const value_string opttype_vals[] = {
61         { 1,    "DUID" },
62         { 11,   "DNS servers address" },
63         { 0,    NULL }
64 };
65
66 /* Returns the number of bytes consumed by this option. */
67 static int
68 dhcpv6_option(tvbuff_t *tvb, proto_tree *bp_tree, int off, int eoff,
69     gboolean *at_end)
70 {
71         guint16 opttype;
72         guint16 optlen;
73         proto_item *ti;
74         proto_tree *subtree;
75         int i;
76         struct e_in6_addr in6;
77         guint16 duidtype;
78
79         /* option type and length must be present */
80         if (eoff - off < 4) {
81                 *at_end = TRUE;
82                 return 0;
83         }
84
85         opttype = tvb_get_ntohs(tvb, off);
86         optlen = tvb_get_ntohs(tvb, off + 2);
87
88         /* truncated case */
89         if (eoff - off < 4 + optlen) {
90                 *at_end = TRUE;
91                 return 0;
92         }
93
94         ti = proto_tree_add_text(bp_tree, tvb, off, 4 + optlen,
95                 "%s", val_to_str(opttype, opttype_vals, "DHCP option %u"));
96
97         subtree = proto_item_add_subtree(ti, ett_dhcpv6_option);
98         proto_tree_add_text(subtree, tvb, off, 2, "option type: %d", opttype);
99         proto_tree_add_text(subtree, tvb, off + 2, 2, "option length: %d",
100                 optlen);
101
102         off += 4;
103         switch (opttype) {
104         case 1: /* DUID */
105                 if (optlen < 2) {
106                         proto_tree_add_text(subtree, tvb, off, optlen,
107                                 "DUID: malformed option");
108                         break;
109                 }
110                 duidtype = tvb_get_ntohs(tvb, off);
111                 proto_tree_add_text(subtree, tvb, off, 2,
112                         "DUID type: %u", duidtype);
113                 switch (duidtype) {
114                 case 1:
115                         if (optlen < 8) {
116                                 proto_tree_add_text(subtree, tvb, off,
117                                         optlen, "DUID: malformed option");
118                                 break;
119                         }
120                         /* XXX seconds since Jan 1 2000 */
121                         proto_tree_add_text(subtree, tvb, off + 2, 4,
122                                 "Time: %u", tvb_get_ntohl(tvb, off + 6));
123                         proto_tree_add_text(subtree, tvb, off + 6, 2,
124                                 "Hardware type: %u",
125                                 tvb_get_ntohs(tvb, off + 6));
126                         if (optlen > 8) {
127                                 proto_tree_add_text(subtree, tvb, off + 8,
128                                         optlen - 8, "Link-layer address");
129                         }
130                         break;
131                 case 2:
132                         if (optlen < 10) {
133                                 proto_tree_add_text(subtree, tvb, off,
134                                         optlen, "DUID: malformed option");
135                                 break;
136                         }
137                         proto_tree_add_text(subtree, tvb, off + 2, 8, "VUID");
138                         if (optlen > 10) {
139                                 proto_tree_add_text(subtree, tvb, off + 10,
140                                         optlen - 10, "Domain name");
141                         }
142                         break;
143                 case 3:
144                         if (optlen < 4) {
145                                 proto_tree_add_text(subtree, tvb, off,
146                                         optlen, "DUID: malformed option");
147                                 break;
148                         }
149                         proto_tree_add_text(subtree, tvb, off + 2, 2,
150                                 "Hardware type: %u",
151                                 tvb_get_ntohs(tvb, off + 10));
152                         if (optlen > 4) {
153                                 proto_tree_add_text(subtree, tvb, off + 4,
154                                         optlen - 4, "Link-layer address");
155                         }
156                         break;
157                 }
158                 break;
159         case 11:        /* DNS servers address */
160                 if (optlen % 16) {
161                         proto_tree_add_text(subtree, tvb, off, optlen,
162                                 "DNS servers address: malformed option");
163                         break;
164                 }
165                 for (i = 0; i < optlen; i += 16) {
166                         tvb_memcpy(tvb, (guint8 *)&in6, off + i, sizeof(in6));
167                         proto_tree_add_text(subtree, tvb, off + i,
168                                 sizeof(in6), "DNS servers address: %s",
169                                 ip6_to_str(&in6));
170                 }
171                 break;
172         }
173
174         return 4 + optlen;
175 }
176
177 static void
178 dissect_dhcpv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
179     gboolean downstream)
180 {
181         proto_tree *bp_tree = NULL;
182         proto_item *ti;
183         guint8 msgtype;
184         guint32 xid;
185         struct e_in6_addr in6;
186         int off, eoff;
187         gboolean at_end;
188
189         if (check_col(pinfo->cinfo, COL_PROTOCOL))
190                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DHCPv6");
191         if (check_col(pinfo->cinfo, COL_INFO))
192                 col_clear(pinfo->cinfo, COL_INFO);
193
194         msgtype = tvb_get_guint8(tvb, 0);
195
196         /* XXX relay agent messages have to be decoded differently */
197
198         xid = tvb_get_ntohl(tvb, 0) & 0x00ffffff;
199
200         if (check_col(pinfo->cinfo, COL_INFO)) {
201                 col_set_str(pinfo->cinfo, COL_INFO,
202                         downstream ? "DHCPv6 reply" : "DHCPv6 request");
203         }
204
205         if (tree) {
206                 ti = proto_tree_add_item(tree, proto_dhcpv6, tvb, 0, -1, FALSE);
207                 bp_tree = proto_item_add_subtree(ti, ett_dhcpv6);
208
209                 proto_tree_add_uint(bp_tree, hf_dhcpv6_msgtype, tvb, 0, 1,
210                         msgtype);
211                 proto_tree_add_text(bp_tree, tvb, 1, 3, "XID: 0x%08x", xid);
212                 tvb_memcpy(tvb, (guint8 *)&in6, 4, sizeof(in6));
213                 proto_tree_add_text(bp_tree, tvb, 4, sizeof(in6),
214                         "Server address: %s", ip6_to_str(&in6));
215         }
216
217         off = 20;
218         eoff = tvb_reported_length(tvb);
219
220         at_end = FALSE;
221         while (off < eoff && !at_end)
222                 off += dhcpv6_option(tvb, bp_tree, off, eoff, &at_end);
223 }
224
225 static void
226 dissect_dhcpv6_downstream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
227 {
228         dissect_dhcpv6(tvb, pinfo, tree, TRUE);
229 }
230
231 static void
232 dissect_dhcpv6_upstream(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
233 {
234         dissect_dhcpv6(tvb, pinfo, tree, FALSE);
235 }
236
237
238 void
239 proto_register_dhcpv6(void)
240 {
241   static hf_register_info hf[] = {
242     { &hf_dhcpv6_msgtype,
243       { "Message type",                 "dhcpv6.msgtype",        FT_UINT8,
244          BASE_DEC,                      VALS(msgtype_vals),   0x0,
245         "", HFILL }},
246   };
247   static gint *ett[] = {
248     &ett_dhcpv6,
249     &ett_dhcpv6_option,
250   };
251   
252   proto_dhcpv6 = proto_register_protocol("DHCPv6", "DHCPv6", "dhcpv6");
253   proto_register_field_array(proto_dhcpv6, hf, array_length(hf));
254   proto_register_subtree_array(ett, array_length(ett));
255 }
256
257 void
258 proto_reg_handoff_dhcpv6(void)
259 {
260   dissector_handle_t dhcpv6_handle;
261
262   dhcpv6_handle = create_dissector_handle(dissect_dhcpv6_downstream,
263         proto_dhcpv6);
264   dissector_add("udp.port", UDP_PORT_DHCPV6_DOWNSTREAM, dhcpv6_handle);
265   dhcpv6_handle = create_dissector_handle(dissect_dhcpv6_upstream,
266         proto_dhcpv6);
267   dissector_add("udp.port", UDP_PORT_DHCPV6_UPSTREAM, dhcpv6_handle);
268 }