3 DHCP options parsing and reassembly. */
6 * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of The Internet Software Consortium nor the names
19 * of its contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * This software has been written for the Internet Software Consortium
37 * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38 * Enterprises. To learn more about the Internet Software Consortium,
39 * see ``http://www.vix.com/isc''. To learn more about Vixie
40 * Enterprises, see ``http://www.vix.com''.
44 static char copyright[] =
45 "$Id: options.c,v 1.26.2.10 1999/05/06 21:54:34 mellon Exp $ Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium. All rights reserved.\n";
48 #define DHCP_OPTION_DATA
52 /* Parse all available options out of the specified packet. */
54 void parse_options (packet)
55 struct packet *packet;
57 /* Initially, zero all option pointers. */
58 memset (packet -> options, 0, sizeof (packet -> options));
60 /* If we don't see the magic cookie, there's nothing to parse. */
61 if (memcmp (packet -> raw -> options, DHCP_OPTIONS_COOKIE, 4)) {
62 packet -> options_valid = 0;
66 /* Go through the options field, up to the end of the packet
68 parse_option_buffer (packet, &packet -> raw -> options [4],
69 packet -> packet_length - DHCP_FIXED_NON_UDP - 4);
70 /* If we parsed a DHCP Option Overload option, parse more
71 options out of the buffer(s) containing them. */
72 if (packet -> options_valid
73 && packet -> options [DHO_DHCP_OPTION_OVERLOAD].data) {
74 if (packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 1)
75 parse_option_buffer (packet,
77 packet -> raw -> file,
78 sizeof packet -> raw -> file);
79 if (packet -> options [DHO_DHCP_OPTION_OVERLOAD].data [0] & 2)
80 parse_option_buffer (packet,
82 packet -> raw -> sname,
83 sizeof packet -> raw -> sname);
87 /* Parse options out of the specified buffer, storing addresses of option
88 values in packet -> options and setting packet -> options_valid if no
89 errors are encountered. */
91 void parse_option_buffer (packet, buffer, length)
92 struct packet *packet;
93 unsigned char *buffer;
97 unsigned char *end = buffer + length;
101 for (s = buffer; *s != DHO_END && s < end; ) {
103 /* Pad options don't have a length - just skip them. */
104 if (code == DHO_PAD) {
108 /* All other fields (except end, see above) have a
112 /* If the length is outrageous, the options are bad. */
113 if (s + len + 2 > end) {
114 warn ("Option %s length %d overflows input buffer.",
115 dhcp_options [code].name,
117 packet -> options_valid = 0;
120 /* If we haven't seen this option before, just make
121 space for it and copy it there. */
122 if (!packet -> options [code].data) {
123 if (!(t = ((unsigned char *)
124 dmalloc (len + 1, "parse_option_buffer"))))
125 error ("Can't allocate storage for option %s.",
126 dhcp_options [code].name);
127 /* Copy and NUL-terminate the option (in case it's an
129 memcpy (t, &s [2], len);
131 packet -> options [code].len = len;
132 packet -> options [code].data = t;
134 /* If it's a repeat, concatenate it to whatever
135 we last saw. This is really only required
136 for clients, but what the heck... */
137 t = ((unsigned char *)
138 dmalloc (len + packet -> options [code].len + 1,
139 "parse_option_buffer"));
141 error ("Can't expand storage for option %s.",
142 dhcp_options [code].name);
143 memcpy (t, packet -> options [code].data,
144 packet -> options [code].len);
145 memcpy (t + packet -> options [code].len,
147 packet -> options [code].len += len;
148 t [packet -> options [code].len] = 0;
149 dfree (packet -> options [code].data,
150 "parse_option_buffer");
151 packet -> options [code].data = t;
155 packet -> options_valid = 1;
158 /* cons options into a big buffer, and then split them out into the
159 three seperate buffers if needed. This allows us to cons up a set
160 of vendor options using the same routine. */
162 int cons_options (inpacket, outpacket, mms,
163 options, overload, terminate, bootpp, prl, prl_len)
164 struct packet *inpacket;
165 struct dhcp_packet *outpacket;
167 struct tree_cache **options;
168 int overload; /* Overload flags that may be set. */
174 unsigned char priority_list [300];
176 unsigned char buffer [4096]; /* Really big buffer... */
177 int main_buffer_size;
178 int mainbufix, bufix;
182 /* If the client has provided a maximum DHCP message size,
183 use that; otherwise, if it's BOOTP, only 64 bytes; otherwise
184 use up to the minimum IP MTU size (576 bytes). */
185 /* XXX if a BOOTP client specifies a max message size, we will
189 inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].data &&
190 (inpacket -> options [DHO_DHCP_MAX_MESSAGE_SIZE].len >=
192 mms = getUShort (inpacket -> options
193 [DHO_DHCP_MAX_MESSAGE_SIZE].data);
195 /* If the client has provided a maximum DHCP message size,
196 use that; otherwise, if it's BOOTP, only 64 bytes; otherwise
197 use up to the minimum IP MTU size (576 bytes). */
198 /* XXX if a BOOTP client specifies a max message size, we will
201 main_buffer_size = mms - DHCP_FIXED_LEN;
203 main_buffer_size = 64;
205 main_buffer_size = 576 - DHCP_FIXED_LEN;
207 if (main_buffer_size > sizeof buffer)
208 main_buffer_size = sizeof buffer;
210 /* Preload the option priority list with mandatory options. */
212 priority_list [priority_len++] = DHO_DHCP_MESSAGE_TYPE;
213 priority_list [priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
214 priority_list [priority_len++] = DHO_DHCP_LEASE_TIME;
215 priority_list [priority_len++] = DHO_DHCP_MESSAGE;
217 /* If the client has provided a list of options that it wishes
218 returned, use it to prioritize. Otherwise, prioritize
219 based on the default priority list. */
222 inpacket -> options [DHO_DHCP_PARAMETER_REQUEST_LIST].data) {
223 int prlen = (inpacket ->
224 options [DHO_DHCP_PARAMETER_REQUEST_LIST].len);
225 if (prlen + priority_len > sizeof priority_list)
226 prlen = (sizeof priority_list) - priority_len;
228 memcpy (&priority_list [priority_len],
230 [DHO_DHCP_PARAMETER_REQUEST_LIST].data), prlen);
231 priority_len += prlen;
234 if (prl_len + priority_len > sizeof priority_list)
235 prl_len = (sizeof priority_list) - priority_len;
237 memcpy (&priority_list [priority_len], prl, prl_len);
238 priority_len += prl_len;
241 memcpy (&priority_list [priority_len],
242 dhcp_option_default_priority_list,
243 sizeof_dhcp_option_default_priority_list);
244 priority_len += sizeof_dhcp_option_default_priority_list;
247 /* Copy the options into the big buffer... */
248 option_size = store_options (buffer,
249 (main_buffer_size - 7 +
250 ((overload & 1) ? DHCP_FILE_LEN : 0) +
251 ((overload & 2) ? DHCP_SNAME_LEN : 0)),
252 options, priority_list, priority_len,
255 ((overload & 1) ? DHCP_FILE_LEN : 0)),
258 /* Put the cookie up front... */
259 memcpy (outpacket -> options, DHCP_OPTIONS_COOKIE, 4);
262 /* If we're going to have to overload, store the overload
263 option at the beginning. If we can, though, just store the
264 whole thing in the packet's option buffer and leave it at
266 if (option_size <= main_buffer_size - mainbufix) {
267 memcpy (&outpacket -> options [mainbufix],
268 buffer, option_size);
269 mainbufix += option_size;
270 if (mainbufix < main_buffer_size)
271 outpacket -> options [mainbufix++]
273 length = DHCP_FIXED_NON_UDP + mainbufix;
275 outpacket -> options [mainbufix++] =
276 DHO_DHCP_OPTION_OVERLOAD;
277 outpacket -> options [mainbufix++] = 1;
278 if (option_size > main_buffer_size - mainbufix + DHCP_FILE_LEN)
279 outpacket -> options [mainbufix++] = 3;
281 outpacket -> options [mainbufix++] = 1;
283 memcpy (&outpacket -> options [mainbufix],
284 buffer, main_buffer_size - mainbufix);
285 bufix = main_buffer_size - mainbufix;
286 length = DHCP_FIXED_NON_UDP + mainbufix;
288 if (option_size - bufix <= DHCP_FILE_LEN) {
289 memcpy (outpacket -> file,
290 &buffer [bufix], option_size - bufix);
291 mainbufix = option_size - bufix;
292 if (mainbufix < DHCP_FILE_LEN)
293 outpacket -> file [mainbufix++]
295 while (mainbufix < DHCP_FILE_LEN)
296 outpacket -> file [mainbufix++]
299 memcpy (outpacket -> file,
300 &buffer [bufix], DHCP_FILE_LEN);
301 bufix += DHCP_FILE_LEN;
304 if ((overload & 2) && option_size < bufix) {
305 memcpy (outpacket -> sname,
306 &buffer [bufix], option_size - bufix);
308 mainbufix = option_size - bufix;
309 if (mainbufix < DHCP_SNAME_LEN)
310 outpacket -> file [mainbufix++]
312 while (mainbufix < DHCP_SNAME_LEN)
313 outpacket -> file [mainbufix++]
320 /* Store all the requested options into the requested buffer. */
322 int store_options (buffer, buflen, options, priority_list, priority_len,
323 first_cutoff, second_cutoff, terminate)
324 unsigned char *buffer;
326 struct tree_cache **options;
327 unsigned char *priority_list;
329 int first_cutoff, second_cutoff;
333 int option_stored [256];
338 /* Zero out the stored-lengths array. */
339 memset (option_stored, 0, sizeof option_stored);
341 /* Copy out the options in the order that they appear in the
343 for (i = 0; i < priority_len; i++) {
344 /* Code for next option to try to store. */
345 int code = priority_list [i];
348 /* Number of bytes left to store (some may already
349 have been stored by a previous pass). */
352 /* If no data is available for this option, skip it. */
353 if (!options [code]) {
357 /* The client could ask for things that are mandatory,
358 in which case we should avoid storing them twice... */
359 if (option_stored [code])
361 option_stored [code] = 1;
363 /* Find the value of the option... */
364 if (!tree_evaluate (options [code])) {
368 /* We should now have a constant length for the option. */
369 length = options [code] -> len;
371 /* Do we add a NUL? */
372 if (terminate && dhcp_options [code].format [0] == 't') {
379 /* Try to store the option. */
381 /* If the option's length is more than 255, we must store it
382 in multiple hunks. Store 255-byte hunks first. However,
383 in any case, if the option data will cross a buffer
384 boundary, split it across that boundary. */
390 unsigned char incr = length > 255 ? 255 : length;
392 /* If this hunk of the buffer will cross a
393 boundary, only go up to the boundary in this
395 if (bufix < first_cutoff &&
396 bufix + incr > first_cutoff)
397 incr = first_cutoff - bufix;
398 else if (bufix < second_cutoff &&
399 bufix + incr > second_cutoff)
400 incr = second_cutoff - bufix;
402 /* If this option is going to overflow the buffer,
404 if (bufix + 2 + incr > buflen) {
409 /* Everything looks good - copy it in! */
410 buffer [bufix] = code;
411 buffer [bufix + 1] = incr;
412 if (tto && incr == length) {
413 memcpy (buffer + bufix + 2,
414 options [code] -> value + ix,
416 buffer [bufix + 2 + incr - 1] = 0;
418 memcpy (buffer + bufix + 2,
419 options [code] -> value + ix, incr);
429 /* Format the specified option so that a human can easily read it. */
431 char *pretty_print_option (code, data, len, emit_commas, emit_quotes)
438 static char optbuf [32768]; /* XXX */
445 unsigned char *dp = data;
449 /* Code should be between 0 and 255. */
451 error ("pretty_print_option: bad code %d\n", code);
458 /* Figure out the size of the data. */
459 for (i = 0; dhcp_options [code].format [i]; i++) {
461 warn ("%s: Excess information in format string: %s\n",
462 dhcp_options [code].name,
463 &(dhcp_options [code].format [i]));
467 fmtbuf [i] = dhcp_options [code].format [i];
468 switch (dhcp_options [code].format [i]) {
475 for (k = 0; k < len; k++) {
476 if (!isascii (data [k]) ||
513 warn ("%s: garbage in format string: %s\n",
514 dhcp_options [code].name,
515 &(dhcp_options [code].format [i]));
520 /* Check for too few bytes... */
521 if (hunksize > len) {
522 warn ("%s: expecting at least %d bytes; got %d",
523 dhcp_options [code].name,
527 /* Check for too many bytes... */
528 if (numhunk == -1 && hunksize < len)
529 warn ("%s: %d extra bytes",
530 dhcp_options [code].name,
533 /* If this is an array, compute its size. */
535 numhunk = len / hunksize;
536 /* See if we got an exact number of hunks. */
537 if (numhunk > 0 && numhunk * hunksize < len)
538 warn ("%s: %d extra bytes at end of array\n",
539 dhcp_options [code].name,
540 len - numhunk * hunksize);
542 /* A one-hunk array prints the same as a single hunk. */
546 /* Cycle through the array (or hunk) printing the data. */
547 for (i = 0; i < numhunk; i++) {
548 for (j = 0; j < numelem; j++) {
549 switch (fmtbuf [j]) {
553 strcpy (op, (char *)dp);
554 op += strlen ((char *)dp);
560 foo.s_addr = htonl (getULong (dp));
561 strcpy (op, inet_ntoa (foo));
565 sprintf (op, "%ld", (long)getLong (dp));
570 (unsigned long)getULong (dp));
574 sprintf (op, "%d", getShort (dp));
578 sprintf (op, "%d", getUShort (dp));
582 sprintf (op, "%d", *(char *)dp++);
585 sprintf (op, "%d", *dp++);
588 sprintf (op, "%x", *dp++);
591 strcpy (op, *dp++ ? "true" : "false");
594 warn ("Unexpected format code %c", fmtbuf [j]);
597 if (j + 1 < numelem && comma != ':')
600 if (i + 1 < numhunk) {
608 void do_packet (interface, packet, len, from_port, from, hfrom)
609 struct interface_info *interface;
610 struct dhcp_packet *packet;
612 unsigned int from_port;
614 struct hardware *hfrom;
619 if (packet -> hlen > sizeof packet -> chaddr) {
620 note ("Discarding packet with invalid hlen.");
624 memset (&tp, 0, sizeof tp);
626 tp.packet_length = len;
627 tp.client_port = from_port;
628 tp.client_addr = from;
629 tp.interface = interface;
633 if (tp.options_valid &&
634 tp.options [DHO_DHCP_MESSAGE_TYPE].data)
636 tp.options [DHO_DHCP_MESSAGE_TYPE].data [0];
642 /* Free the data associated with the options. */
643 for (i = 0; i < 256; i++) {
644 if (tp.options [i].len && tp.options [i].data)
645 dfree (tp.options [i].data, "do_packet");