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