xref: /openbsd-src/usr.sbin/dhcpd/options.c (revision 35318e8fe7d95f005335d5d2b39414681f271ddb)
1*35318e8fSkrw /*	$OpenBSD: options.c,v 1.35 2017/02/13 22:33:39 krw Exp $	*/
2e853bc5dShenning 
3c824f21bShenning /* DHCP options parsing and reassembly. */
4e853bc5dShenning 
5e853bc5dShenning /*
6e853bc5dShenning  * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
7e853bc5dShenning  * All rights reserved.
8e853bc5dShenning  *
9e853bc5dShenning  * Redistribution and use in source and binary forms, with or without
10e853bc5dShenning  * modification, are permitted provided that the following conditions
11e853bc5dShenning  * are met:
12e853bc5dShenning  *
13e853bc5dShenning  * 1. Redistributions of source code must retain the above copyright
14e853bc5dShenning  *    notice, this list of conditions and the following disclaimer.
15e853bc5dShenning  * 2. Redistributions in binary form must reproduce the above copyright
16e853bc5dShenning  *    notice, this list of conditions and the following disclaimer in the
17e853bc5dShenning  *    documentation and/or other materials provided with the distribution.
18e853bc5dShenning  * 3. Neither the name of The Internet Software Consortium nor the names
19e853bc5dShenning  *    of its contributors may be used to endorse or promote products derived
20e853bc5dShenning  *    from this software without specific prior written permission.
21e853bc5dShenning  *
22e853bc5dShenning  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
23e853bc5dShenning  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24e853bc5dShenning  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25e853bc5dShenning  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26e853bc5dShenning  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
27e853bc5dShenning  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28e853bc5dShenning  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29e853bc5dShenning  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30e853bc5dShenning  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31e853bc5dShenning  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32e853bc5dShenning  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33e853bc5dShenning  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34e853bc5dShenning  * SUCH DAMAGE.
35e853bc5dShenning  *
36e853bc5dShenning  * This software has been written for the Internet Software Consortium
37e853bc5dShenning  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
38e853bc5dShenning  * Enterprises.  To learn more about the Internet Software Consortium,
39e853bc5dShenning  * see ``http://www.vix.com/isc''.  To learn more about Vixie
40e853bc5dShenning  * Enterprises, see ``http://www.vix.com''.
41e853bc5dShenning  */
42e853bc5dShenning 
43837cddffSkrw #include <sys/types.h>
44837cddffSkrw #include <sys/socket.h>
45c824f21bShenning 
46837cddffSkrw #include <net/if.h>
47837cddffSkrw 
48837cddffSkrw #include <netinet/in.h>
49837cddffSkrw 
50837cddffSkrw #include <stdio.h>
51837cddffSkrw #include <stdlib.h>
52837cddffSkrw #include <string.h>
53837cddffSkrw 
54837cddffSkrw #include "dhcp.h"
55837cddffSkrw #include "tree.h"
56e853bc5dShenning #include "dhcpd.h"
57c525a185Skrw #include "log.h"
58e853bc5dShenning 
59e853bc5dShenning int bad_options = 0;
60e853bc5dShenning int bad_options_max = 5;
61e853bc5dShenning 
62c824f21bShenning void	parse_options(struct packet *);
63c824f21bShenning void	parse_option_buffer(struct packet *, unsigned char *, int);
64cd4ecb23Skrw void	create_priority_list(unsigned char *, unsigned char *, int);
6584f58762Skrw int	store_option_fragment(unsigned char *, int, unsigned char,
6684f58762Skrw 	    int, unsigned char *);
67c824f21bShenning int	store_options(unsigned char *, int, struct tree_cache **,
68c432531bSkrw 	    unsigned char *, int, int);
69e853bc5dShenning 
70c824f21bShenning 
71c824f21bShenning /*
72c824f21bShenning  * Parse all available options out of the specified packet.
73c824f21bShenning  */
74c824f21bShenning void
parse_options(struct packet * packet)75c824f21bShenning parse_options(struct packet *packet)
76e853bc5dShenning {
77e853bc5dShenning 	/* Initially, zero all option pointers. */
78e853bc5dShenning 	memset(packet->options, 0, sizeof(packet->options));
79e853bc5dShenning 
80e853bc5dShenning 	/* If we don't see the magic cookie, there's nothing to parse. */
81e853bc5dShenning 	if (memcmp(packet->raw->options, DHCP_OPTIONS_COOKIE, 4)) {
82e853bc5dShenning 		packet->options_valid = 0;
83e853bc5dShenning 		return;
84e853bc5dShenning 	}
85e853bc5dShenning 
86c824f21bShenning 	/*
87c824f21bShenning 	 * Go through the options field, up to the end of the packet or
88c824f21bShenning 	 * the End field.
89c824f21bShenning 	 */
90e853bc5dShenning 	parse_option_buffer(packet, &packet->raw->options[4],
91e853bc5dShenning 	    packet->packet_length - DHCP_FIXED_NON_UDP - 4);
92c824f21bShenning 
93c824f21bShenning 	/*
94c824f21bShenning 	 * If we parsed a DHCP Option Overload option, parse more
95c824f21bShenning 	 * options out of the buffer(s) containing them.
96c824f21bShenning 	 */
97c824f21bShenning 	if (packet->options_valid &&
98c824f21bShenning 	    packet->options[DHO_DHCP_OPTION_OVERLOAD].data) {
99e853bc5dShenning 		if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
100e853bc5dShenning 			parse_option_buffer(packet,
101c824f21bShenning 			    (unsigned char *)packet->raw->file,
102c824f21bShenning 			    sizeof(packet->raw->file));
103e853bc5dShenning 		if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
104e853bc5dShenning 			parse_option_buffer(packet,
105c824f21bShenning 			    (unsigned char *)packet->raw->sname,
106c824f21bShenning 			    sizeof(packet->raw->sname));
107e853bc5dShenning 	}
108e853bc5dShenning }
109e853bc5dShenning 
110c824f21bShenning /*
111c824f21bShenning  * Parse options out of the specified buffer, storing addresses of
112c824f21bShenning  * option values in packet->options and setting packet->options_valid if
113c824f21bShenning  * no errors are encountered.
114c824f21bShenning  */
115c824f21bShenning void
parse_option_buffer(struct packet * packet,unsigned char * buffer,int length)116c824f21bShenning parse_option_buffer(struct packet *packet,
117c824f21bShenning     unsigned char *buffer, int length)
118e853bc5dShenning {
119e853bc5dShenning 	unsigned char *s, *t;
120e853bc5dShenning 	unsigned char *end = buffer + length;
121e853bc5dShenning 	int len;
122e853bc5dShenning 	int code;
123e853bc5dShenning 
124e853bc5dShenning 	for (s = buffer; *s != DHO_END && s < end; ) {
125e853bc5dShenning 		code = s[0];
126e853bc5dShenning 
127e853bc5dShenning 		/* Pad options don't have a length - just skip them. */
128e853bc5dShenning 		if (code == DHO_PAD) {
129c824f21bShenning 			s++;
130e853bc5dShenning 			continue;
131e853bc5dShenning 		}
132e853bc5dShenning 		if (s + 2 > end) {
133e853bc5dShenning 			len = 65536;
134e853bc5dShenning 			goto bogus;
135e853bc5dShenning 		}
136e853bc5dShenning 
137c824f21bShenning 		/*
138c824f21bShenning 		 * All other fields (except end, see above) have a
139c824f21bShenning 		 * one-byte length.
140c824f21bShenning 		 */
141e853bc5dShenning 		len = s[1];
142e853bc5dShenning 
143c824f21bShenning 		/*
144c824f21bShenning 		 * If the length is outrageous, silently skip the rest,
145fd9f780dSdavid 		 * and mark the packet bad. Unfortunately some crappy
146c824f21bShenning 		 * dhcp servers always seem to give us garbage on the
147c824f21bShenning 		 * end of a packet. so rather than keep refusing, give
148c824f21bShenning 		 * up and try to take one after seeing a few without
149c824f21bShenning 		 * anything good.
150e853bc5dShenning 		 */
151e853bc5dShenning 		if (s + len + 2 > end) {
152e853bc5dShenning 		    bogus:
153e853bc5dShenning 			bad_options++;
154c525a185Skrw 			log_warnx("option %s (%d) %s.",
155e853bc5dShenning 			    dhcp_options[code].name, len,
156e853bc5dShenning 			    "larger than buffer");
157e853bc5dShenning 			if (bad_options == bad_options_max) {
158e853bc5dShenning 				packet->options_valid = 1;
159e853bc5dShenning 				bad_options = 0;
160*35318e8fSkrw 				log_warnx("Many bogus options seen in "
161*35318e8fSkrw 				    "offers.");
162*35318e8fSkrw 				log_warnx("Taking this offer in spite of "
163*35318e8fSkrw 				    "bogus");
164c525a185Skrw 				log_warnx("options - hope for the best!");
165e853bc5dShenning 			} else {
166c525a185Skrw 				log_warnx("rejecting bogus offer.");
167e853bc5dShenning 				packet->options_valid = 0;
168e853bc5dShenning 			}
169e853bc5dShenning 			return;
170e853bc5dShenning 		}
171c824f21bShenning 		/*
172c824f21bShenning 		 * If we haven't seen this option before, just make
173c824f21bShenning 		 * space for it and copy it there.
174c824f21bShenning 		 */
175e853bc5dShenning 		if (!packet->options[code].data) {
176d60fc4a4Skrw 			t = calloc(1, len + 1);
177d60fc4a4Skrw 			if (!t)
178c525a185Skrw 				fatalx("Can't allocate storage for option %s.",
179e853bc5dShenning 				    dhcp_options[code].name);
180c824f21bShenning 			/*
181c824f21bShenning 			 * Copy and NUL-terminate the option (in case
182cff08477Sstevesk 			 * it's an ASCII string).
183c824f21bShenning 			 */
184e853bc5dShenning 			memcpy(t, &s[2], len);
185e853bc5dShenning 			t[len] = 0;
186e853bc5dShenning 			packet->options[code].len = len;
187e853bc5dShenning 			packet->options[code].data = t;
188e853bc5dShenning 		} else {
189c824f21bShenning 			/*
190c824f21bShenning 			 * If it's a repeat, concatenate it to whatever
191c824f21bShenning 			 * we last saw.   This is really only required
192c824f21bShenning 			 * for clients, but what the heck...
193c824f21bShenning 			 */
194d60fc4a4Skrw 			t = calloc(1, len + packet->options[code].len + 1);
195e853bc5dShenning 			if (!t)
196c525a185Skrw 				fatalx("Can't expand storage for option %s.",
197e853bc5dShenning 				    dhcp_options[code].name);
198e853bc5dShenning 			memcpy(t, packet->options[code].data,
199e853bc5dShenning 				packet->options[code].len);
200e853bc5dShenning 			memcpy(t + packet->options[code].len,
201e853bc5dShenning 				&s[2], len);
202e853bc5dShenning 			packet->options[code].len += len;
203e853bc5dShenning 			t[packet->options[code].len] = 0;
20492b98df2Skrw 			free(packet->options[code].data);
205e853bc5dShenning 			packet->options[code].data = t;
206e853bc5dShenning 		}
207e853bc5dShenning 		s += len + 2;
208e853bc5dShenning 	}
209e853bc5dShenning 	packet->options_valid = 1;
210e853bc5dShenning }
211e853bc5dShenning 
212c824f21bShenning /*
213cd4ecb23Skrw  * Fill priority_list with a complete list of DHCP options sorted by
214cd4ecb23Skrw  * priority. i.e.
215cd4ecb23Skrw  *     1) Mandatory options.
216cd4ecb23Skrw  *     2) Options from prl that are not already present.
217cd4ecb23Skrw  *     3) Options from the default list that are not already present.
218cd4ecb23Skrw  */
219cd4ecb23Skrw void
create_priority_list(unsigned char * priority_list,unsigned char * prl,int prl_len)220cd4ecb23Skrw create_priority_list(unsigned char *priority_list, unsigned char *prl,
221cd4ecb23Skrw     int prl_len)
222cd4ecb23Skrw {
223c5c66dc3Sreyk 	unsigned char stored_list[256];
224cd4ecb23Skrw 	int i, priority_len = 0;
225cd4ecb23Skrw 
226c5c66dc3Sreyk 	/* clear stored_list, priority_list should be cleared before */
227359ce2c3Smestre 	memset(&stored_list, 0, sizeof(stored_list));
228cd4ecb23Skrw 
229cd4ecb23Skrw 	/* Some options we don't want on the priority list. */
230cd4ecb23Skrw 	stored_list[DHO_PAD] = 1;
231cd4ecb23Skrw 	stored_list[DHO_END] = 1;
232cd4ecb23Skrw 
233cd4ecb23Skrw 	/* Mandatory options. */
234cd4ecb23Skrw 	for(i = 0; dhcp_option_default_priority_list[i] != DHO_END; i++) {
235cd4ecb23Skrw 		priority_list[priority_len++] =
236cd4ecb23Skrw 		    dhcp_option_default_priority_list[i];
237cd4ecb23Skrw 		stored_list[dhcp_option_default_priority_list[i]] = 1;
238cd4ecb23Skrw 	}
239cd4ecb23Skrw 
240cd4ecb23Skrw 	/* Supplied priority list. */
241cd4ecb23Skrw 	if (!prl)
242cd4ecb23Skrw 		prl_len = 0;
243cd4ecb23Skrw 	for(i = 0; i < prl_len; i++) {
2443f461432Skrw 		/* CLASSLESS routes always have priority, sayeth RFC 3442. */
2453f461432Skrw 		if (prl[i] == DHO_CLASSLESS_STATIC_ROUTES ||
2463f461432Skrw 		    prl[i] == DHO_CLASSLESS_MS_STATIC_ROUTES) {
2473f461432Skrw 			priority_list[priority_len++] = prl[i];
2483f461432Skrw 			stored_list[prl[i]] = 1;
2493f461432Skrw 		}
2503f461432Skrw 	}
2513f461432Skrw 	for(i = 0; i < prl_len; i++) {
252cd4ecb23Skrw 		if (stored_list[prl[i]])
253cd4ecb23Skrw 			continue;
254cd4ecb23Skrw 		priority_list[priority_len++] = prl[i];
255cd4ecb23Skrw 		stored_list[prl[i]] = 1;
256cd4ecb23Skrw 	}
257cd4ecb23Skrw 
258cd4ecb23Skrw 	/* Default priority list. */
259cd4ecb23Skrw 	prl = dhcp_option_default_priority_list;
260cd4ecb23Skrw 	for(i = 0; i < 256; i++) {
261cd4ecb23Skrw 		if (stored_list[prl[i]])
262cd4ecb23Skrw 			continue;
263cd4ecb23Skrw 		priority_list[priority_len++] = prl[i];
264cd4ecb23Skrw 		stored_list[prl[i]] = 1;
265cd4ecb23Skrw 	}
266cd4ecb23Skrw }
267cd4ecb23Skrw /*
268c824f21bShenning  * cons options into a big buffer, and then split them out into the
269c824f21bShenning  * three separate buffers if needed.  This allows us to cons up a set of
270c824f21bShenning  * vendor options using the same routine.
271c824f21bShenning  */
272c824f21bShenning int
cons_options(struct packet * inpacket,struct dhcp_packet * outpacket,int mms,struct tree_cache ** options,int overload,int terminate,int bootpp,u_int8_t * prl,int prl_len)273c824f21bShenning cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
274c824f21bShenning     int mms, struct tree_cache **options,
275c824f21bShenning     int overload, /* Overload flags that may be set. */
276c824f21bShenning     int terminate, int bootpp, u_int8_t *prl, int prl_len)
277e853bc5dShenning {
278cd4ecb23Skrw 	unsigned char priority_list[256];
279e853bc5dShenning 	unsigned char buffer[4096];	/* Really big buffer... */
280c432531bSkrw 	int bufix, main_buffer_size, option_size;
281e853bc5dShenning 
282c824f21bShenning 	/*
283c824f21bShenning 	 * If the client has provided a maximum DHCP message size, use
284c824f21bShenning 	 * that; otherwise, if it's BOOTP, only 64 bytes; otherwise use
285c824f21bShenning 	 * up to the minimum IP MTU size (576 bytes).
286c824f21bShenning 	 *
287c824f21bShenning 	 * XXX if a BOOTP client specifies a max message size, we will
288c824f21bShenning 	 * honor it.
289c824f21bShenning 	 */
290e853bc5dShenning 	if (!mms &&
291e853bc5dShenning 	    inpacket &&
292e853bc5dShenning 	    inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data &&
293e853bc5dShenning 	    (inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].len >=
2947eb1ada0Skrw 	    sizeof(u_int16_t))) {
295c824f21bShenning 		mms = getUShort(
296c824f21bShenning 		    inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data);
2977eb1ada0Skrw 	}
298e853bc5dShenning 
2992b30e5f2Skrw 	if (mms) {
3002b30e5f2Skrw 		if (mms < 576)
3012b30e5f2Skrw 			mms = 576;	/* mms must be >= minimum IP MTU */
302e853bc5dShenning 		main_buffer_size = mms - DHCP_FIXED_LEN;
3032b30e5f2Skrw 	} else if (bootpp)
304e853bc5dShenning 		main_buffer_size = 64;
305e853bc5dShenning 	else
306e853bc5dShenning 		main_buffer_size = 576 - DHCP_FIXED_LEN;
307e853bc5dShenning 
308ef0ff70bSkrw 	if (main_buffer_size > sizeof(outpacket->options))
309ef0ff70bSkrw 		main_buffer_size = sizeof(outpacket->options);
310e853bc5dShenning 
311c824f21bShenning 	/*
312c432531bSkrw 	 * Initialize the available buffers, some or all of which may not be
313c432531bSkrw 	 * used.
314c432531bSkrw 	 */
315c432531bSkrw 	memset(outpacket->options, DHO_PAD, sizeof(outpacket->options));
316c432531bSkrw 	if (overload & 1)
317c432531bSkrw 		memset(outpacket->file, DHO_PAD, DHCP_FILE_LEN);
318c432531bSkrw 	if (overload & 2)
319c432531bSkrw 		memset(outpacket->sname, DHO_PAD, DHCP_SNAME_LEN);
320c432531bSkrw 	if (bootpp)
321c432531bSkrw 		overload = 0; /* Don't use overload buffers for bootp! */
322c432531bSkrw 
323c432531bSkrw 	/*
324cd4ecb23Skrw 	 * Get complete list of possible options in priority order. Use the
325cd4ecb23Skrw 	 * list provided in the options. Lacking that use the list provided by
326cd4ecb23Skrw 	 * prl. If that is not available just use the default list.
327c824f21bShenning 	 */
328359ce2c3Smestre 	memset(&priority_list, 0, sizeof(priority_list));
329*35318e8fSkrw 	if (inpacket &&
330*35318e8fSkrw 	    inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data)
331cd4ecb23Skrw 		create_priority_list(priority_list,
332c824f21bShenning 		    inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data,
333cd4ecb23Skrw 		    inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].len);
334cd4ecb23Skrw 	else if (prl)
335cd4ecb23Skrw 		create_priority_list(priority_list, prl, prl_len);
336cd4ecb23Skrw 	else
337cd4ecb23Skrw 		create_priority_list(priority_list, NULL, 0);
338e853bc5dShenning 
339c824f21bShenning 	/*
340c432531bSkrw 	 * Copy the options into the big buffer, including leading cookie and
341c432531bSkrw 	 * DHCP_OVERLOAD_OPTION, and DHO_END if it fits. All unused space will
342c432531bSkrw 	 * be set to DHO_PAD
343c824f21bShenning 	 */
344c432531bSkrw 	option_size = store_options(buffer, main_buffer_size, options,
345c432531bSkrw 	    priority_list, overload, terminate);
346c432531bSkrw 	if (option_size == 0)
347c432531bSkrw 		return (DHCP_FIXED_NON_UDP);
34858337b23Skrw 
349c432531bSkrw 	/* Copy the main buffer. */
350c432531bSkrw 	memcpy(&outpacket->options[0], buffer, main_buffer_size);
351c432531bSkrw 	if (option_size <= main_buffer_size)
352c432531bSkrw 		return (DHCP_FIXED_NON_UDP + option_size);
353e853bc5dShenning 
354c432531bSkrw 	/* Copy the overflow buffers. */
355c432531bSkrw 	bufix = main_buffer_size;
356e853bc5dShenning 	if (overload & 1) {
357b2db86bdSkrw 		memcpy(outpacket->file, &buffer[bufix], DHCP_FILE_LEN);
358e853bc5dShenning 		bufix += DHCP_FILE_LEN;
359e853bc5dShenning 	}
360c432531bSkrw 	if (overload & 2)
361c432531bSkrw 		memcpy(outpacket->sname, &buffer[bufix], DHCP_SNAME_LEN);
36258337b23Skrw 
36358337b23Skrw 	return (DHCP_FIXED_NON_UDP + main_buffer_size);
364e853bc5dShenning }
365e853bc5dShenning 
366c824f21bShenning /*
36784f58762Skrw  * Store a <code><length><data> fragment in buffer. Return the number of
36884f58762Skrw  * characters used. Return 0 if no data could be stored.
36984f58762Skrw  */
37084f58762Skrw int
store_option_fragment(unsigned char * buffer,int buffer_size,unsigned char code,int length,unsigned char * data)37184f58762Skrw store_option_fragment(unsigned char *buffer, int buffer_size,
37284f58762Skrw     unsigned char code, int length, unsigned char *data)
37384f58762Skrw {
37484f58762Skrw 	buffer_size -= 2; /* Space for option code and option length. */
37584f58762Skrw 
37684f58762Skrw 	if (buffer_size < 1)
37784f58762Skrw 		return (0);
37884f58762Skrw 
37984f58762Skrw 	if (buffer_size > 255)
38084f58762Skrw 		buffer_size = 255;
38184f58762Skrw 	if (length > buffer_size)
38284f58762Skrw 		length = buffer_size;
38384f58762Skrw 
38484f58762Skrw 	buffer[0] = code;
38584f58762Skrw 	buffer[1] = length;
38684f58762Skrw 
38784f58762Skrw 	memcpy(&buffer[2], data, length);
38884f58762Skrw 
38984f58762Skrw 	return (length + 2);
39084f58762Skrw }
39184f58762Skrw 
39284f58762Skrw /*
393c432531bSkrw  * Store all the requested options into the requested buffer. Insert the
394c432531bSkrw  * required cookie, DHO_DHCP_OPTION_OVERLOAD options and append a DHO_END if
395c432531bSkrw  * if fits. Ensure all buffer space is set to DHO_PAD if unused.
396c824f21bShenning  */
397c824f21bShenning int
store_options(unsigned char * buffer,int main_buffer_size,struct tree_cache ** options,unsigned char * priority_list,int overload,int terminate)398c432531bSkrw store_options(unsigned char *buffer, int main_buffer_size,
399c432531bSkrw     struct tree_cache **options, unsigned char *priority_list, int overload,
400cd4ecb23Skrw     int terminate)
401e853bc5dShenning {
402c432531bSkrw 	int buflen, code, cutoff, i, incr, ix, length, optstart, overflow;
403c432531bSkrw 	int second_cutoff;
40484f58762Skrw 	int bufix = 0;
4053f461432Skrw 	int stored_classless = 0;
406e853bc5dShenning 
407920fcdb1Skrw 	overload &= 3; /* Only consider valid bits. */
408920fcdb1Skrw 
409c432531bSkrw 	cutoff = main_buffer_size;
410c432531bSkrw 	second_cutoff = cutoff + ((overload & 1) ? DHCP_FILE_LEN : 0);
411c432531bSkrw 	buflen = second_cutoff + ((overload & 2) ? DHCP_SNAME_LEN : 0);
412920fcdb1Skrw 
413c432531bSkrw 	memset(buffer, DHO_PAD, buflen);
414c432531bSkrw 	memcpy(buffer, DHCP_OPTIONS_COOKIE, 4);
415c432531bSkrw 
416920fcdb1Skrw 	if (overload)
417920fcdb1Skrw 		bufix = 7; /* Reserve space for DHO_DHCP_OPTION_OVERLOAD. */
418920fcdb1Skrw 	else
419920fcdb1Skrw 		bufix = 4;
420c432531bSkrw 
421c824f21bShenning 	/*
42284f58762Skrw 	 * Store options in the order they appear in the priority list.
423c824f21bShenning 	 */
424cd4ecb23Skrw 	for (i = 0; i < 256; i++) {
425e853bc5dShenning 		/* Code for next option to try to store. */
42684f58762Skrw 		code = priority_list[i];
427cd4ecb23Skrw 		if (code == DHO_PAD || code == DHO_END)
428e853bc5dShenning 			continue;
429e853bc5dShenning 
430cd4ecb23Skrw 		if (!options[code] || !tree_evaluate(options[code]))
431cd4ecb23Skrw 			continue;
432e853bc5dShenning 
4333f461432Skrw 		/*
4343f461432Skrw 		 * RFC 3442 says:
4353f461432Skrw 		 *
4363f461432Skrw 		 * When a DHCP client requests the Classless Static
4373f461432Skrw 		 * Routes option and also requests either or both of the
4383f461432Skrw 		 * Router option and the Static Routes option, and the
4393f461432Skrw 		 * DHCP server is sending Classless Static Routes options
4403f461432Skrw 		 * to that client, the server SHOULD NOT include the
4413f461432Skrw 		 * Router or Static Routes options.
4423f461432Skrw 		 */
4433f461432Skrw 		if ((code == DHO_ROUTERS || code == DHO_STATIC_ROUTES) &&
4443f461432Skrw 		    stored_classless)
4453f461432Skrw 			continue;
4463f461432Skrw 
447e853bc5dShenning 		/* We should now have a constant length for the option. */
448e853bc5dShenning 		length = options[code]->len;
449e853bc5dShenning 
450e853bc5dShenning 		/* Try to store the option. */
451e853bc5dShenning 		optstart = bufix;
45284f58762Skrw 		ix = 0;
453e853bc5dShenning 		while (length) {
45484f58762Skrw 			incr = store_option_fragment(&buffer[bufix],
45584f58762Skrw 			    cutoff - bufix, code, length,
45684f58762Skrw 			    options[code]->value + ix);
45784f58762Skrw 
45884f58762Skrw 			if (incr > 0) {
45984f58762Skrw 				bufix += incr;
46084f58762Skrw 				length -= incr - 2;
46184f58762Skrw 				ix += incr - 2;
46284f58762Skrw 				continue;
46384f58762Skrw 			}
464e853bc5dShenning 
465c824f21bShenning 			/*
46684f58762Skrw 			 * No fragment could be stored in the space before the
46784f58762Skrw 			 * cutoff. Fill the unusable space with DHO_PAD and
46884f58762Skrw 			 * move cutoff for another attempt.
469c824f21bShenning 			 */
47084f58762Skrw 			memset(&buffer[bufix], DHO_PAD, cutoff - bufix);
47184f58762Skrw 			bufix = cutoff;
47284f58762Skrw 			if (cutoff < second_cutoff)
47384f58762Skrw 				cutoff = second_cutoff;
47484f58762Skrw 			else if (cutoff < buflen)
47584f58762Skrw 				cutoff = buflen;
47684f58762Skrw 			else
477e853bc5dShenning 				break;
478e853bc5dShenning 		}
479e853bc5dShenning 
48084f58762Skrw 		if (length > 0) {
48184f58762Skrw zapfrags:
48284f58762Skrw 			memset(&buffer[optstart], DHO_PAD, buflen - optstart);
48384f58762Skrw 			bufix = optstart;
48484f58762Skrw 		} else if (terminate && dhcp_options[code].format[0] == 't') {
48584f58762Skrw 			if (bufix < cutoff)
48684f58762Skrw 				buffer[bufix++] = '\0';
48784f58762Skrw 			else
48884f58762Skrw 				goto zapfrags;
489e853bc5dShenning 		}
4903f461432Skrw 		if (code == DHO_CLASSLESS_STATIC_ROUTES ||
4913f461432Skrw 		    code == DHO_CLASSLESS_MS_STATIC_ROUTES)
4923f461432Skrw 			stored_classless = 1;
493e853bc5dShenning 	}
49484f58762Skrw 
495c432531bSkrw 	if (bufix == (4 + (overload ? 3 : 0)))
496c432531bSkrw 		/* Didn't manage to store any options. */
497c432531bSkrw 		return (0);
498c432531bSkrw 
499c432531bSkrw 	if (bufix < buflen)
500c432531bSkrw 		buffer[bufix++] = DHO_END;
501c432531bSkrw 
502c432531bSkrw 	/* Fill in overload option value based on space used for options. */
5038914f811Skrw 	if (overload) {
504c432531bSkrw 		overflow = bufix - main_buffer_size;
5058914f811Skrw 		if (overflow > 0) {
506920fcdb1Skrw 			buffer[4] = DHO_DHCP_OPTION_OVERLOAD;
507920fcdb1Skrw 			buffer[5] = 1;
508c432531bSkrw 			if (overload & 1) {
509920fcdb1Skrw 				buffer[6] |= 1;
510c432531bSkrw 				overflow -= DHCP_FILE_LEN;
511c432531bSkrw 			}
512920fcdb1Skrw 			if ((overload & 2) && overflow > 0)
513c432531bSkrw 				buffer[6] |= 2;
5148914f811Skrw 		} else {
5158914f811Skrw 			/*
5168914f811Skrw 			 * Compact buffer to eliminate the unused
5178914f811Skrw 			 * DHO_DHCP_OPTION_OVERLOAD option. Some clients
5188914f811Skrw 			 * choke on DHO_PAD options there.
5198914f811Skrw 			 */
5208914f811Skrw 			memmove(&buffer[4], &buffer[7], buflen - 7);
5218914f811Skrw 			bufix -= 3;
5228914f811Skrw 			memset(&buffer[bufix], DHO_PAD, 3);
5238914f811Skrw 		}
524c432531bSkrw 	}
525c432531bSkrw 
526c824f21bShenning 	return (bufix);
527e853bc5dShenning }
528e853bc5dShenning 
529c824f21bShenning void
do_packet(struct interface_info * interface,struct dhcp_packet * packet,int len,unsigned int from_port,struct iaddr from,struct hardware * hfrom)530c824f21bShenning do_packet(struct interface_info *interface, struct dhcp_packet *packet,
531c824f21bShenning     int len, unsigned int from_port, struct iaddr from, struct hardware *hfrom)
532e853bc5dShenning {
533e853bc5dShenning 	struct packet tp;
534e853bc5dShenning 	int i;
535e853bc5dShenning 
536c824f21bShenning 	if (packet->hlen > sizeof(packet->chaddr)) {
537c525a185Skrw 		log_info("Discarding packet with invalid hlen.");
538e853bc5dShenning 		return;
539e853bc5dShenning 	}
540e853bc5dShenning 
541c824f21bShenning 	memset(&tp, 0, sizeof(tp));
542e853bc5dShenning 	tp.raw = packet;
543e853bc5dShenning 	tp.packet_length = len;
544e853bc5dShenning 	tp.client_port = from_port;
545e853bc5dShenning 	tp.client_addr = from;
546e853bc5dShenning 	tp.interface = interface;
547e853bc5dShenning 	tp.haddr = hfrom;
548e853bc5dShenning 
549e853bc5dShenning 	parse_options(&tp);
550e853bc5dShenning 	if (tp.options_valid &&
551e853bc5dShenning 	    tp.options[DHO_DHCP_MESSAGE_TYPE].data)
552c824f21bShenning 		tp.packet_type = tp.options[DHO_DHCP_MESSAGE_TYPE].data[0];
55384d8c049Syasuoka 
554e853bc5dShenning 	if (tp.packet_type)
55594bf53e6Skrw 		dhcp(&tp, interface->is_udpsock);
556e853bc5dShenning 	else
557e853bc5dShenning 		bootp(&tp);
558e853bc5dShenning 
559e853bc5dShenning 	/* Free the data associated with the options. */
560c824f21bShenning 	for (i = 0; i < 256; i++)
56192b98df2Skrw 		free(tp.options[i].data);
562e853bc5dShenning }
563