xref: /illumos-gate/usr/src/stand/lib/inet/dhcpv4.c (revision 60405de4d8688d96dd05157c28db3ade5c9bc234)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Standalone dhcp client.
27  */
28 
29 #pragma ident	"%Z%%M%	%I%	%E% SMI"
30 
31 #include <sys/types.h>
32 #include <sys/salib.h>
33 #include <sys/bootconf.h>
34 #include <sys/bootcmn.h>
35 #include <sys/socket.h>
36 #include <sys/isa_defs.h>
37 #include <netinet/in.h>
38 #include <netinet/in_systm.h>
39 #include <netinet/inetutil.h>
40 #include <netinet/dhcp.h>
41 #include <netinet/ip.h>
42 #include <netinet/udp.h>
43 #include <dhcp_impl.h>
44 #include <net/if_types.h>
45 #include <sys/promif.h>
46 #include <sys/platnames.h>
47 #include <socket_inet.h>
48 
49 #include "ipv4.h"
50 #include "mac.h"
51 #include <sys/bootdebug.h>
52 #include "dhcpv4.h"
53 
54 static char		*s_n = "INIT";
55 static enum DHCPSTATE 	dhcp_state = INIT;
56 
57 static PKT		*dhcp_snd_bufp, *dhcp_rcv_bufp;
58 static int		dhcp_buf_size;
59 
60 static const uint8_t	magic[] = BOOTMAGIC;	/* RFC1048 */
61 static uint8_t		opt_discover[]  = { CD_DHCP_TYPE, 1, DISCOVER };
62 static uint8_t		opt_request[]   = { CD_DHCP_TYPE, 1, REQUEST };
63 static uint8_t		opt_decline[]   = { CD_DHCP_TYPE, 1, DECLINE };
64 
65 static uint8_t		dhcp_classid[DHCP_MAX_OPT_SIZE + 3];
66 static uint8_t		dhcp_clientid[DHCP_MAX_CID_LEN];
67 static uint8_t		dhcp_clientid_len = 0;
68 
69 static uint32_t		dhcp_start_time;	/* start time (msecs */
70 static time_t		dhcp_secs;
71 static uint32_t		timeout;	/* timeout in milliseconds */
72 
73 static int		pkt_counter;
74 PKT_LIST		*list_tl, *list_hd;
75 PKT_LIST		*state_pl = NULL;
76 
77 #define	PROM_BOOT_CACHED	"bootp-response"
78 #ifndef	__i386
79 extern char	*bootp_response;	/* bootprop.c */
80 #else
81 char		*bootp_response;	/* i386 has *real* bsetprop */
82 #endif	/* __i386 */
83 extern int	pagesize;
84 
85 /*
86  * Do whatever reset actions/initialization actions are generic for every
87  * DHCP/bootp message. Set the message type.
88  *
89  * Returns: the updated options ptr.
90  */
91 static uint8_t *
92 init_msg(PKT *pkt, uint8_t *pkttype)
93 {
94 	static uint32_t xid;
95 
96 	bzero(pkt, dhcp_buf_size);
97 	bcopy(magic, pkt->cookie, sizeof (pkt->cookie));
98 	pkt->op = BOOTREQUEST;
99 	if (xid == 0)
100 		bcopy(mac_get_addr_buf()+2, &xid, 4);
101 	else
102 		xid++;
103 	pkt->xid = xid;
104 	bcopy(pkttype, pkt->options, 3);
105 	return ((uint8_t *)(pkt->options + 3));
106 }
107 
108 /*
109  *  Parameter request list.
110  */
111 static void
112 parameter_request_list(uint8_t **opt)
113 {
114 	/*
115 	 * This parameter request list is used in the normal 4-packet
116 	 * DHCPDISCOVER/OFFER/REQUEST/ACK exchange; it must not contain
117 	 * CD_REQUESTED_IP_ADDR or CD_LEASE_TIME.
118 	 */
119 	static uint8_t	prlist[] = {
120 	    CD_REQUEST_LIST,	/* parameter request list option number */
121 	    4,			/* number of options requested */
122 	    CD_SUBNETMASK,
123 	    CD_ROUTER,
124 	    CD_HOSTNAME,
125 	    CD_VENDOR_SPEC
126 	    };
127 	if (opt && *opt) {
128 		bcopy(prlist, *opt, sizeof (prlist));
129 		*opt += sizeof (prlist);
130 	}
131 }
132 
133 /*
134  * Set hardware specific fields
135  */
136 static void
137 set_hw_spec_data(PKT *p, uint8_t **opt, uint8_t *pkttype)
138 {
139 	char mfg[DHCP_MAX_OPT_SIZE + 1], cbuf[DHCP_MAX_OPT_SIZE + 1];
140 	uint8_t *tp, *dp;
141 	int adjust_len, len, i;
142 
143 	p->htype = mac_arp_type(mac_get_type());
144 	len = (uchar_t)mac_get_addr_len();
145 	if (len <= sizeof (p->chaddr)) {
146 		p->hlen = len;
147 		bcopy(mac_get_addr_buf(), p->chaddr, len);
148 	} else {
149 		uint8_t type = *(pkttype + 2);
150 		/*
151 		 * The mac address does not fit in the chaddr
152 		 * field, thus it can not be sent to the server,
153 		 * thus server can not unicast the reply. Per
154 		 * RFC 2131 4.4.1, client can set this bit in
155 		 * DISCOVER/REQUEST.
156 		 */
157 		if ((type == DISCOVER) || (type == REQUEST))
158 			p->flags = htons(BCAST_MASK);
159 	}
160 
161 	if (opt && *opt) {
162 		if (dhcp_classid[0] == '\0') {
163 			/*
164 			 * Classids based on mfg name: Commas (,) are
165 			 * converted to periods (.), and spaces ( ) are removed.
166 			 */
167 			dhcp_classid[0] = CD_CLASS_ID;
168 
169 			(void) strncpy(mfg, get_mfg_name(), sizeof (mfg));
170 			if (strncmp(mfg, "SUNW", strlen("SUNW")) != 0) {
171 				len = strlen("SUNW.");
172 				(void) strcpy(cbuf, "SUNW.");
173 			} else {
174 				len = 0;
175 				cbuf[0] = '\0';
176 			}
177 			len += strlen(mfg);
178 
179 			if ((len + 2) < DHCP_MAX_OPT_SIZE) {
180 				tp = (uint8_t *)strcat(cbuf, mfg);
181 				dp = &dhcp_classid[2];
182 				adjust_len = 0;
183 				for (i = 0; i < len; i++, tp++) {
184 					if (*tp == ',') {
185 						*dp++ = '.';
186 					} else if (*tp == ' ') {
187 						adjust_len++;
188 					} else {
189 						*dp++ = *tp;
190 					}
191 				}
192 				len -= adjust_len;
193 				dhcp_classid[1] = (uint8_t)len;
194 			} else
195 				prom_panic("Not enough space for class id");
196 #ifdef	DHCP_DEBUG
197 			printf("%s: Classid: %s\n", s_n, &dhcp_classid[2]);
198 #endif	/* DHCP_DEBUG */
199 		}
200 		bcopy(dhcp_classid, *opt, dhcp_classid[1] + 2);
201 		*opt += dhcp_classid[1] + 2;
202 	}
203 }
204 
205 static void
206 flush_list(void)
207 {
208 	PKT_LIST *wk, *tmp;
209 
210 	wk = list_hd;
211 	while (wk != NULL) {
212 		tmp = wk;
213 		wk = wk->next;
214 		bkmem_free((char *)tmp->pkt, tmp->len);
215 		bkmem_free((char *)tmp, sizeof (PKT_LIST));
216 	}
217 	list_hd = list_tl = NULL;
218 	pkt_counter = 0;
219 }
220 
221 static void
222 remove_list(PKT_LIST *pl, int flag)
223 {
224 	if (list_hd == NULL)
225 		return;
226 
227 	if (list_hd == list_tl) {
228 		list_hd = list_tl = NULL;
229 	} else if (list_hd == pl) {
230 		list_hd = pl->next;
231 		list_hd->prev = NULL;
232 	} else if (list_tl == pl) {
233 		list_tl = list_tl->prev;
234 		list_tl->next = NULL;
235 	} else {
236 		pl->prev->next = pl->next;
237 		pl->next->prev = pl->prev;
238 	}
239 	pkt_counter--;
240 	if (flag) {
241 		bkmem_free((char *)pl->pkt, pl->len);
242 		bkmem_free((char *)pl, sizeof (PKT_LIST));
243 	}
244 }
245 
246 /*
247  * Collects BOOTP responses. Length has to be right, it has to be
248  * a BOOTP reply pkt, with the same XID and HW address as ours. Adds
249  * them to the pkt list.
250  *
251  * Returns 0 if no error processing packet, 1 if an error occurred and/or
252  * collection of replies should stop. Used in inet() calls.
253  */
254 static int
255 bootp_collect(int len)
256 {
257 	PKT		*s = (PKT *)dhcp_snd_bufp;
258 	PKT		*r = (PKT *)dhcp_rcv_bufp;
259 	PKT_LIST	*pl;
260 
261 	if (len < sizeof (PKT)) {
262 		dprintf("%s: BOOTP reply too small: %d\n", s_n, len);
263 		return (1);
264 	}
265 	if (r->op == BOOTREPLY && r->xid == s->xid &&
266 	    bcmp((caddr_t)s->chaddr, (caddr_t)r->chaddr, s->hlen) == 0) {
267 		/* Add a packet to the pkt list */
268 		if (pkt_counter > (MAX_PKT_LIST - 1))
269 			return (1);	/* got enough packets already */
270 		if (((pl = (PKT_LIST *)bkmem_zalloc(sizeof (PKT_LIST))) ==
271 		    NULL) || ((pl->pkt = (PKT *)bkmem_zalloc(len)) == NULL)) {
272 			errno = ENOMEM;
273 			if (pl != NULL)
274 				bkmem_free((char *)pl, sizeof (PKT_LIST));
275 			return (1);
276 		}
277 		bcopy(dhcp_rcv_bufp, pl->pkt, len);
278 		pl->len = len;
279 		if (list_hd == NULL) {
280 			list_hd = list_tl = pl;
281 			pl->prev = NULL;
282 		} else {
283 			list_tl->next = pl;
284 			pl->prev = list_tl;
285 			list_tl = pl;
286 		}
287 		pkt_counter++;
288 		pl->next = NULL;
289 	}
290 	return (0);
291 }
292 
293 /*
294  * Checks if BOOTP exchange(s) were successful. Returns 1 if they
295  * were, 0 otherwise. Used in inet() calls.
296  */
297 static int
298 bootp_success(void)
299 {
300 	PKT	*s = (PKT *)dhcp_snd_bufp;
301 
302 	if (list_hd != NULL) {
303 		/* remember the secs - we may need them later */
304 		dhcp_secs = ntohs(s->secs);
305 		return (1);
306 	}
307 	s->secs = htons((uint16_t)((prom_gettime() - dhcp_start_time)/1000));
308 	return (0);
309 }
310 
311 /*
312  * This function accesses the network. Opens a connection, and binds to
313  * it if a client binding doesn't already exist. If 'tries' is 0, then
314  * no reply is expected/returned. If 'tries' is non-zero, then 'tries'
315  * attempts are made to get a valid response. If 'tol' is not zero,
316  * then this function will wait for 'tol' milliseconds for more than one
317  * response to a transmit.
318  *
319  * Returns 0 for success, errno otherwise.
320  */
321 static int
322 inet(uint32_t size, struct in_addr *src, struct in_addr *dest, uint32_t tries,
323     uint32_t tol)
324 {
325 	int			done = B_FALSE, flags, len;
326 	uint32_t		attempts = 0;
327 	int			sd;
328 	uint32_t		wait_time;	/* Max time collect replies */
329 	uint32_t		init_timeout;	/* Max time wait ANY reply */
330 	uint32_t		now;
331 	struct sockaddr_in	saddr, daddr;
332 
333 	if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
334 		dprintf("%s: Can't open a socket.\n", s_n);
335 		return (errno);
336 	}
337 
338 	flags = 0;
339 
340 	bzero(&saddr, sizeof (struct sockaddr_in));
341 	saddr.sin_family = AF_INET;
342 	saddr.sin_port = htons(IPPORT_BOOTPC);
343 	saddr.sin_addr.s_addr = htonl(src->s_addr);
344 
345 	if (bind(sd, (struct sockaddr *)&saddr, sizeof (saddr)) < 0) {
346 		dprintf("%s: Cannot bind to port %d, errno: %d\n",
347 		    s_n, IPPORT_BOOTPC, errno);
348 		(void) socket_close(sd);
349 		return (errno);
350 	}
351 
352 	if (ntohl(dest->s_addr) == INADDR_BROADCAST) {
353 		int dontroute = B_TRUE;
354 		(void) setsockopt(sd, SOL_SOCKET, SO_DONTROUTE,
355 		    (const void *)&dontroute, sizeof (dontroute));
356 	}
357 
358 	bzero(&daddr, sizeof (struct sockaddr_in));
359 	daddr.sin_family = AF_INET;
360 	daddr.sin_port = htons(IPPORT_BOOTPS);
361 	daddr.sin_addr.s_addr = htonl(dest->s_addr);
362 	wait_time = prom_gettime() + tol;
363 
364 	do {
365 		if (sendto(sd, (char *)dhcp_snd_bufp, size, flags,
366 		    (struct sockaddr *)&daddr, sizeof (daddr)) < 0) {
367 			dprintf("%s: sendto failed with errno: %d\n",
368 			    s_n, errno);
369 			(void) socket_close(sd);
370 			return (errno);
371 		}
372 		if (!tries)
373 			break;   /* don't bother to check for reply */
374 
375 		now = prom_gettime();
376 		if (timeout == 0)
377 			timeout = 4000;
378 		else {
379 			timeout <<= 1;
380 			if (timeout > 64000)
381 				timeout = 64000;
382 		}
383 		init_timeout = now + timeout;
384 		wait_time = now + tol;
385 		do {
386 			if ((len = recvfrom(sd, (char *)dhcp_rcv_bufp,
387 			    (int)dhcp_buf_size, MSG_DONTWAIT, NULL,
388 			    NULL)) < 0) {
389 				if (errno == EWOULDBLOCK)
390 					continue;	/* DONT WAIT */
391 				(void) socket_close(sd);
392 				flush_list();
393 				return (errno);
394 			}
395 
396 			if (bootp_collect(len))
397 				break;	/* Stop collecting */
398 
399 			if (tol != 0) {
400 				if (wait_time < prom_gettime())
401 					break; /* collection timeout */
402 			}
403 		} while (prom_gettime() < init_timeout);
404 
405 		if (bootp_success()) {
406 			done = B_TRUE;
407 			break;  /* got the goods */
408 		}
409 	} while (++attempts < tries);
410 
411 	(void) socket_close(sd);
412 
413 	return (done ? 0 : DHCP_NO_DATA);
414 }
415 
416 /*
417  * Print the message from the server.
418  */
419 static void
420 prt_server_msg(DHCP_OPT *p)
421 {
422 	int len = p->len;
423 	char scratch[DHCP_MAX_OPT_SIZE + 1];
424 
425 	if (len > DHCP_MAX_OPT_SIZE)
426 		len = DHCP_MAX_OPT_SIZE;
427 	bcopy(p->value, scratch, len);
428 	scratch[len] = '\0';
429 	printf("%s: Message from server: '%s'\n", s_n, scratch);
430 }
431 
432 /*
433  * This function scans the list of OFFERS, and returns the "best" offer.
434  * The criteria used for determining this is:
435  *
436  * The best:
437  * DHCP OFFER (not BOOTP), same client_id as ours, same class_id,
438  * Longest lease, all the options we need.
439  *
440  * Not quite as good:
441  * DHCP OFFER, no class_id, short lease, only some of the options we need.
442  *
443  * We're really reach'in
444  * BOOTP reply.
445  *
446  * DON'T select an offer from a server that gave us a configuration we
447  * couldn't use. Take this server off the "bad" list when this is done.
448  * Next time, we could potentially retry this server's configuration.
449  *
450  * NOTE: perhaps this bad server should have a counter associated with it.
451  */
452 static PKT_LIST *
453 select_best(void)
454 {
455 	PKT_LIST	*wk, *tk, *best;
456 	int		err = 0;
457 
458 	/* Pass one. Scan for options, set appropriate opt field. */
459 	wk = list_hd;
460 	while (wk != NULL) {
461 		if ((err = dhcp_options_scan(wk, B_TRUE)) != 0) {
462 			/* Garbled Options. Nuke this pkt. */
463 			if (boothowto & RB_DEBUG) {
464 				switch (err) {
465 				case DHCP_WRONG_MSG_TYPE:
466 					printf("%s: Unexpected DHCP message.\n",
467 					    s_n);
468 					break;
469 				case DHCP_GARBLED_MSG_TYPE:
470 					printf(
471 					    "%s: Garbled DHCP message type.\n",
472 					    s_n);
473 					break;
474 				case DHCP_BAD_OPT_OVLD:
475 					printf("%s: Bad option overload.\n",
476 					    s_n);
477 					break;
478 				}
479 			}
480 			tk = wk;
481 			wk = wk->next;
482 			remove_list(tk, B_TRUE);
483 			continue;
484 		}
485 		wk = wk->next;
486 	}
487 
488 	/*
489 	 * Pass two. Pick out the best offer. Point system.
490 	 * What's important?
491 	 *	0) DHCP
492 	 *	1) No option overload
493 	 *	2) Encapsulated vendor option
494 	 *	3) Non-null sname and siaddr fields
495 	 *	4) Non-null file field
496 	 *	5) Hostname
497 	 *	6) Subnetmask
498 	 *	7) Router
499 	 */
500 	best = NULL;
501 	for (wk = list_hd; wk != NULL; wk = wk->next) {
502 		wk->offset = 0;
503 		if (wk->opts[CD_DHCP_TYPE] &&
504 		    wk->opts[CD_DHCP_TYPE]->len == 1) {
505 			if (*wk->opts[CD_DHCP_TYPE]->value != OFFER) {
506 				dprintf("%s: Unexpected DHCP message."
507 				    " Expected OFFER message.\n", s_n);
508 				continue;
509 			}
510 			if (!wk->opts[CD_LEASE_TIME]) {
511 				dprintf("%s: DHCP OFFER message without lease "
512 				    "time parameter.\n", s_n);
513 				continue;
514 			} else {
515 				if (wk->opts[CD_LEASE_TIME]->len != 4) {
516 					dprintf("%s: Lease expiration time is "
517 					    "garbled.\n", s_n);
518 					continue;
519 				}
520 			}
521 			if (!wk->opts[CD_SERVER_ID]) {
522 				dprintf("%s: DHCP OFFER message without server "
523 				    "id parameter.\n", s_n);
524 				continue;
525 			} else {
526 				if (wk->opts[CD_SERVER_ID]->len != 4) {
527 					dprintf("%s: Server identifier "
528 					    "parameter is garbled.\n", s_n);
529 					continue;
530 				}
531 			}
532 			/* Valid DHCP OFFER. See if we got our parameters. */
533 			dprintf("%s: Found valid DHCP OFFER message.\n", s_n);
534 
535 			wk->offset += 30;
536 
537 			/*
538 			 * Also could be faked, though more difficult
539 			 * because the encapsulation is hard to encode
540 			 * on a BOOTP server; plus there's not as much
541 			 * real estate in the packet for options, so
542 			 * it's likely this option would get dropped.
543 			 */
544 			if (wk->opts[CD_VENDOR_SPEC])
545 				wk->offset += 80;
546 		} else
547 			dprintf("%s: Found valid BOOTP reply.\n", s_n);
548 
549 		/*
550 		 * RFC1048 BOOTP?
551 		 */
552 		if (bcmp((caddr_t)wk->pkt->cookie, (caddr_t)magic,
553 		    sizeof (magic)) == 0) {
554 			wk->offset += 5;
555 			if (wk->opts[CD_SUBNETMASK])
556 				wk->offset++;
557 			if (wk->opts[CD_ROUTER])
558 				wk->offset++;
559 			if (wk->opts[CD_HOSTNAME])
560 				wk->offset += 5;
561 
562 			/*
563 			 * Prefer options that have diskless boot significance
564 			 */
565 			if (ntohl(wk->pkt->siaddr.s_addr) != INADDR_ANY)
566 				wk->offset += 10; /* server ip */
567 			if (wk->opts[CD_OPTION_OVERLOAD] == NULL) {
568 				if (wk->pkt->sname[0] != '\0')
569 					wk->offset += 10; /* server name */
570 				if (wk->pkt->file[0] != '\0')
571 					wk->offset += 5; /* File to load */
572 			}
573 		}
574 #ifdef	DHCP_DEBUG
575 		printf("%s: This server configuration has '%d' points.\n", s_n,
576 			wk->offset);
577 #endif	/* DHCP_DEBUG */
578 		if (!best)
579 			best = wk;
580 		else {
581 			if (best->offset < wk->offset)
582 				best = wk;
583 		}
584 	}
585 	if (best) {
586 #ifdef	DHCP_DEBUG
587 		printf("%s: Found best: points: %d\n", s_n, best->offset);
588 #endif	/* DHCP_DEBUG */
589 		remove_list(best, B_FALSE);
590 	} else {
591 		dprintf("%s: No valid BOOTP reply or DHCP OFFER was found.\n",
592 		    s_n);
593 	}
594 	flush_list();	/* toss the remaining list */
595 	return (best);
596 }
597 
598 /*
599  * Send a decline message to the generator of the DHCPACK.
600  */
601 static void
602 dhcp_decline(char *msg, PKT_LIST *pl)
603 {
604 	PKT		*pkt;
605 	uint8_t		*opt, ulen;
606 	int		pkt_size;
607 	struct in_addr	nets, ours, t_server, t_yiaddr;
608 
609 	if (pl != NULL) {
610 		if (pl->opts[CD_SERVER_ID] != NULL &&
611 		    pl->opts[CD_SERVER_ID]->len == sizeof (struct in_addr)) {
612 			bcopy(pl->opts[CD_SERVER_ID]->value,
613 			    &t_server, pl->opts[CD_SERVER_ID]->len);
614 		} else
615 			t_server.s_addr = htonl(INADDR_BROADCAST);
616 		t_yiaddr.s_addr = pl->pkt->yiaddr.s_addr;
617 	}
618 	pkt = (PKT *)dhcp_snd_bufp;
619 	opt = init_msg(pkt, opt_decline);
620 	set_hw_spec_data(pkt, &opt, opt_decline);
621 
622 	*opt++ = CD_SERVER_ID;
623 	*opt++ = sizeof (struct in_addr);
624 	bcopy(&t_server, opt, sizeof (struct in_addr));
625 	opt += sizeof (struct in_addr);
626 	nets.s_addr = htonl(INADDR_BROADCAST);
627 
628 	/*
629 	 * Use the given yiaddr in our ciaddr field so server can identify us.
630 	 */
631 	pkt->ciaddr.s_addr = t_yiaddr.s_addr;
632 
633 	ipv4_getipaddr(&ours);	/* Could be 0, could be yiaddr */
634 	ours.s_addr = htonl(ours.s_addr);
635 
636 	ulen = (uint8_t)strlen(msg);
637 	*opt++ = CD_MESSAGE;
638 	*opt++ = ulen;
639 	bcopy(msg, opt, ulen);
640 	opt += ulen;
641 	*opt++ = CD_END;
642 
643 	pkt_size = (uint8_t *)opt - (uint8_t *)pkt;
644 	if (pkt_size < sizeof (PKT))
645 		pkt_size = sizeof (PKT);
646 
647 	timeout = 0;	/* reset timeout */
648 	(void) inet(pkt_size, &ours, &nets, 0, 0L);
649 }
650 
651 /*
652  * Implementings SELECTING state of DHCP client state machine.
653  */
654 static int
655 dhcp_selecting(void)
656 {
657 	int		pkt_size;
658 	PKT		*pkt;
659 	uint8_t		*opt;
660 	uint16_t	size;
661 	uint32_t	lease;
662 	struct in_addr	nets, ours;
663 
664 	pkt = (PKT *)dhcp_snd_bufp;
665 	opt = init_msg(pkt, opt_discover);
666 	pkt->secs = htons((uint16_t)((prom_gettime() - dhcp_start_time)/1000));
667 
668 	*opt++ = CD_MAX_DHCP_SIZE;
669 	*opt++ = sizeof (size);
670 	size = (uint16_t)(dhcp_buf_size - sizeof (struct ip) -
671 	    sizeof (struct udphdr));
672 	size = htons(size);
673 	bcopy(&size, opt, sizeof (size));
674 	opt += sizeof (size);
675 
676 	set_hw_spec_data(pkt, &opt, opt_discover);
677 
678 	*opt++ = CD_LEASE_TIME;
679 	*opt++ = sizeof (lease);
680 	lease = htonl(DHCP_PERM);	/* ask for a permanent lease */
681 	bcopy(&lease, opt, sizeof (lease));
682 	opt += sizeof (lease);
683 
684 	/*
685 	 * If a clientid was set using dhcp_set_client_id(), add this
686 	 * to the options.
687 	 */
688 	if (dhcp_clientid_len > 0) {
689 		*opt++ = CD_CLIENT_ID;
690 		*opt++ = dhcp_clientid_len;
691 		bcopy(dhcp_clientid, opt, dhcp_clientid_len);
692 		opt += dhcp_clientid_len;
693 	}
694 
695 	parameter_request_list(&opt);
696 
697 	*opt++ = CD_END;
698 
699 	pkt_size = (uint8_t *)opt - (uint8_t *)pkt;
700 	if (pkt_size < sizeof (PKT))
701 		pkt_size = sizeof (PKT);
702 
703 	nets.s_addr = INADDR_BROADCAST;
704 	ours.s_addr = INADDR_ANY;
705 	timeout = 0;	/* reset timeout */
706 
707 	return (inet(pkt_size, &ours, &nets, DHCP_RETRIES, DHCP_WAIT));
708 }
709 
710 /*
711  * implements the REQUESTING state of the DHCP client state machine.
712  */
713 static int
714 dhcp_requesting(void)
715 {
716 	PKT_LIST	*pl, *wk;
717 	PKT		*pkt, *pl_pkt;
718 	uint8_t		*opt;
719 	int		pkt_size, err;
720 	uint32_t	t_time;
721 	struct in_addr	nets, ours;
722 	DHCP_OPT	*doptp;
723 	uint16_t	size;
724 
725 	/* here we scan the list of OFFERS, select the best one. */
726 	state_pl = NULL;
727 
728 	if ((pl = select_best()) == NULL) {
729 		dprintf("%s: No valid BOOTP reply or DHCP OFFER was found.\n",
730 		    s_n);
731 		return (1);
732 	}
733 
734 	pl_pkt = pl->pkt;
735 
736 	/*
737 	 * Check to see if we got an OFFER pkt(s). If not, then We only got
738 	 * a response from a BOOTP server. We'll go to the bound state and
739 	 * try to use that configuration.
740 	 */
741 	if (pl->opts[CD_DHCP_TYPE] == NULL) {
742 		if (mac_call_arp(&pl_pkt->yiaddr, NULL, DHCP_ARP_TIMEOUT)) {
743 			/* Someone responded! go back to SELECTING state. */
744 			printf("%s: Some host already using BOOTP %s.\n", s_n,
745 			    inet_ntoa(pl_pkt->yiaddr));
746 			bkmem_free((char *)pl->pkt, pl->len);
747 			bkmem_free((char *)pl, sizeof (PKT_LIST));
748 			return (1);
749 		}
750 		state_pl = pl;
751 		return (0);
752 	}
753 
754 	/*
755 	 * if we got a message from the server, display it.
756 	 */
757 	if (pl->opts[CD_MESSAGE])
758 		prt_server_msg(pl->opts[CD_MESSAGE]);
759 	/*
760 	 * Assemble a DHCPREQUEST, with the ciaddr field set to 0, since we
761 	 * got here from DISCOVER state. Keep secs field the same for relay
762 	 * agents. We start with the DHCPOFFER packet we got, and the
763 	 * options contained in it to make a requested option list.
764 	 */
765 	pkt = (PKT *)dhcp_snd_bufp;
766 	opt = init_msg(pkt, opt_request);
767 
768 	/* Time from Successful DISCOVER message. */
769 	pkt->secs = htons((uint16_t)dhcp_secs);
770 
771 	size = (uint16_t)(dhcp_buf_size - sizeof (struct ip) -
772 	    sizeof (struct udphdr));
773 	size = htons(size);
774 	*opt++ = CD_MAX_DHCP_SIZE;
775 	*opt++ = sizeof (size);
776 	bcopy(&size, opt, sizeof (size));
777 	opt += sizeof (size);
778 
779 	set_hw_spec_data(pkt, &opt, opt_request);
780 	*opt++ = CD_SERVER_ID;
781 	*opt++ = pl->opts[CD_SERVER_ID]->len;
782 	bcopy(pl->opts[CD_SERVER_ID]->value, opt,
783 	    pl->opts[CD_SERVER_ID]->len);
784 	opt += pl->opts[CD_SERVER_ID]->len;
785 
786 	/* our offered lease minus boot time */
787 	*opt++ = CD_LEASE_TIME;
788 	*opt++ = 4;
789 	bcopy(pl->opts[CD_LEASE_TIME]->value, &t_time,
790 	    sizeof (t_time));
791 	t_time = ntohl(t_time);
792 	if ((uint32_t)t_time != DHCP_PERM)
793 		t_time -= (uint32_t)dhcp_secs;
794 	t_time = htonl(t_time);
795 	bcopy(&t_time, opt, sizeof (t_time));
796 	opt += sizeof (t_time);
797 
798 	/* our offered IP address, as required. */
799 	*opt++ = CD_REQUESTED_IP_ADDR;
800 	*opt++ = sizeof (struct in_addr);
801 	bcopy(&pl_pkt->yiaddr, opt, sizeof (struct in_addr));
802 	opt += sizeof (struct in_addr);
803 
804 	/*
805 	 * If a clientid was set using dhcp_set_client_id(), add this
806 	 * to the options.
807 	 */
808 	if (dhcp_clientid_len > 0) {
809 		*opt++ = CD_CLIENT_ID;
810 		*opt++ = dhcp_clientid_len;
811 		bcopy(dhcp_clientid, opt, dhcp_clientid_len);
812 		opt += dhcp_clientid_len;
813 	}
814 
815 	parameter_request_list(&opt);
816 
817 	*opt++ = CD_END;
818 
819 	/* Done with the OFFER pkt. */
820 	bkmem_free((char *)pl->pkt, pl->len);
821 	bkmem_free((char *)pl, sizeof (PKT_LIST));
822 
823 	/*
824 	 * We make 4 attempts here. We wait for 2 seconds to accumulate
825 	 * requests, just in case a BOOTP server is too fast!
826 	 */
827 	pkt_size = (uint8_t *)opt - (uint8_t *)pkt;
828 	if (pkt_size < sizeof (PKT))
829 		pkt_size = sizeof (PKT);
830 
831 	nets.s_addr = INADDR_BROADCAST;
832 	ours.s_addr = INADDR_ANY;
833 	timeout = 0;	/* reset timeout */
834 
835 	if ((err = inet(pkt_size, &ours, &nets, 4, (time_t)2L)) != 0)
836 		return (err);
837 	for (wk = list_hd; wk != NULL && state_pl == NULL; wk = wk->next) {
838 		if (dhcp_options_scan(wk, B_TRUE) != 0 ||
839 		    !wk->opts[CD_DHCP_TYPE])
840 			continue;	/* garbled options */
841 		switch (*wk->opts[CD_DHCP_TYPE]->value) {
842 		case ACK:
843 			remove_list(wk, B_FALSE);
844 			state_pl = wk;
845 			break;
846 		case NAK:
847 			printf("%s: rejected by DHCP server: %s\n",
848 			    s_n, inet_ntoa(*((struct in_addr *)wk->
849 			    opts[CD_SERVER_ID]->value)));
850 			if (wk->opts[CD_MESSAGE])
851 				prt_server_msg(wk->opts[CD_MESSAGE]);
852 			break;
853 		default:
854 			dprintf("%s: Unexpected DHCP message type.\n", s_n);
855 			break;
856 		}
857 	}
858 	flush_list();
859 	if (state_pl != NULL) {
860 		if (state_pl->opts[CD_MESSAGE])
861 			prt_server_msg(state_pl->opts[CD_MESSAGE]);
862 		pl_pkt = state_pl->pkt;
863 		/* arp our new address, just to make sure */
864 		if (!mac_call_arp(&pl_pkt->yiaddr, NULL, DHCP_ARP_TIMEOUT)) {
865 			/*
866 			 * No response. Check if lease is ok.
867 			 */
868 			doptp = state_pl->opts[CD_LEASE_TIME];
869 			if (state_pl->opts[CD_DHCP_TYPE] && (!doptp ||
870 			    (doptp->len % 4) != 0)) {
871 				dhcp_decline("Missing or corrupted lease time",
872 				    state_pl);
873 				err++;
874 			}
875 		} else {
876 			dhcp_decline("IP Address already being used!",
877 			    state_pl);
878 			err++;
879 		}
880 		if (err) {
881 			bkmem_free((char *)state_pl->pkt, state_pl->len);
882 			bkmem_free((char *)state_pl, sizeof (PKT_LIST));
883 			state_pl = NULL;
884 		} else
885 			return (0);	/* passes the tests! */
886 	}
887 	dprintf("%s: No valid DHCP acknowledge messages received.\n", s_n);
888 	return (1);
889 }
890 
891 /*
892  * Implements BOUND state of DHCP client state machine.
893  */
894 static int
895 dhcp_bound(void)
896 {
897 	PKT		*pl_pkt = state_pl->pkt;
898 	DHCP_OPT	*doptp;
899 	uint8_t		*tp, *hp;
900 	int		items, i, fnd, k;
901 	char		hostname[MAXHOSTNAMELEN+1];
902 	struct in_addr	subnet, defr, savr, *ipp, xip;
903 
904 #ifdef	DHCP_DEBUG
905 	if (dhcp_classid[0] != 0) {
906 		char 	scratch[DHCP_MAX_OPT_SIZE + 1];
907 
908 		bcopy(&dhcp_classid[2], scratch, dhcp_classid[1]);
909 		scratch[dhcp_classid[1]] = '\0';
910 		printf("Your machine is of the class: '%s'.\n", scratch);
911 	}
912 #endif	/* DHCP_DEBUG */
913 
914 	/* First, set the bare essentials. (IP layer parameters) */
915 
916 	ipv4_getipaddr(&xip);
917 	if (xip.s_addr != INADDR_ANY)
918 		ipp = &xip;
919 	else {
920 		ipp = &pl_pkt->yiaddr;
921 		ipp->s_addr = ntohl(ipp->s_addr);
922 		ipv4_setipaddr(ipp);
923 	}
924 
925 	ipp->s_addr = htonl(ipp->s_addr);
926 
927 	if (boothowto & RB_VERBOSE)
928 		printf("%s: IP address is: %s\n", s_n, inet_ntoa(*ipp));
929 
930 	/*
931 	 * Ensure that the Boot NFS READ size, if given, is an int16_t.
932 	 */
933 	if (state_pl->vs[VS_BOOT_NFS_READSIZE] != NULL) {
934 		doptp = state_pl->vs[VS_BOOT_NFS_READSIZE];
935 		if (doptp->len != sizeof (int16_t))
936 			state_pl->vs[VS_BOOT_NFS_READSIZE] = NULL;
937 	}
938 
939 	/*
940 	 * Set subnetmask. Nice, but not required.
941 	 */
942 	if (state_pl->opts[CD_SUBNETMASK] != NULL) {
943 		doptp = state_pl->opts[CD_SUBNETMASK];
944 		if (doptp->len != 4)
945 			state_pl->opts[CD_SUBNETMASK] = NULL;
946 		else {
947 			bcopy(doptp->value, &subnet,
948 			    sizeof (struct in_addr));
949 			dprintf("%s: Setting netmask to: %s\n", s_n,
950 			    inet_ntoa(subnet));
951 			subnet.s_addr = ntohl(subnet.s_addr);
952 			ipv4_setnetmask(&subnet);
953 		}
954 	}
955 
956 	/*
957 	 * Set default IP TTL. Nice, but not required.
958 	 */
959 	if (state_pl->opts[CD_IPTTL] != NULL) {
960 		doptp = state_pl->opts[CD_IPTTL];
961 		if (doptp->len > 1)
962 			state_pl->opts[CD_IPTTL] = NULL;
963 		else {
964 			doptp = state_pl->opts[CD_IPTTL];
965 			ipv4_setmaxttl(*(uint8_t *)doptp->value);
966 			dprintf("%s: Setting IP TTL to: %d\n", s_n,
967 			    *(uint8_t *)doptp->value);
968 		}
969 	}
970 
971 	/*
972 	 * Set default router. Not required, although we'll know soon
973 	 * enough...
974 	 */
975 	if (state_pl->opts[CD_ROUTER] != NULL) {
976 		doptp = state_pl->opts[CD_ROUTER];
977 		if ((doptp->len % 4) != 0) {
978 			state_pl->opts[CD_ROUTER] = NULL;
979 		} else {
980 			if ((hp = (uint8_t *)bkmem_alloc(
981 			    mac_get_hdr_len())) == NULL) {
982 				errno = ENOMEM;
983 				return (-1);
984 			}
985 			items = doptp->len / sizeof (struct in_addr);
986 			tp = doptp->value;
987 			bcopy(tp, &savr, sizeof (struct in_addr));
988 			for (i = 0, fnd = 0; i < items; i++) {
989 				bcopy(tp, &defr, sizeof (struct in_addr));
990 				for (k = 0, fnd = 0; k < 2 && fnd == 0; k++) {
991 					fnd = mac_get_arp(&defr, hp,
992 					    mac_get_hdr_len(),
993 					    mac_get_arp_timeout());
994 				}
995 				if (fnd)
996 					break;
997 				dprintf(
998 				    "%s: Warning: Router %s is unreachable.\n",
999 				    s_n, inet_ntoa(defr));
1000 				tp += sizeof (struct in_addr);
1001 			}
1002 			bkmem_free((char *)hp, mac_get_hdr_len());
1003 
1004 			/*
1005 			 * If fnd is 0, we didn't find a working router. We'll
1006 			 * still try to use first default router. If we got
1007 			 * a bad router address (like not on the same net),
1008 			 * we're hosed anyway.
1009 			 */
1010 			if (!fnd) {
1011 				dprintf(
1012 				    "%s: Warning: Using default router: %s\n",
1013 				    s_n, inet_ntoa(savr));
1014 				defr.s_addr = savr.s_addr;
1015 			}
1016 			/* ipv4_route expects network order IP addresses */
1017 			(void) ipv4_route(IPV4_ADD_ROUTE, RT_DEFAULT, NULL,
1018 			    &defr);
1019 		}
1020 	}
1021 
1022 	/*
1023 	 * Set hostname. Not required.
1024 	 */
1025 	if (state_pl->opts[CD_HOSTNAME] != NULL) {
1026 		doptp = state_pl->opts[CD_HOSTNAME];
1027 		i = doptp->len;
1028 		if (i > MAXHOSTNAMELEN)
1029 			i = MAXHOSTNAMELEN;
1030 		bcopy(doptp->value, hostname, i);
1031 		hostname[i] = '\0';
1032 		(void) sethostname(hostname, i);
1033 		if (boothowto & RB_VERBOSE)
1034 			printf("%s: Hostname is %s\n", s_n, hostname);
1035 	}
1036 
1037 	/*
1038 	 * We don't care about the lease time.... We can't enforce it anyway.
1039 	 */
1040 	return (0);
1041 }
1042 
1043 /*
1044  * Convert the DHCPACK into a pure ASCII boot property for use by the kernel
1045  * later in the boot process.
1046  */
1047 static void
1048 create_bootpresponse_bprop(PKT_LIST *pl)
1049 {
1050 	uint_t	buflen;
1051 #if defined(__i386)
1052 	extern struct bootops bootops;
1053 	extern int bsetprop(struct bootops *, char *, caddr_t, int, phandle_t);
1054 #endif	/* __i386 */
1055 
1056 	if (pl == NULL || bootp_response != NULL)
1057 		return;
1058 
1059 	buflen = (pl->len * 2) + 1;	/* extra space for null (1) */
1060 	if ((bootp_response = bkmem_alloc(buflen)) == NULL)
1061 		return;
1062 
1063 	if (octet_to_hexascii((uint8_t *)pl->pkt, pl->len, bootp_response,
1064 	    &buflen) != 0) {
1065 		bkmem_free(bootp_response, (pl->len * 2) + 1);
1066 		bootp_response = NULL;
1067 	}
1068 
1069 #if defined(__i386)
1070 	/* Use bsetprop to create the bootp-response property */
1071 	if (bsetprop(&bootops, "bootp-response", bootp_response, 0, 0) !=
1072 	    BOOT_SUCCESS) {
1073 		bkmem_free(bootp_response, (pl->len * 2) + 1);
1074 		bootp_response = NULL;
1075 	}
1076 #elif defined(__sparc)
1077 	prom_create_encoded_prop("bootp-response", pl->pkt, pl->len,
1078 	    ENCODE_BYTES);
1079 #endif	/* __i386 */
1080 }
1081 
1082 /*
1083  * Examines /chosen node for "bootp-response" property. If it exists, this
1084  * property is the DHCPACK cached there by the PROM's DHCP implementation.
1085  *
1086  * If cache_present is B_TRUE, we simply return B_TRUE if the property exists
1087  * w/o decoding it. If cache_present is B_FALSE, we decode the packet and
1088  * use it as our state packet for the jump to BOUND mode. Note that it's good
1089  * enough that the cache exists w/o validation for determining if that's what
1090  * the user intended.
1091  *
1092  * We'll short-circuit the DHCP exchange by accepting this packet. We build a
1093  * PKT_LIST structure, and copy the bootp-response property value into a
1094  * PKT buffer we allocated. We then scan the PKT for options, and then
1095  * set state_pl to point to it.
1096  *
1097  * Returns B_TRUE if a packet was cached (and was processed correctly), false
1098  * otherwise. The caller needs to make the state change from SELECTING to
1099  * BOUND upon a B_TRUE return from this function.
1100  */
1101 int
1102 prom_cached_reply(int cache_present)
1103 {
1104 	PKT_LIST	*pl;
1105 	int	len;
1106 #if defined(__i386)
1107 	char	*ack;
1108 	int	pxe_ack_cache(char **);
1109 
1110 	if ((len = pxe_ack_cache(&ack)) <= 0)
1111 		return (B_FALSE);
1112 #else
1113 	pnode_t	chosen;
1114 	char	*prop = PROM_BOOT_CACHED;
1115 
1116 	chosen = prom_finddevice("/chosen");
1117 	if (chosen == OBP_NONODE || chosen == OBP_BADNODE)
1118 		chosen = prom_nextnode((pnode_t)0);	/* root node */
1119 
1120 	if ((len = prom_getproplen(chosen, prop)) <= 0)
1121 		return (B_FALSE);
1122 #endif	/* __i386 */
1123 
1124 	if (cache_present)
1125 		return (B_TRUE);
1126 
1127 	if (((pl = (PKT_LIST *)bkmem_zalloc(sizeof (PKT_LIST))) == NULL) ||
1128 	    ((pl->pkt = (PKT *)bkmem_zalloc(len)) == NULL)) {
1129 		errno = ENOMEM;
1130 		if (pl != NULL)
1131 			bkmem_free((char *)pl, sizeof (PKT_LIST));
1132 		return (B_FALSE);
1133 	}
1134 
1135 #if defined(__i386)
1136 	bcopy(ack, pl->pkt, len);
1137 #else
1138 	(void) prom_getprop(chosen, prop, (caddr_t)pl->pkt);
1139 #endif	/* __i386 */
1140 
1141 	pl->len = len;
1142 
1143 	if (dhcp_options_scan(pl, B_TRUE) != 0) {
1144 		/* garbled packet */
1145 		bkmem_free((char *)pl->pkt, pl->len);
1146 		bkmem_free((char *)pl, sizeof (PKT_LIST));
1147 		return (B_FALSE);
1148 	}
1149 
1150 	state_pl = pl;
1151 	return (B_TRUE);
1152 }
1153 
1154 /*
1155  * Perform DHCP to acquire what we used to get using rarp/bootparams.
1156  * Returns 0 for success, nonzero otherwise.
1157  *
1158  * DHCP client state machine.
1159  */
1160 int
1161 dhcp(void)
1162 {
1163 	int	err = 0;
1164 	int	done = B_FALSE;
1165 
1166 	dhcp_buf_size = mac_get_mtu();
1167 	dhcp_snd_bufp = (PKT *)bkmem_alloc(dhcp_buf_size);
1168 	dhcp_rcv_bufp = (PKT *)bkmem_alloc(dhcp_buf_size);
1169 
1170 	if (dhcp_snd_bufp == NULL || dhcp_rcv_bufp == NULL) {
1171 		if (dhcp_snd_bufp != NULL)
1172 			bkmem_free((char *)dhcp_snd_bufp, dhcp_buf_size);
1173 		if (dhcp_rcv_bufp != NULL)
1174 			bkmem_free((char *)dhcp_rcv_bufp, dhcp_buf_size);
1175 		errno = ENOMEM;
1176 		return (-1);
1177 	}
1178 
1179 	while (err == 0 && !done) {
1180 		switch (dhcp_state) {
1181 		case INIT:
1182 
1183 			s_n = "INIT";
1184 			if (prom_cached_reply(B_FALSE)) {
1185 				dprintf("%s: Using PROM cached BOOT reply...\n",
1186 				    s_n);
1187 				dhcp_state = BOUND;
1188 			} else {
1189 				dhcp_state = SELECTING;
1190 				dhcp_start_time = prom_gettime();
1191 			}
1192 			break;
1193 
1194 		case SELECTING:
1195 
1196 			s_n = "SELECTING";
1197 			err = dhcp_selecting();
1198 			switch (err) {
1199 			case 0:
1200 				dhcp_state = REQUESTING;
1201 				break;
1202 			case DHCP_NO_DATA:
1203 				dprintf(
1204 				    "%s: No DHCP response after %d tries.\n",
1205 				    s_n, DHCP_RETRIES);
1206 				break;
1207 			default:
1208 				/* major network problems */
1209 				dprintf("%s: Network transaction failed: %d\n",
1210 				    s_n, err);
1211 				break;
1212 			}
1213 			break;
1214 
1215 		case REQUESTING:
1216 
1217 			s_n = "REQUESTING";
1218 			err = dhcp_requesting();
1219 			switch (err) {
1220 			case 0:
1221 				dhcp_state = BOUND;
1222 				break;
1223 			case DHCP_NO_DATA:
1224 				dprintf("%s: Request timed out.\n", s_n);
1225 				dhcp_state = SELECTING;
1226 				err = 0;
1227 				(void) sleep(10);
1228 				break;
1229 			default:
1230 				/* major network problems */
1231 				dprintf("%s: Network transaction failed: %d\n",
1232 				    s_n, err);
1233 				break;
1234 			}
1235 			break;
1236 
1237 		case BOUND:
1238 
1239 			/*
1240 			 * We just "give up" if bound state fails.
1241 			 */
1242 			s_n = "BOUND";
1243 			if ((err = dhcp_bound()) == 0) {
1244 				dhcp_state = CONFIGURED;
1245 			}
1246 			break;
1247 
1248 		case CONFIGURED:
1249 			s_n = "CONFIGURED";
1250 			create_bootpresponse_bprop(state_pl);
1251 			done = B_TRUE;
1252 			break;
1253 		}
1254 	}
1255 	bkmem_free((char *)dhcp_snd_bufp, dhcp_buf_size);
1256 	bkmem_free((char *)dhcp_rcv_bufp, dhcp_buf_size);
1257 
1258 	return (err);
1259 }
1260 
1261 /*
1262  * Returns a copy of the DHCP-supplied value of the parameter requested
1263  * by code.
1264  */
1265 boolean_t
1266 dhcp_getinfo(uchar_t optcat, uint16_t code, uint16_t optsize, void *value,
1267     size_t *vallenp)
1268 {
1269 	size_t		len = *vallenp;
1270 
1271 	if (dhcp_getinfo_pl(state_pl, optcat, code,
1272 				optsize, value, &len)) {
1273 
1274 		if (len <= *vallenp) {
1275 			*vallenp = len;
1276 			return (B_TRUE);
1277 		}
1278 	}
1279 
1280 	return (B_FALSE);
1281 }
1282 
1283 /*
1284  * Sets the clientid option.
1285  */
1286 void
1287 dhcp_set_client_id(uint8_t *clientid, uint8_t clientid_len)
1288 {
1289 	bcopy(clientid, dhcp_clientid, clientid_len);
1290 	dhcp_clientid_len = clientid_len;
1291 }
1292