xref: /freebsd/sbin/dhclient/options.c (revision 2be1a816b9ff69588e55be0a84cbe2a31efc0f2f)
1 /*	$OpenBSD: options.c,v 1.15 2004/12/26 03:17:07 deraadt Exp $	*/
2 
3 /* DHCP options parsing and reassembly. */
4 
5 /*
6  * Copyright (c) 1995, 1996, 1997, 1998 The Internet Software Consortium.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
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.
21  *
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
34  * SUCH DAMAGE.
35  *
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''.
41  */
42 
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45 
46 #include <ctype.h>
47 
48 #define DHCP_OPTION_DATA
49 #include "dhcpd.h"
50 
51 int bad_options = 0;
52 int bad_options_max = 5;
53 
54 void	parse_options(struct packet *);
55 void	parse_option_buffer(struct packet *, unsigned char *, int);
56 int	store_options(unsigned char *, int, struct tree_cache **,
57 	    unsigned char *, int, int, int, int);
58 
59 
60 /*
61  * Parse all available options out of the specified packet.
62  */
63 void
64 parse_options(struct packet *packet)
65 {
66 	/* Initially, zero all option pointers. */
67 	memset(packet->options, 0, sizeof(packet->options));
68 
69 	/* If we don't see the magic cookie, there's nothing to parse. */
70 	if (memcmp(packet->raw->options, DHCP_OPTIONS_COOKIE, 4)) {
71 		packet->options_valid = 0;
72 		return;
73 	}
74 
75 	/*
76 	 * Go through the options field, up to the end of the packet or
77 	 * the End field.
78 	 */
79 	parse_option_buffer(packet, &packet->raw->options[4],
80 	    packet->packet_length - DHCP_FIXED_NON_UDP - 4);
81 
82 	/*
83 	 * If we parsed a DHCP Option Overload option, parse more
84 	 * options out of the buffer(s) containing them.
85 	 */
86 	if (packet->options_valid &&
87 	    packet->options[DHO_DHCP_OPTION_OVERLOAD].data) {
88 		if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)
89 			parse_option_buffer(packet,
90 			    (unsigned char *)packet->raw->file,
91 			    sizeof(packet->raw->file));
92 		if (packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)
93 			parse_option_buffer(packet,
94 			    (unsigned char *)packet->raw->sname,
95 			    sizeof(packet->raw->sname));
96 	}
97 }
98 
99 /*
100  * Parse options out of the specified buffer, storing addresses of
101  * option values in packet->options and setting packet->options_valid if
102  * no errors are encountered.
103  */
104 void
105 parse_option_buffer(struct packet *packet,
106     unsigned char *buffer, int length)
107 {
108 	unsigned char *s, *t, *end = buffer + length;
109 	int len, code;
110 
111 	for (s = buffer; *s != DHO_END && s < end; ) {
112 		code = s[0];
113 
114 		/* Pad options don't have a length - just skip them. */
115 		if (code == DHO_PAD) {
116 			s++;
117 			continue;
118 		}
119 		if (s + 2 > end) {
120 			len = 65536;
121 			goto bogus;
122 		}
123 
124 		/*
125 		 * All other fields (except end, see above) have a
126 		 * one-byte length.
127 		 */
128 		len = s[1];
129 
130 		/*
131 		 * If the length is outrageous, silently skip the rest,
132 		 * and mark the packet bad. Unfortunately some crappy
133 		 * dhcp servers always seem to give us garbage on the
134 		 * end of a packet. so rather than keep refusing, give
135 		 * up and try to take one after seeing a few without
136 		 * anything good.
137 		 */
138 		if (s + len + 2 > end) {
139 		    bogus:
140 			bad_options++;
141 			warning("option %s (%d) %s.",
142 			    dhcp_options[code].name, len,
143 			    "larger than buffer");
144 			if (bad_options == bad_options_max) {
145 				packet->options_valid = 1;
146 				bad_options = 0;
147 				warning("Many bogus options seen in offers. "
148 				    "Taking this offer in spite of bogus "
149 				    "options - hope for the best!");
150 			} else {
151 				warning("rejecting bogus offer.");
152 				packet->options_valid = 0;
153 			}
154 			return;
155 		}
156 		/*
157 		 * If we haven't seen this option before, just make
158 		 * space for it and copy it there.
159 		 */
160 		if (!packet->options[code].data) {
161 			if (!(t = calloc(1, len + 1)))
162 				error("Can't allocate storage for option %s.",
163 				    dhcp_options[code].name);
164 			/*
165 			 * Copy and NUL-terminate the option (in case
166 			 * it's an ASCII string.
167 			 */
168 			memcpy(t, &s[2], len);
169 			t[len] = 0;
170 			packet->options[code].len = len;
171 			packet->options[code].data = t;
172 		} else {
173 			/*
174 			 * If it's a repeat, concatenate it to whatever
175 			 * we last saw.   This is really only required
176 			 * for clients, but what the heck...
177 			 */
178 			t = calloc(1, len + packet->options[code].len + 1);
179 			if (!t)
180 				error("Can't expand storage for option %s.",
181 				    dhcp_options[code].name);
182 			memcpy(t, packet->options[code].data,
183 				packet->options[code].len);
184 			memcpy(t + packet->options[code].len,
185 				&s[2], len);
186 			packet->options[code].len += len;
187 			t[packet->options[code].len] = 0;
188 			free(packet->options[code].data);
189 			packet->options[code].data = t;
190 		}
191 		s += len + 2;
192 	}
193 	packet->options_valid = 1;
194 }
195 
196 /*
197  * cons options into a big buffer, and then split them out into the
198  * three separate buffers if needed.  This allows us to cons up a set of
199  * vendor options using the same routine.
200  */
201 int
202 cons_options(struct packet *inpacket, struct dhcp_packet *outpacket,
203     int mms, struct tree_cache **options,
204     int overload, /* Overload flags that may be set. */
205     int terminate, int bootpp, u_int8_t *prl, int prl_len)
206 {
207 	unsigned char priority_list[300], buffer[4096];
208 	int priority_len, main_buffer_size, mainbufix, bufix;
209 	int option_size, length;
210 
211 	/*
212 	 * If the client has provided a maximum DHCP message size, use
213 	 * that; otherwise, if it's BOOTP, only 64 bytes; otherwise use
214 	 * up to the minimum IP MTU size (576 bytes).
215 	 *
216 	 * XXX if a BOOTP client specifies a max message size, we will
217 	 * honor it.
218 	 */
219 	if (!mms &&
220 	    inpacket &&
221 	    inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data &&
222 	    (inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].len >=
223 	    sizeof(u_int16_t)))
224 		mms = getUShort(
225 		    inpacket->options[DHO_DHCP_MAX_MESSAGE_SIZE].data);
226 
227 	if (mms)
228 		main_buffer_size = mms - DHCP_FIXED_LEN;
229 	else if (bootpp)
230 		main_buffer_size = 64;
231 	else
232 		main_buffer_size = 576 - DHCP_FIXED_LEN;
233 
234 	if (main_buffer_size > sizeof(buffer))
235 		main_buffer_size = sizeof(buffer);
236 
237 	/* Preload the option priority list with mandatory options. */
238 	priority_len = 0;
239 	priority_list[priority_len++] = DHO_DHCP_MESSAGE_TYPE;
240 	priority_list[priority_len++] = DHO_DHCP_SERVER_IDENTIFIER;
241 	priority_list[priority_len++] = DHO_DHCP_LEASE_TIME;
242 	priority_list[priority_len++] = DHO_DHCP_MESSAGE;
243 
244 	/*
245 	 * If the client has provided a list of options that it wishes
246 	 * returned, use it to prioritize.  Otherwise, prioritize based
247 	 * on the default priority list.
248 	 */
249 	if (inpacket &&
250 	    inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data) {
251 		int prlen =
252 		    inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].len;
253 		if (prlen + priority_len > sizeof(priority_list))
254 			prlen = sizeof(priority_list) - priority_len;
255 
256 		memcpy(&priority_list[priority_len],
257 		    inpacket->options[DHO_DHCP_PARAMETER_REQUEST_LIST].data,
258 		    prlen);
259 		priority_len += prlen;
260 		prl = priority_list;
261 	} else if (prl) {
262 		if (prl_len + priority_len > sizeof(priority_list))
263 			prl_len = sizeof(priority_list) - priority_len;
264 
265 		memcpy(&priority_list[priority_len], prl, prl_len);
266 		priority_len += prl_len;
267 		prl = priority_list;
268 	} else {
269 		memcpy(&priority_list[priority_len],
270 		    dhcp_option_default_priority_list,
271 		    sizeof_dhcp_option_default_priority_list);
272 		priority_len += sizeof_dhcp_option_default_priority_list;
273 	}
274 
275 	/* Copy the options into the big buffer... */
276 	option_size = store_options(
277 	    buffer,
278 	    (main_buffer_size - 7 + ((overload & 1) ? DHCP_FILE_LEN : 0) +
279 		((overload & 2) ? DHCP_SNAME_LEN : 0)),
280 	    options, priority_list, priority_len, main_buffer_size,
281 	    (main_buffer_size + ((overload & 1) ? DHCP_FILE_LEN : 0)),
282 	    terminate);
283 
284 	/* Put the cookie up front... */
285 	memcpy(outpacket->options, DHCP_OPTIONS_COOKIE, 4);
286 	mainbufix = 4;
287 
288 	/*
289 	 * If we're going to have to overload, store the overload option
290 	 * at the beginning.  If we can, though, just store the whole
291 	 * thing in the packet's option buffer and leave it at that.
292 	 */
293 	if (option_size <= main_buffer_size - mainbufix) {
294 		memcpy(&outpacket->options[mainbufix],
295 		    buffer, option_size);
296 		mainbufix += option_size;
297 		if (mainbufix < main_buffer_size)
298 			outpacket->options[mainbufix++] = DHO_END;
299 		length = DHCP_FIXED_NON_UDP + mainbufix;
300 	} else {
301 		outpacket->options[mainbufix++] = DHO_DHCP_OPTION_OVERLOAD;
302 		outpacket->options[mainbufix++] = 1;
303 		if (option_size >
304 		    main_buffer_size - mainbufix + DHCP_FILE_LEN)
305 			outpacket->options[mainbufix++] = 3;
306 		else
307 			outpacket->options[mainbufix++] = 1;
308 
309 		memcpy(&outpacket->options[mainbufix],
310 		    buffer, main_buffer_size - mainbufix);
311 		bufix = main_buffer_size - mainbufix;
312 		length = DHCP_FIXED_NON_UDP + mainbufix;
313 		if (overload & 1) {
314 			if (option_size - bufix <= DHCP_FILE_LEN) {
315 				memcpy(outpacket->file,
316 				    &buffer[bufix], option_size - bufix);
317 				mainbufix = option_size - bufix;
318 				if (mainbufix < DHCP_FILE_LEN)
319 					outpacket->file[mainbufix++] = (char)DHO_END;
320 				while (mainbufix < DHCP_FILE_LEN)
321 					outpacket->file[mainbufix++] = (char)DHO_PAD;
322 			} else {
323 				memcpy(outpacket->file,
324 				    &buffer[bufix], DHCP_FILE_LEN);
325 				bufix += DHCP_FILE_LEN;
326 			}
327 		}
328 		if ((overload & 2) && option_size < bufix) {
329 			memcpy(outpacket->sname,
330 			    &buffer[bufix], option_size - bufix);
331 
332 			mainbufix = option_size - bufix;
333 			if (mainbufix < DHCP_SNAME_LEN)
334 				outpacket->file[mainbufix++] = (char)DHO_END;
335 			while (mainbufix < DHCP_SNAME_LEN)
336 				outpacket->file[mainbufix++] = (char)DHO_PAD;
337 		}
338 	}
339 	return (length);
340 }
341 
342 /*
343  * Store all the requested options into the requested buffer.
344  */
345 int
346 store_options(unsigned char *buffer, int buflen, struct tree_cache **options,
347     unsigned char *priority_list, int priority_len, int first_cutoff,
348     int second_cutoff, int terminate)
349 {
350 	int bufix = 0, option_stored[256], i, ix, tto;
351 
352 	/* Zero out the stored-lengths array. */
353 	memset(option_stored, 0, sizeof(option_stored));
354 
355 	/*
356 	 * Copy out the options in the order that they appear in the
357 	 * priority list...
358 	 */
359 	for (i = 0; i < priority_len; i++) {
360 		/* Code for next option to try to store. */
361 		int code = priority_list[i];
362 		int optstart;
363 
364 		/*
365 		 * Number of bytes left to store (some may already have
366 		 * been stored by a previous pass).
367 		 */
368 		int length;
369 
370 		/* If no data is available for this option, skip it. */
371 		if (!options[code]) {
372 			continue;
373 		}
374 
375 		/*
376 		 * The client could ask for things that are mandatory,
377 		 * in which case we should avoid storing them twice...
378 		 */
379 		if (option_stored[code])
380 			continue;
381 		option_stored[code] = 1;
382 
383 		/* We should now have a constant length for the option. */
384 		length = options[code]->len;
385 
386 		/* Do we add a NUL? */
387 		if (terminate && dhcp_options[code].format[0] == 't') {
388 			length++;
389 			tto = 1;
390 		} else
391 			tto = 0;
392 
393 		/* Try to store the option. */
394 
395 		/*
396 		 * If the option's length is more than 255, we must
397 		 * store it in multiple hunks.   Store 255-byte hunks
398 		 * first.  However, in any case, if the option data will
399 		 * cross a buffer boundary, split it across that
400 		 * boundary.
401 		 */
402 		ix = 0;
403 
404 		optstart = bufix;
405 		while (length) {
406 			unsigned char incr = length > 255 ? 255 : length;
407 
408 			/*
409 			 * If this hunk of the buffer will cross a
410 			 * boundary, only go up to the boundary in this
411 			 * pass.
412 			 */
413 			if (bufix < first_cutoff &&
414 			    bufix + incr > first_cutoff)
415 				incr = first_cutoff - bufix;
416 			else if (bufix < second_cutoff &&
417 			    bufix + incr > second_cutoff)
418 				incr = second_cutoff - bufix;
419 
420 			/*
421 			 * If this option is going to overflow the
422 			 * buffer, skip it.
423 			 */
424 			if (bufix + 2 + incr > buflen) {
425 				bufix = optstart;
426 				break;
427 			}
428 
429 			/* Everything looks good - copy it in! */
430 			buffer[bufix] = code;
431 			buffer[bufix + 1] = incr;
432 			if (tto && incr == length) {
433 				memcpy(buffer + bufix + 2,
434 				    options[code]->value + ix, incr - 1);
435 				buffer[bufix + 2 + incr - 1] = 0;
436 			} else
437 				memcpy(buffer + bufix + 2,
438 				    options[code]->value + ix, incr);
439 			length -= incr;
440 			ix += incr;
441 			bufix += 2 + incr;
442 		}
443 	}
444 	return (bufix);
445 }
446 
447 /*
448  * Format the specified option so that a human can easily read it.
449  */
450 char *
451 pretty_print_option(unsigned int code, unsigned char *data, int len,
452     int emit_commas, int emit_quotes)
453 {
454 	static char optbuf[32768]; /* XXX */
455 	int hunksize = 0, numhunk = -1, numelem = 0;
456 	char fmtbuf[32], *op = optbuf;
457 	int i, j, k, opleft = sizeof(optbuf);
458 	unsigned char *dp = data;
459 	struct in_addr foo;
460 	char comma;
461 
462 	/* Code should be between 0 and 255. */
463 	if (code > 255)
464 		error("pretty_print_option: bad code %d", code);
465 
466 	if (emit_commas)
467 		comma = ',';
468 	else
469 		comma = ' ';
470 
471 	/* Figure out the size of the data. */
472 	for (i = 0; dhcp_options[code].format[i]; i++) {
473 		if (!numhunk) {
474 			warning("%s: Excess information in format string: %s",
475 			    dhcp_options[code].name,
476 			    &(dhcp_options[code].format[i]));
477 			break;
478 		}
479 		numelem++;
480 		fmtbuf[i] = dhcp_options[code].format[i];
481 		switch (dhcp_options[code].format[i]) {
482 		case 'A':
483 			--numelem;
484 			fmtbuf[i] = 0;
485 			numhunk = 0;
486 			break;
487 		case 'X':
488 			for (k = 0; k < len; k++)
489 				if (!isascii(data[k]) ||
490 				    !isprint(data[k]))
491 					break;
492 			if (k == len) {
493 				fmtbuf[i] = 't';
494 				numhunk = -2;
495 			} else {
496 				fmtbuf[i] = 'x';
497 				hunksize++;
498 				comma = ':';
499 				numhunk = 0;
500 			}
501 			fmtbuf[i + 1] = 0;
502 			break;
503 		case 't':
504 			fmtbuf[i] = 't';
505 			fmtbuf[i + 1] = 0;
506 			numhunk = -2;
507 			break;
508 		case 'I':
509 		case 'l':
510 		case 'L':
511 			hunksize += 4;
512 			break;
513 		case 's':
514 		case 'S':
515 			hunksize += 2;
516 			break;
517 		case 'b':
518 		case 'B':
519 		case 'f':
520 			hunksize++;
521 			break;
522 		case 'e':
523 			break;
524 		default:
525 			warning("%s: garbage in format string: %s",
526 			    dhcp_options[code].name,
527 			    &(dhcp_options[code].format[i]));
528 			break;
529 		}
530 	}
531 
532 	/* Check for too few bytes... */
533 	if (hunksize > len) {
534 		warning("%s: expecting at least %d bytes; got %d",
535 		    dhcp_options[code].name, hunksize, len);
536 		return ("<error>");
537 	}
538 	/* Check for too many bytes... */
539 	if (numhunk == -1 && hunksize < len)
540 		warning("%s: %d extra bytes",
541 		    dhcp_options[code].name, len - hunksize);
542 
543 	/* If this is an array, compute its size. */
544 	if (!numhunk)
545 		numhunk = len / hunksize;
546 	/* See if we got an exact number of hunks. */
547 	if (numhunk > 0 && numhunk * hunksize < len)
548 		warning("%s: %d extra bytes at end of array",
549 		    dhcp_options[code].name, len - numhunk * hunksize);
550 
551 	/* A one-hunk array prints the same as a single hunk. */
552 	if (numhunk < 0)
553 		numhunk = 1;
554 
555 	/* Cycle through the array (or hunk) printing the data. */
556 	for (i = 0; i < numhunk; i++) {
557 		for (j = 0; j < numelem; j++) {
558 			int opcount;
559 			switch (fmtbuf[j]) {
560 			case 't':
561 				if (emit_quotes) {
562 					*op++ = '"';
563 					opleft--;
564 				}
565 				for (; dp < data + len; dp++) {
566 					if (!isascii(*dp) ||
567 					    !isprint(*dp)) {
568 						if (dp + 1 != data + len ||
569 						    *dp != 0) {
570 							snprintf(op, opleft,
571 							    "\\%03o", *dp);
572 							op += 4;
573 							opleft -= 4;
574 						}
575 					} else if (*dp == '"' ||
576 					    *dp == '\'' ||
577 					    *dp == '$' ||
578 					    *dp == '`' ||
579 					    *dp == '\\') {
580 						*op++ = '\\';
581 						*op++ = *dp;
582 						opleft -= 2;
583 					} else {
584 						*op++ = *dp;
585 						opleft--;
586 					}
587 				}
588 				if (emit_quotes) {
589 					*op++ = '"';
590 					opleft--;
591 				}
592 
593 				*op = 0;
594 				break;
595 			case 'I':
596 				foo.s_addr = htonl(getULong(dp));
597 				opcount = strlcpy(op, inet_ntoa(foo), opleft);
598 				if (opcount >= opleft)
599 					goto toobig;
600 				opleft -= opcount;
601 				dp += 4;
602 				break;
603 			case 'l':
604 				opcount = snprintf(op, opleft, "%ld",
605 				    (long)getLong(dp));
606 				if (opcount >= opleft || opcount == -1)
607 					goto toobig;
608 				opleft -= opcount;
609 				dp += 4;
610 				break;
611 			case 'L':
612 				opcount = snprintf(op, opleft, "%ld",
613 				    (unsigned long)getULong(dp));
614 				if (opcount >= opleft || opcount == -1)
615 					goto toobig;
616 				opleft -= opcount;
617 				dp += 4;
618 				break;
619 			case 's':
620 				opcount = snprintf(op, opleft, "%d",
621 				    getShort(dp));
622 				if (opcount >= opleft || opcount == -1)
623 					goto toobig;
624 				opleft -= opcount;
625 				dp += 2;
626 				break;
627 			case 'S':
628 				opcount = snprintf(op, opleft, "%d",
629 				    getUShort(dp));
630 				if (opcount >= opleft || opcount == -1)
631 					goto toobig;
632 				opleft -= opcount;
633 				dp += 2;
634 				break;
635 			case 'b':
636 				opcount = snprintf(op, opleft, "%d",
637 				    *(char *)dp++);
638 				if (opcount >= opleft || opcount == -1)
639 					goto toobig;
640 				opleft -= opcount;
641 				break;
642 			case 'B':
643 				opcount = snprintf(op, opleft, "%d", *dp++);
644 				if (opcount >= opleft || opcount == -1)
645 					goto toobig;
646 				opleft -= opcount;
647 				break;
648 			case 'x':
649 				opcount = snprintf(op, opleft, "%x", *dp++);
650 				if (opcount >= opleft || opcount == -1)
651 					goto toobig;
652 				opleft -= opcount;
653 				break;
654 			case 'f':
655 				opcount = strlcpy(op,
656 				    *dp++ ? "true" : "false", opleft);
657 				if (opcount >= opleft)
658 					goto toobig;
659 				opleft -= opcount;
660 				break;
661 			default:
662 				warning("Unexpected format code %c", fmtbuf[j]);
663 			}
664 			op += strlen(op);
665 			opleft -= strlen(op);
666 			if (opleft < 1)
667 				goto toobig;
668 			if (j + 1 < numelem && comma != ':') {
669 				*op++ = ' ';
670 				opleft--;
671 			}
672 		}
673 		if (i + 1 < numhunk) {
674 			*op++ = comma;
675 			opleft--;
676 		}
677 		if (opleft < 1)
678 			goto toobig;
679 
680 	}
681 	return (optbuf);
682  toobig:
683 	warning("dhcp option too large");
684 	return ("<error>");
685 }
686 
687 void
688 do_packet(struct interface_info *interface, struct dhcp_packet *packet,
689     int len, unsigned int from_port, struct iaddr from, struct hardware *hfrom)
690 {
691 	struct packet tp;
692 	int i;
693 
694 	if (packet->hlen > sizeof(packet->chaddr)) {
695 		note("Discarding packet with invalid hlen.");
696 		return;
697 	}
698 
699 	memset(&tp, 0, sizeof(tp));
700 	tp.raw = packet;
701 	tp.packet_length = len;
702 	tp.client_port = from_port;
703 	tp.client_addr = from;
704 	tp.interface = interface;
705 	tp.haddr = hfrom;
706 
707 	parse_options(&tp);
708 	if (tp.options_valid &&
709 	    tp.options[DHO_DHCP_MESSAGE_TYPE].data)
710 		tp.packet_type = tp.options[DHO_DHCP_MESSAGE_TYPE].data[0];
711 	if (tp.packet_type)
712 		dhcp(&tp);
713 	else
714 		bootp(&tp);
715 
716 	/* Free the data associated with the options. */
717 	for (i = 0; i < 256; i++)
718 		if (tp.options[i].len && tp.options[i].data)
719 			free(tp.options[i].data);
720 }
721