Don't attempt to modify the data pointed to by the result of
[obnox/wireshark/wip.git] / packet-bootp.c
1 /* packet-bootp.c
2  * Routines for BOOTP/DHCP packet disassembly
3  * Gilbert Ramirez <gram@xiexie.org>
4  *
5  * $Id: packet-bootp.c,v 1.48 2001/03/13 21:34:23 gram Exp $
6  *
7  * The information used comes from:
8  * RFC  951: Bootstrap Protocol
9  * RFC 1542: Clarifications and Extensions for the Bootstrap Protocol
10  * RFC 2131: Dynamic Host Configuration Protocol
11  * RFC 2132: DHCP Options and BOOTP Vendor Extensions
12  * RFC 2489: Procedure for Defining New DHCP Options
13  * RFC 3046: DHCP Relay Agent Information Option
14  * BOOTP and DHCP Parameters
15  *     http://www.isi.edu/in-notes/iana/assignments/bootp-dhcp-parameters
16  *
17  * Ethereal - Network traffic analyzer
18  * By Gerald Combs <gerald@zing.org>
19  * Copyright 1998 Gerald Combs
20  *
21  * 
22  * This program is free software; you can redistribute it and/or
23  * modify it under the terms of the GNU General Public License
24  * as published by the Free Software Foundation; either version 2
25  * of the License, or (at your option) any later version.
26  * 
27  * This program is distributed in the hope that it will be useful,
28  * but WITHOUT ANY WARRANTY; without even the implied warranty of
29  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30  * GNU General Public License for more details.
31  * 
32  * You should have received a copy of the GNU General Public License
33  * along with this program; if not, write to the Free Software
34  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
35  */
36
37 #ifdef HAVE_CONFIG_H
38 # include "config.h"
39 #endif
40
41 #ifdef HAVE_SYS_TYPES_H
42 # include <sys/types.h>
43 #endif
44
45 #include <string.h>
46 #include <glib.h>
47 #include "packet.h"
48 #include "packet-arp.h"
49
50 static int proto_bootp = -1;
51 static int hf_bootp_type = -1;
52 static int hf_bootp_hw_type = -1;
53 static int hf_bootp_hw_len = -1;
54 static int hf_bootp_hops = -1;
55 static int hf_bootp_id = -1;
56 static int hf_bootp_secs = -1;
57 static int hf_bootp_flag = -1;
58 static int hf_bootp_ip_client = -1;
59 static int hf_bootp_ip_your = -1;
60 static int hf_bootp_ip_server = -1;
61 static int hf_bootp_ip_relay = -1;
62 static int hf_bootp_hw_addr = -1;
63 static int hf_bootp_server = -1;
64 static int hf_bootp_file = -1;
65 static int hf_bootp_cookie = -1;
66 static int hf_bootp_dhcp = -1;
67
68 static guint ett_bootp = -1;
69 static guint ett_bootp_option = -1;
70
71 #define UDP_PORT_BOOTPS  67
72
73 enum field_type { none, ipv4, string, toggle, yes_no, special, opaque,
74         time_in_secs,
75         val_u_byte, val_u_short, val_u_long,
76         val_s_long };
77
78 struct opt_info {
79         char    *text;
80         enum field_type ftype;
81 };
82
83 #define NUM_OPT_INFOS 128
84 #define NUM_O63_SUBOPTS 11
85
86 static int dissect_netware_ip_suboption(proto_tree *v_tree, tvbuff_t *tvb,
87     int optp);
88 static int bootp_dhcp_decode_agent_info(proto_tree *v_tree, tvbuff_t *tvb,
89     int optp);
90
91 static const char *
92 get_dhcp_type(guint8 byte)
93 {
94         static const char       *opt53_text[] = {
95                 "Unknown Message Type",
96                 "Discover",
97                 "Offer",
98                 "Request",
99                 "Decline",
100                 "ACK",
101                 "NAK",
102                 "Release",
103                 "Inform"
104         };
105         int i;
106
107         if (byte > 0 && byte < (sizeof opt53_text / sizeof opt53_text[0]))
108                 i = byte;
109         else
110                 i = 0;
111         return opt53_text[i];
112 }
113
114 /* Returns the number of bytes consumed by this option. */
115 static int
116 bootp_option(tvbuff_t *tvb, proto_tree *bp_tree, int voff, int eoff)
117 {
118         char                    *text;
119         enum field_type         ftype;
120         u_char                  code = tvb_get_guint8(tvb, voff);
121         int                     vlen;
122         u_char                  byte;
123         int                     i,optp, consumed;
124         u_long                  time_secs;
125         proto_tree              *v_tree;
126         proto_item              *vti;
127
128         static const value_string nbnt_vals[] = {
129             {0x1,   "B-node" },
130             {0x2,   "P-node" },
131             {0x4,   "M-node" },
132             {0x8,   "H-node" },
133             {0,     NULL     } };
134
135         static struct opt_info opt[] = {
136                 /*   0 */ { "Padding",                                                          none },
137                 /*   1 */ { "Subnet Mask",                                                      ipv4 },
138                 /*   2 */ { "Time Offset",                                                      val_s_long },
139                 /*   3 */ { "Router",                                                           ipv4 },
140                 /*   4 */ { "Time Server",                                                      ipv4 },
141                 /*   5 */ { "Name Server",                                                      ipv4 },
142                 /*   6 */ { "Domain Name Server",                                       ipv4 },
143                 /*   7 */ { "Log Server",                                                       ipv4 },
144                 /*   8 */ { "Cookie Server",                                            ipv4 },
145                 /*   9 */ { "LPR Server",                                                       ipv4 },
146                 /*  10 */ { "Impress Server",                                           ipv4 },
147                 /*  11 */ { "Resource Location Server",                         ipv4 },
148                 /*  12 */ { "Host Name",                                                        string },
149                 /*  13 */ { "Boot File Size",                                           val_u_short },
150                 /*  14 */ { "Merit Dump File",                                          string },
151                 /*  15 */ { "Domain Name",                                                      string },
152                 /*  16 */ { "Swap Server",                                                      ipv4 },
153                 /*  17 */ { "Root Path",                                                        string },
154                 /*  18 */ { "Extensions Path",                                          string },
155                 /*  19 */ { "IP Forwarding",                                            toggle },
156                 /*  20 */ { "Non-Local Source Routing",                         toggle },
157                 /*  21 */ { "Policy Filter",                                            special },
158                 /*  22 */ { "Maximum Datagram Reassembly Size",         val_u_short },
159                 /*  23 */ { "Default IP Time-to-Live",                          val_u_byte },
160                 /*  24 */ { "Path MTU Aging Timeout",                           time_in_secs },
161                 /*  25 */ { "Path MTU Plateau Table",                           val_u_short },
162                 /*  26 */ { "Interface MTU",                                            val_u_short },
163                 /*  27 */ { "All Subnets are Local",                            yes_no },
164                 /*  28 */ { "Broadcast Address",                                        ipv4 },
165                 /*  29 */ { "Perform Mask Discovery",                           toggle },
166                 /*  30 */ { "Mask Supplier",                                            yes_no },
167                 /*  31 */ { "Perform Router Discover",                          toggle },
168                 /*  32 */ { "Router Solicitation Address",                      ipv4 },
169                 /*  33 */ { "Static Route",                                                     special },
170                 /*  34 */ { "Trailer Encapsulation",                            toggle },
171                 /*  35 */ { "ARP Cache Timeout",                                        time_in_secs },
172                 /*  36 */ { "Ethernet Encapsulation",                           toggle },
173                 /*  37 */ { "TCP Default TTL",                                          val_u_byte },
174                 /*  38 */ { "TCP Keepalive Interval",                           time_in_secs },
175                 /*  39 */ { "TCP Keepalive Garbage",                            toggle },
176                 /*  40 */ { "Network Information Service Domain",       string },
177                 /*  41 */ { "Network Information Service Servers",      ipv4 },
178                 /*  42 */ { "Network Time Protocol Servers",            ipv4 },
179                 /*  43 */ { "Vendor-Specific Information",                      special },
180                 /*  44 */ { "NetBIOS over TCP/IP Name Server",          ipv4 },
181                 /*  45 */ { "NetBIOS over TCP/IP Datagram Distribution Name Server", ipv4 },
182                 /*  46 */ { "NetBIOS over TCP/IP Node Type",            special },
183                 /*  47 */ { "NetBIOS over TCP/IP Scope",                        string },
184                 /*  48 */ { "X Window System Font Server",                      ipv4 },
185                 /*  49 */ { "X Window System Display Manager",          ipv4 },
186                 /*  50 */ { "Requested IP Address",                                     ipv4 },
187                 /*  51 */ { "IP Address Lease Time",                            time_in_secs },
188                 /*  52 */ { "Option Overload",                                          special },
189                 /*  53 */ { "DHCP Message Type",                                        special },
190                 /*  54 */ { "Server Identifier",                                        ipv4 },
191                 /*  55 */ { "Parameter Request List",                           special },
192                 /*  56 */ { "Message",                                                          string },
193                 /*  57 */ { "Maximum DHCP Message Size",                        val_u_short },
194                 /*  58 */ { "Renewal Time Value",                                       time_in_secs },
195                 /*  59 */ { "Rebinding Time Value",                                     time_in_secs },
196                 /*  60 */ { "Vendor class identifier",                          opaque },
197                 /*  61 */ { "Client identifier",                                        special },
198                 /*  62 */ { "Novell/Netware IP domain",                                 string },
199                 /*  63 */ { "Novell Options",   special },
200                 /*  64 */ { "Network Information Service+ Domain",      string },
201                 /*  65 */ { "Network Information Service+ Servers",     ipv4 },
202                 /*  66 */ { "TFTP Server Name",                                         string },
203                 /*  67 */ { "Bootfile name",                                            string },
204                 /*  68 */ { "Mobile IP Home Agent",                                     ipv4 },
205                 /*  69 */ { "SMTP Server",                                                      ipv4 },
206                 /*  70 */ { "POP3 Server",                                                      ipv4 },
207                 /*  71 */ { "NNTP Server",                                                      ipv4 },
208                 /*  72 */ { "Default WWW Server",                                       ipv4 },
209                 /*  73 */ { "Default Finger Server",                            ipv4 },
210                 /*  74 */ { "Default IRC Server",                                       ipv4 },
211                 /*  75 */ { "StreetTalk Server",                                        ipv4 },
212                 /*  76 */ { "StreetTalk Directory Assistance Server", ipv4 },
213                 /*  77 */ { "User Class Information",                           opaque },
214                 /*  78 */ { "Directory Agent Information",                      opaque },
215                 /*  79 */ { "Service Location Agent Scope",                     opaque },
216                 /*  80 */ { "Naming Authority",                                         opaque },
217                 /*  81 */ { "Client Fully Qualified Domain Name",       opaque },
218                 /*  82 */ { "Agent Information Option",                 special },
219                 /*  83 */ { "Unassigned",                               opaque },
220                 /*  84 */ { "Unassigned",                               opaque },
221                 /*  85 */ { "Novell Directory Services Servers",        opaque },
222                 /*  86 */ { "Novell Directory Services Tree Name",      opaque },
223                 /*  87 */ { "Novell Directory Services Context",        opaque },
224                 /*  88 */ { "IEEE 1003.1 POSIX Timezone",                       opaque },
225                 /*  89 */ { "Fully Qualified Domain Name",                      opaque },
226                 /*  90 */ { "Authentication",                                           opaque },
227                 /*  91 */ { "Vines TCP/IP Server Option",                       opaque },
228                 /*  92 */ { "Server Selection Option",                          opaque },
229                 /*  93 */ { "Client System Architecture",                       opaque },
230                 /*  94 */ { "Client Network Device Interface",          opaque },
231                 /*  95 */ { "Lightweight Directory Access Protocol",    opaque },
232                 /*  96 */ { "IPv6 Transitions",                                         opaque },
233                 /*  97 */ { "UUID/GUID-based Client Identifier",        opaque },
234                 /*  98 */ { "Open Group's User Authentication",         opaque },
235                 /*  99 */ { "Unassigned",                                                       opaque },
236                 /* 100 */ { "Printer Name",                                                     opaque },
237                 /* 101 */ { "MDHCP multicast address",                          opaque },
238                 /* 102 */ { "Removed/unassigned",                                       opaque },
239                 /* 103 */ { "Removed/unassigned",                                       opaque },
240                 /* 104 */ { "Removed/unassigned",                                       opaque },
241                 /* 105 */ { "Removed/unassigned",                                       opaque },
242                 /* 106 */ { "Removed/unassigned",                                       opaque },
243                 /* 107 */ { "Removed/unassigned",                                       opaque },
244                 /* 108 */ { "Swap Path Option",                                         opaque },
245                 /* 109 */ { "Unassigned",                                                       opaque },
246                 /* 110 */ { "IPX Compability",                                          opaque },
247                 /* 111 */ { "Unassigned",                                                       opaque },
248                 /* 112 */ { "Netinfo Parent Server Address",            opaque },
249                 /* 113 */ { "Netinfo Parent Server Tag",                        opaque },
250                 /* 114 */ { "URL",                                                                      opaque },
251                 /* 115 */ { "DHCP Failover Protocol",                           opaque },
252                 /* 116 */ { "DHCP Auto-Configuration",                          opaque },
253                 /* 117 */ { "Unassigned",                                                       opaque },
254                 /* 118 */ { "Unassigned",                                                       opaque },
255                 /* 119 */ { "Unassigned",                                                       opaque },
256                 /* 120 */ { "Unassigned",                                                       opaque },
257                 /* 121 */ { "Unassigned",                                                       opaque },
258                 /* 122 */ { "Unassigned",                                                       opaque },
259                 /* 123 */ { "Unassigned",                                                       opaque },
260                 /* 124 */ { "Unassigned",                                                       opaque },
261                 /* 125 */ { "Unassigned",                                                       opaque },
262                 /* 126 */ { "Extension",                                                        opaque },
263                 /* 127 */ { "Extension",                                                        opaque }
264         };
265
266         /* Options whose length isn't "vlen + 2". */
267         switch (code) {
268
269         case 0:         /* Padding */
270                 /* check how much padding we have */
271                 for (i = voff + 1; i < eoff; i++ ) {
272                         if (tvb_get_guint8(tvb, i) != 0) {
273                                 break;
274                         }
275                 }
276                 i = i - voff;
277                 if (bp_tree != NULL)
278                         proto_tree_add_text(bp_tree, tvb, voff, i, "Padding");
279                 consumed = i;
280                 return consumed;
281                 break;
282
283         case 255:       /* End Option */
284                 if (bp_tree != NULL)
285                         proto_tree_add_text(bp_tree, tvb, voff, 1, "End Option");
286                 consumed = 1;
287                 return consumed;
288         }
289
290         /*
291          * Get the length of the option, and the number of bytes it
292          * consumes (the length doesn't include the option code or
293          * length bytes).
294          */
295         vlen = tvb_get_guint8(tvb, voff+1);
296         consumed = vlen + 2;
297         if (bp_tree == NULL) {
298                 /* Don't put anything in the protocol tree. */
299                 return consumed;
300         }
301
302         text = opt[code].text;
303         /* Special cases */
304         switch (code) {
305
306         case 21:        /* Policy Filter */
307                 if (vlen == 8) {
308                         /* one IP address pair */
309                         proto_tree_add_text(bp_tree, tvb, voff, consumed,
310                                 "Option %d: %s = %s/%s", code, text,
311                                 ip_to_str(tvb_get_ptr(tvb, voff+2, 4)),
312                                 ip_to_str(tvb_get_ptr(tvb, voff+6, 4)));
313                 } else {
314                         /* > 1 IP address pair. Let's make a sub-tree */
315                         vti = proto_tree_add_text(bp_tree, tvb, voff,
316                                 consumed, "Option %d: %s", code, text);
317                         v_tree = proto_item_add_subtree(vti, ett_bootp_option);
318                         for (i = voff + 2; i < voff + consumed; i += 8) {
319                                 proto_tree_add_text(v_tree, tvb, i, 8, "IP Address/Mask: %s/%s",
320                                         ip_to_str(tvb_get_ptr(tvb, i, 4)),
321                                         ip_to_str(tvb_get_ptr(tvb, i+4, 4)));
322                         }
323                 }
324                 break;
325
326         case 33:        /* Static Route */
327                 if (vlen == 8) {
328                         /* one IP address pair */
329                         proto_tree_add_text(bp_tree, tvb, voff, consumed,
330                                 "Option %d: %s = %s/%s", code, text,
331                                 ip_to_str(tvb_get_ptr(tvb, voff+2, 4)),
332                                 ip_to_str(tvb_get_ptr(tvb, voff+6, 4)));
333                 } else {
334                         /* > 1 IP address pair. Let's make a sub-tree */
335                         vti = proto_tree_add_text(bp_tree, tvb, voff,
336                                 consumed, "Option %d: %s", code, text);
337                         v_tree = proto_item_add_subtree(vti, ett_bootp_option);
338                         for (i = voff + 2; i < voff + consumed; i += 8) {
339                                 proto_tree_add_text(v_tree, tvb, i, 8,
340                                         "Destination IP Address/Router: %s/%s",
341                                         ip_to_str(tvb_get_ptr(tvb, i, 4)),
342                                         ip_to_str(tvb_get_ptr(tvb, i+4, 4)));
343                         }
344                 }
345                 break;
346
347         case 43:        /* Vendor-Specific Info */
348                 proto_tree_add_text(bp_tree, tvb, voff, consumed,
349                                 "Option %d: %s", code, text);
350                 break;
351
352         case 46:        /* NetBIOS-over-TCP/IP Node Type */
353                 byte = tvb_get_guint8(tvb, voff+2);
354                 proto_tree_add_text(bp_tree, tvb, voff, consumed,
355                                 "Option %d: %s = %s", code, text,
356                                 val_to_str(byte, nbnt_vals,
357                                     "Unknown (0x%02x)"));
358                 break;
359                                 
360         case 53:        /* DHCP Message Type */
361                 proto_tree_add_text(bp_tree, tvb, voff, 3, "Option %d: %s = DHCP %s",
362                         code, text, get_dhcp_type(tvb_get_guint8(tvb, voff+2)));
363                 break;
364
365         case 55:        /* Parameter Request List */
366                 vti = proto_tree_add_text(bp_tree, tvb, voff,
367                         vlen + 2, "Option %d: %s", code, text);
368                 v_tree = proto_item_add_subtree(vti, ett_bootp_option);
369                 for (i = 0; i < vlen; i++) {
370                         byte = tvb_get_guint8(tvb, voff+2+i);
371                         if (byte < NUM_OPT_INFOS) {
372                                 proto_tree_add_text(v_tree, tvb, voff+2+i, 1, "%d = %s",
373                                                 byte, opt[byte].text);
374                         } else {
375                                 proto_tree_add_text(vti, tvb, voff+2+i, 1,
376                                         "Unknown Option Code: %d", byte);
377                         }
378                 }
379                 break;
380
381         case 61:        /* Client Identifier */
382                 /* We *MAY* use hwtype/hwaddr. If we have 7 bytes, I'll
383                    guess that the first is the hwtype, and the last 6
384                    are the hw addr */
385                 if (vlen == 7) {
386                         guint8 htype;
387
388                         vti = proto_tree_add_text(bp_tree, tvb, voff,
389                                 consumed, "Option %d: %s", code, text);
390                         v_tree = proto_item_add_subtree(vti, ett_bootp_option);
391                         htype = tvb_get_guint8(tvb, voff+2);
392                         proto_tree_add_text(v_tree, tvb, voff+2, 1,
393                                 "Hardware type: %s",
394                                 arphrdtype_to_str(htype,
395                                         "Unknown (0x%02x)"));
396                         proto_tree_add_text(v_tree, tvb, voff+3, 6,
397                                 "Client hardware address: %s",
398                                 arphrdaddr_to_str(tvb_get_ptr(tvb, voff+3, 6),
399                                         6, htype));
400                 } else {
401                         /* otherwise, it's opaque data */
402                         proto_tree_add_text(bp_tree, tvb, voff, consumed,
403                                 "Option %d: %s (%d bytes)", code, text, vlen);
404                 }
405                 break;
406
407         case 63:        /* NetWare/IP options */
408                 vti = proto_tree_add_text(bp_tree, tvb, voff,
409                     consumed, "Option %d: %s", code, text);
410                 v_tree = proto_item_add_subtree(vti, ett_bootp_option);
411
412                 optp = voff+2;
413                 while (optp < voff+consumed)
414                         optp = dissect_netware_ip_suboption(v_tree, tvb, optp);
415                 break;
416
417         case 82:        /* Relay Agent Information Option */
418                 vti = proto_tree_add_text(bp_tree, tvb, voff, consumed,
419                                           "Option %d: %s (%d bytes)",
420                                           code, text, vlen);
421                 v_tree = proto_item_add_subtree(vti, ett_bootp_option);
422                 optp = voff+2;
423                 while (optp < voff+consumed) {
424                         optp = bootp_dhcp_decode_agent_info(v_tree, tvb, optp);
425                 }
426                 break;
427
428         default:        /* not special */
429                 break;
430         }
431
432         /* Normal cases */
433         if (code < NUM_OPT_INFOS) {
434                 text = opt[code].text;
435                 ftype = opt[code].ftype;
436
437                 switch (ftype) {
438
439                 case special:
440                         return consumed;
441
442                 case ipv4:
443                         if (vlen == 4) {
444                                 /* one IP address */
445                                 proto_tree_add_text(bp_tree, tvb, voff, consumed,
446                                         "Option %d: %s = %s", code, text,
447                                         ip_to_str(tvb_get_ptr(tvb, voff+2, 4)));
448                         } else {
449                                 /* > 1 IP addresses. Let's make a sub-tree */
450                                 vti = proto_tree_add_text(bp_tree, tvb, voff,
451                                         consumed, "Option %d: %s", code, text);
452                                 v_tree = proto_item_add_subtree(vti, ett_bootp_option);
453                                 for (i = voff + 2; i < voff + consumed; i += 4) {
454                                         proto_tree_add_text(v_tree, tvb, i, 4, "IP Address: %s",
455                                                 ip_to_str(tvb_get_ptr(tvb, i, 4)));
456                                 }
457                         }
458                         break;
459
460                 case string:
461                         /* Fix for non null-terminated string supplied by
462                          * John Lines <John.Lines@aeat.co.uk>
463                          */
464                         proto_tree_add_text(bp_tree, tvb, voff, consumed,
465                                         "Option %d: %s = %.*s", code, text, vlen,
466                                         tvb_get_ptr(tvb, voff+2, consumed-2));
467                         break;
468
469                 case opaque:
470                         proto_tree_add_text(bp_tree, tvb, voff, consumed,
471                                         "Option %d: %s (%d bytes)",
472                                         code, text, vlen);
473                         break;
474
475                 case val_u_short:
476                         if (vlen == 2) {
477                                 /* one u_short */
478                                 proto_tree_add_text(bp_tree, tvb, voff, consumed,
479                                                 "Option %d: %s = %d", code, text,
480                                                 tvb_get_ntohs(tvb, voff+2));
481                         } else {
482                                 /* > 1 u_short */
483                                 vti = proto_tree_add_text(bp_tree, tvb, voff,
484                                         consumed, "Option %d: %s", code, text);
485                                 v_tree = proto_item_add_subtree(vti, ett_bootp_option);
486                                 for (i = voff + 2; i < voff + consumed; i += 2) {
487                                         proto_tree_add_text(v_tree, tvb, i, 4, "Value: %d",
488                                                 tvb_get_ntohs(tvb, i));
489                                 }
490                         }
491                         break;
492
493                 case val_u_long:
494                         proto_tree_add_text(bp_tree, tvb, voff, consumed,
495                                         "Option %d: %s = %d", code, text,
496                                         tvb_get_ntohl(tvb, voff+2));
497                         break;
498
499                 case val_u_byte:
500                         proto_tree_add_text(bp_tree, tvb, voff, consumed,
501                                         "Option %d: %s = %d", code, text,
502                                         tvb_get_guint8(tvb, voff+2));
503                         break;
504
505                 case toggle:
506                         i = tvb_get_guint8(tvb, voff+2);
507                         if (i != 0 && i != 1) {
508                                 proto_tree_add_text(bp_tree, tvb, voff, consumed,
509                                                 "Option %d: %s = Invalid Value %d", code, text,
510                                                 i);
511                         } else {
512                                 proto_tree_add_text(bp_tree, tvb, voff, consumed,
513                                                 "Option %d: %s = %s", code, text,
514                                                 i == 0 ? "Disabled" : "Enabled");
515                         }
516                         break;
517
518                 case yes_no:
519                         i = tvb_get_guint8(tvb, voff+2);
520                         if (i != 0 && i != 1) {
521                                 proto_tree_add_text(bp_tree, tvb, voff, consumed,
522                                                 "Option %d: %s = Invalid Value %d", code, text,
523                                                 i);
524                         } else {
525                                 proto_tree_add_text(bp_tree, tvb, voff, consumed,
526                                                 "Option %d: %s = %s", code, text,
527                                                 i == 0 ? "No" : "Yes");
528                         }
529                         break;
530
531                 case time_in_secs:
532                         time_secs = tvb_get_ntohl(tvb, voff+2);
533                         proto_tree_add_text(bp_tree, tvb, voff, consumed,
534                                 "Option %d: %s = %s", code, text,
535                                 ((time_secs == 0xffffffff) ?
536                                     "infinity" :
537                                     time_secs_to_str(time_secs)));
538                         break;
539
540                 default:
541                         proto_tree_add_text(bp_tree, tvb, voff, consumed,
542                                         "Option %d: %s (%d bytes)", code, text, vlen);
543                 }
544         } else {
545                 proto_tree_add_text(bp_tree, tvb, voff, consumed,
546                                 "Unknown Option Code: %d (%d bytes)", code, vlen);
547         }
548
549         return consumed;
550 }
551
552 static int
553 bootp_dhcp_decode_agent_info(proto_tree *v_tree, tvbuff_t *tvb, int optp)
554 {
555         guint8 subopt;
556         guint8 subopt_len;
557         
558         subopt = tvb_get_guint8(tvb, optp);
559         subopt_len = tvb_get_guint8(tvb, optp+1);
560         switch (subopt) {
561         case 1:
562                 proto_tree_add_text(v_tree, tvb, optp, subopt_len + 2,
563                                     "Agent Circuit ID (%d bytes)", subopt_len);
564                 break;
565         case 2:
566                 proto_tree_add_text(v_tree, tvb, optp, subopt_len + 2,
567                                     "Agent Remote ID (%d bytes)", subopt_len);
568                 break;
569         default:
570                 proto_tree_add_text(v_tree, tvb, optp, subopt_len + 2,
571                                     "Unknown agent option: %d", subopt);
572                 break;
573         }
574         optp += (subopt_len + 2);
575         return optp;
576 }
577
578 static int
579 dissect_netware_ip_suboption(proto_tree *v_tree, tvbuff_t *tvb, int optp)
580 {
581         guint8 subopt;
582         guint8 subopt_len;
583         int slask;
584         proto_tree *o63_v_tree;
585         proto_item *vti;
586
587         struct o63_opt_info { 
588                 char    *truet;
589                 char    *falset;
590                 enum field_type ft;
591         };
592
593         static struct o63_opt_info o63_opt[]= {
594                 /* 0 */ {"","",none},
595                 /* 1 */ {"NWIP does not exist on subnet","",string},
596                 /* 2 */ {"NWIP exist in options area","",string},
597                 /* 3 */ {"NWIP exists in sname/file","",string},
598                 /* 4 */ {"NWIP exists, but too big","",string},
599                 /* 5 */ {"Broadcast for nearest Netware server","Do NOT Broadcast for nearest Netware server",yes_no}, 
600                 /* 6 */ {"Preferred DSS server","",ipv4},
601                 /* 7 */ {"Nearest NWIP server","",ipv4},
602                 /* 8 */ {"Autoretries","",val_u_short},
603                 /* 9 */ {"Autoretry delay, secs ","",val_u_short},
604                 /* 10*/ {"Support NetWare/IP v1.1","Do NOT support NetWare/IP v1.1",yes_no},
605                 /* 11*/ {"Primary DSS ", "" , special}
606         };
607                 
608         subopt = tvb_get_guint8(tvb, optp);
609         if (subopt > NUM_O63_SUBOPTS) {
610                 proto_tree_add_text(v_tree, tvb,optp,1,"Unknown suboption %d", subopt);
611                 optp++;
612         } else {
613                 switch (o63_opt[subopt].ft) {
614
615                 case string:
616                         proto_tree_add_text(v_tree, tvb, optp, 2, "Suboption %d: %s", subopt, o63_opt[subopt].truet);
617                         optp+=2;
618                         break;
619
620                 case yes_no:
621                         if (tvb_get_guint8(tvb, optp+2)==1) {
622                                 proto_tree_add_text(v_tree, tvb, optp, 3, "Suboption %d: %s", subopt, o63_opt[subopt].truet);
623                         } else {
624                                 proto_tree_add_text(v_tree, tvb, optp, 3, "Suboption %d: %s" , subopt, o63_opt[subopt].falset);
625                         }
626                         optp+=3;
627                         break;
628
629                 case special:   
630                         proto_tree_add_text(v_tree, tvb, optp, 6,
631                             "Suboption %d: %s = %s" ,
632                             subopt, o63_opt[subopt].truet,
633                             ip_to_str(tvb_get_ptr(tvb, optp+2, 4)));
634                         optp=optp+6;
635                         break;
636
637                 case val_u_short:
638                         proto_tree_add_text(v_tree, tvb, optp, 3, "Suboption %d: %s = %u",
639                             subopt, o63_opt[subopt].truet,
640                             tvb_get_guint8(tvb, optp+2));       /* XXX - 1 byte? */
641                         optp+=3;
642                         break;
643                                                         
644                 case ipv4:
645                         subopt_len = tvb_get_guint8(tvb, optp+1);
646                         if (subopt_len == 4) {
647                                 /* one IP address */
648                                 proto_tree_add_text(v_tree, tvb, optp, 6,
649                                     "Suboption %d : %s = %s",
650                                     subopt, o63_opt[subopt].truet,
651                                     ip_to_str(tvb_get_ptr(tvb, optp+2, 4)));
652                                 optp=optp+6;
653                         } else {
654                                 /* > 1 IP addresses. Let's make a sub-tree */
655                                 vti = proto_tree_add_text(v_tree, tvb, optp,
656                                     subopt_len+2, "Suboption %d: %s",
657                                     subopt, o63_opt[subopt].truet);
658                                 o63_v_tree = proto_item_add_subtree(vti, ett_bootp_option);
659                                 for (slask = optp + 2 ; slask < optp+subopt_len; slask += 4) {
660                                         proto_tree_add_text(o63_v_tree, tvb, slask, 4, "IP Address: %s",
661                                             ip_to_str(tvb_get_ptr(tvb, slask, 4)));
662                                 }
663                                 optp=slask;
664                         }
665                         break;
666                 default:
667                         proto_tree_add_text(v_tree, tvb,optp,1,"Unknown suboption %d", subopt);
668                         optp++;
669                         break;
670                 }
671         }
672         return optp;
673 }
674
675 #define BOOTREQUEST     1
676 #define BOOTREPLY       2
677
678 static const value_string op_vals[] = {
679         { BOOTREQUEST,  "Boot Request" },
680         { BOOTREPLY,    "Boot Reply" },
681         { 0,            NULL }
682 };
683
684 static void
685 dissect_bootp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
686 {
687         proto_tree      *bp_tree = NULL;
688         proto_item      *ti;
689         guint8          op;
690         guint8          htype, hlen;
691         const guint8    *haddr;
692         int             voff, eoff; /* vender offset, end offset */
693         guint32         ip_addr;
694         gboolean        is_dhcp = FALSE;
695         const char      *dhcp_type;
696
697         if (check_col(pinfo->fd, COL_PROTOCOL))
698                 col_set_str(pinfo->fd, COL_PROTOCOL, "BOOTP");
699         if (check_col(pinfo->fd, COL_INFO)) {
700                 /*
701                  * In case we throw an exception fetching the opcode, etc.
702                  */
703                 col_clear(pinfo->fd, COL_INFO);
704         }
705
706         op = tvb_get_guint8(tvb, 0);
707         htype = tvb_get_guint8(tvb, 1);
708         hlen = tvb_get_guint8(tvb, 2);
709         if (check_col(pinfo->fd, COL_INFO)) {
710                 switch (op) {
711
712                 case BOOTREQUEST:
713                         col_add_fstr(pinfo->fd, COL_INFO, "Boot Request from %s",
714                                 arphrdaddr_to_str(tvb_get_ptr(tvb, 28, hlen),
715                                         hlen, htype));
716                         break;
717
718                 case BOOTREPLY:
719                         col_set_str(pinfo->fd, COL_INFO, "Boot Reply");
720                         break;
721
722                 default:
723                         col_add_fstr(pinfo->fd, COL_INFO, "Unknown BOOTP message type (%u)",
724                             op);
725                         break;
726                 }
727         }
728
729         if (tree) {
730                 ti = proto_tree_add_item(tree, proto_bootp, tvb, 0,
731                     tvb_length(tvb), FALSE);
732                 bp_tree = proto_item_add_subtree(ti, ett_bootp);
733
734                 proto_tree_add_uint(bp_tree, hf_bootp_type, tvb, 
735                                            0, 1,
736                                            op);
737                 proto_tree_add_uint_format(bp_tree, hf_bootp_hw_type, tvb,
738                                            1, 1,
739                                            htype,
740                                            "Hardware type: %s",
741                                            arphrdtype_to_str(htype,
742                                                              "Unknown (0x%02x)"));
743                 proto_tree_add_uint(bp_tree, hf_bootp_hw_len, tvb,
744                                     2, 1, hlen);
745                 proto_tree_add_item(bp_tree, hf_bootp_hops, tvb,
746                                     3, 1, FALSE);
747                 proto_tree_add_item(bp_tree, hf_bootp_id, tvb,
748                                     4, 4, FALSE);
749                 proto_tree_add_item(bp_tree, hf_bootp_secs, tvb,
750                                     8, 2, FALSE);
751                 proto_tree_add_uint(bp_tree, hf_bootp_flag, tvb,
752                                     10, 2, tvb_get_ntohs(tvb, 10) & 0x8000);
753
754                 proto_tree_add_item(bp_tree, hf_bootp_ip_client, tvb, 
755                                     12, 4, FALSE);
756                 proto_tree_add_item(bp_tree, hf_bootp_ip_your, tvb, 
757                                     16, 4, FALSE);
758                 proto_tree_add_item(bp_tree, hf_bootp_ip_server, tvb,
759                                     20, 4, FALSE);
760                 proto_tree_add_item(bp_tree, hf_bootp_ip_relay, tvb,
761                                     24, 4, FALSE);
762
763                 if (hlen > 0) {
764                         haddr = tvb_get_ptr(tvb, 28, hlen);
765                         proto_tree_add_bytes_format(bp_tree, hf_bootp_hw_addr, tvb, 
766                                                    28, hlen,
767                                                    haddr,
768                                                    "Client hardware address: %s",
769                                                    arphrdaddr_to_str(haddr,
770                                                                      hlen,
771                                                                      htype));
772                 }
773                 else {
774                         proto_tree_add_text(bp_tree,  tvb,
775                                                    28, 0, "Client address not given");
776                 }
777
778                 /* The server host name is optional */
779                 if (tvb_get_guint8(tvb, 44) != '\0') {
780                         proto_tree_add_item(bp_tree, hf_bootp_server, tvb,
781                                                    44, 64, FALSE);
782                 }
783                 else {
784                         proto_tree_add_string_format(bp_tree, hf_bootp_server, tvb,
785                                                    44, 64,
786                                                    tvb_get_ptr(tvb, 44, 1),
787                                                    "Server host name not given");
788                 }
789
790                 /* Boot file */
791                 if (tvb_get_guint8(tvb, 108) != '\0') {
792                         proto_tree_add_item(bp_tree, hf_bootp_file, tvb,
793                                                    108, 128, FALSE);
794                 }
795                 else {
796                         proto_tree_add_string_format(bp_tree, hf_bootp_file, tvb,
797                                                    108, 128,
798                                                    tvb_get_ptr(tvb, 108, 1),
799                                                    "Boot file name not given");
800                 }
801
802                 tvb_memcpy(tvb, (void *)&ip_addr, 236, sizeof(ip_addr));
803                 if (tvb_get_ntohl(tvb, 236) == 0x63825363) {
804                         proto_tree_add_ipv4_format(bp_tree, hf_bootp_cookie, tvb,
805                                             236, 4, ip_addr,
806                                             "Magic cookie: (OK)");
807                 }
808                 else {
809                         proto_tree_add_ipv4(bp_tree, hf_bootp_cookie, tvb,
810                                             236, 4, ip_addr);
811                 }
812         }
813
814         voff = 240;
815         eoff = tvb_reported_length(tvb);
816         while (voff < eoff) {
817                 /* Handle the DHCP option specially here, so that we
818                    can flag DHCP packets as such. */
819                 if (!is_dhcp && tvb_get_guint8(tvb, voff) == 53) {
820                         /*
821                          * We haven't already seen a DHCP Type option, and
822                          * this is a DHCP Type option, so flag this packet
823                          * as DHCP.
824                          */
825                         dhcp_type = get_dhcp_type(tvb_get_guint8(tvb, voff+2));
826                         if (check_col(pinfo->fd, COL_PROTOCOL))
827                                 col_set_str(pinfo->fd, COL_PROTOCOL, "DHCP");
828                         if (check_col(pinfo->fd, COL_INFO))
829                                 col_add_fstr(pinfo->fd, COL_INFO, "DHCP %-8s - Transaction ID 0x%x",
830                                     dhcp_type, tvb_get_ntohl(tvb, 4));
831                         if (tree)
832                                 proto_tree_add_boolean_hidden(bp_tree, hf_bootp_dhcp,
833                                     tvb, 0, 0, 1);
834                         is_dhcp = TRUE;
835                 }
836                 voff += bootp_option(tvb, bp_tree, voff, eoff);
837         }
838 }
839
840 void
841 proto_register_bootp(void)
842 {
843   static hf_register_info hf[] = {
844     { &hf_bootp_dhcp,
845       { "Frame is DHCP",                "bootp.dhcp",    FT_BOOLEAN,
846         BASE_NONE,                      NULL,            0x0,
847         "" }},                            
848                       
849     { &hf_bootp_type,
850       { "Message type",                 "bootp.type",    FT_UINT8,
851          BASE_DEC,                      VALS(op_vals),   0x0,
852         "" }},
853
854     { &hf_bootp_hw_type,
855       { "Hardware type",                "bootp.hw.type", FT_UINT8,
856         BASE_HEX,                       NULL,            0x0,
857         "" }},
858
859     { &hf_bootp_hw_len,
860       { "Hardware address length",      "bootp.hw.len",  FT_UINT8,
861         BASE_DEC,                       NULL,            0x0,
862         "" }},
863
864     { &hf_bootp_hops,
865       { "Hops",                         "bootp.hops",    FT_UINT8,
866         BASE_DEC,                       NULL,            0x0,
867         "" }},
868
869     { &hf_bootp_id,
870       { "Transaction ID",               "bootp.id",      FT_UINT32,
871         BASE_HEX,                        NULL,           0x0,
872         "" }},
873
874     { &hf_bootp_secs,
875       { "Seconds elapsed",              "bootp.secs",    FT_UINT16,
876         BASE_DEC,                        NULL,           0x0,
877         "" }},
878
879     { &hf_bootp_flag,
880       { "Broadcast flag",               "bootp.flag",    FT_UINT16,
881         BASE_HEX,                       NULL,            0x0,
882         "" }},
883
884     { &hf_bootp_ip_client,
885       { "Client IP address",            "bootp.ip.client",FT_IPv4,
886         BASE_NONE,                      NULL,             0x0,
887         "" }},
888
889     { &hf_bootp_ip_your,
890       { "Your (client) IP address",     "bootp.ip.your",  FT_IPv4,
891         BASE_NONE,                      NULL,             0x0,
892         "" }},
893
894     { &hf_bootp_ip_server,
895       { "Next server IP address",       "bootp.ip.server",FT_IPv4,
896         BASE_NONE,                      NULL,             0x0,
897         "" }},
898
899     { &hf_bootp_ip_relay,
900       { "Relay agent IP address",       "bootp.ip.relay", FT_IPv4,
901         BASE_NONE,                      NULL,             0x0,
902         "" }},
903
904     { &hf_bootp_hw_addr,
905       { "Client hardware address",      "bootp.hw.addr", FT_BYTES,
906         BASE_NONE,                      NULL,            0x0,
907         "" }},
908
909     { &hf_bootp_server,
910       { "Server host name",             "bootp.server",  FT_STRING,
911         BASE_NONE,                      NULL,            0x0,
912         "" }},
913
914     { &hf_bootp_file,
915       { "Boot file name",               "bootp.file",    FT_STRING,
916         BASE_NONE,                      NULL,            0x0,
917         "" }},
918
919     { &hf_bootp_cookie,
920       { "Magic cookie",                 "bootp.cookie",  FT_IPv4,
921          BASE_NONE,                     NULL,            0x0,
922         "" }},
923   };
924   static gint *ett[] = {
925     &ett_bootp,
926     &ett_bootp_option,
927   };
928   
929   proto_bootp = proto_register_protocol("Bootstrap Protocol", "BOOTP/DHCP",
930                                         "bootp");
931   proto_register_field_array(proto_bootp, hf, array_length(hf));
932   proto_register_subtree_array(ett, array_length(ett));
933 }
934
935 void
936 proto_reg_handoff_bootp(void)
937 {
938   dissector_add("udp.port", UDP_PORT_BOOTPS, dissect_bootp, proto_bootp);
939 }