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