xref: /illumos-gate/usr/src/stand/lib/inet/dhcpv4.c (revision bcbe9155)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
23*bcbe9155Sjg  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  *
267c478bd9Sstevel@tonic-gate  * Standalone dhcp client.
277c478bd9Sstevel@tonic-gate  */
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #include <sys/types.h>
327c478bd9Sstevel@tonic-gate #include <sys/salib.h>
337c478bd9Sstevel@tonic-gate #include <sys/bootconf.h>
347c478bd9Sstevel@tonic-gate #include <sys/bootcmn.h>
357c478bd9Sstevel@tonic-gate #include <sys/socket.h>
367c478bd9Sstevel@tonic-gate #include <sys/isa_defs.h>
377c478bd9Sstevel@tonic-gate #include <netinet/in.h>
387c478bd9Sstevel@tonic-gate #include <netinet/in_systm.h>
397c478bd9Sstevel@tonic-gate #include <netinet/inetutil.h>
407c478bd9Sstevel@tonic-gate #include <netinet/dhcp.h>
417c478bd9Sstevel@tonic-gate #include <netinet/ip.h>
427c478bd9Sstevel@tonic-gate #include <netinet/udp.h>
437c478bd9Sstevel@tonic-gate #include <dhcp_impl.h>
447c478bd9Sstevel@tonic-gate #include <net/if_types.h>
457c478bd9Sstevel@tonic-gate #include <sys/promif.h>
467c478bd9Sstevel@tonic-gate #include <sys/platnames.h>
477c478bd9Sstevel@tonic-gate #include <socket_inet.h>
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate #include "ipv4.h"
507c478bd9Sstevel@tonic-gate #include "mac.h"
517c478bd9Sstevel@tonic-gate #include <sys/bootdebug.h>
527c478bd9Sstevel@tonic-gate #include "dhcpv4.h"
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate static char		*s_n = "INIT";
557c478bd9Sstevel@tonic-gate static enum DHCPSTATE 	dhcp_state = INIT;
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate static PKT		*dhcp_snd_bufp, *dhcp_rcv_bufp;
587c478bd9Sstevel@tonic-gate static int		dhcp_buf_size;
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate static const uint8_t	magic[] = BOOTMAGIC;	/* RFC1048 */
617c478bd9Sstevel@tonic-gate static uint8_t		opt_discover[]  = { CD_DHCP_TYPE, 1, DISCOVER };
627c478bd9Sstevel@tonic-gate static uint8_t		opt_request[]   = { CD_DHCP_TYPE, 1, REQUEST };
637c478bd9Sstevel@tonic-gate static uint8_t		opt_decline[]   = { CD_DHCP_TYPE, 1, DECLINE };
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate static uint8_t		dhcp_classid[DHCP_MAX_OPT_SIZE + 3];
667c478bd9Sstevel@tonic-gate static uint8_t		dhcp_clientid[DHCP_MAX_CID_LEN];
677c478bd9Sstevel@tonic-gate static uint8_t		dhcp_clientid_len = 0;
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate static uint32_t		dhcp_start_time;	/* start time (msecs */
707c478bd9Sstevel@tonic-gate static time_t		dhcp_secs;
717c478bd9Sstevel@tonic-gate static uint32_t		timeout;	/* timeout in milliseconds */
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate static int		pkt_counter;
747c478bd9Sstevel@tonic-gate PKT_LIST		*list_tl, *list_hd;
757c478bd9Sstevel@tonic-gate PKT_LIST		*state_pl = NULL;
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate #define	PROM_BOOT_CACHED	"bootp-response"
787c478bd9Sstevel@tonic-gate #ifndef	__i386
797c478bd9Sstevel@tonic-gate extern char	*bootp_response;	/* bootprop.c */
807c478bd9Sstevel@tonic-gate #else
817c478bd9Sstevel@tonic-gate char		*bootp_response;	/* i386 has *real* bsetprop */
827c478bd9Sstevel@tonic-gate #endif	/* __i386 */
837c478bd9Sstevel@tonic-gate extern int	pagesize;
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate /*
867c478bd9Sstevel@tonic-gate  * Do whatever reset actions/initialization actions are generic for every
877c478bd9Sstevel@tonic-gate  * DHCP/bootp message. Set the message type.
887c478bd9Sstevel@tonic-gate  *
897c478bd9Sstevel@tonic-gate  * Returns: the updated options ptr.
907c478bd9Sstevel@tonic-gate  */
917c478bd9Sstevel@tonic-gate static uint8_t *
927c478bd9Sstevel@tonic-gate init_msg(PKT *pkt, uint8_t *pkttype)
937c478bd9Sstevel@tonic-gate {
947c478bd9Sstevel@tonic-gate 	static uint32_t xid;
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate 	bzero(pkt, dhcp_buf_size);
977c478bd9Sstevel@tonic-gate 	bcopy(magic, pkt->cookie, sizeof (pkt->cookie));
987c478bd9Sstevel@tonic-gate 	pkt->op = BOOTREQUEST;
997c478bd9Sstevel@tonic-gate 	if (xid == 0)
1007c478bd9Sstevel@tonic-gate 		bcopy(mac_get_addr_buf()+2, &xid, 4);
1017c478bd9Sstevel@tonic-gate 	else
1027c478bd9Sstevel@tonic-gate 		xid++;
1037c478bd9Sstevel@tonic-gate 	pkt->xid = xid;
1047c478bd9Sstevel@tonic-gate 	bcopy(pkttype, pkt->options, 3);
1057c478bd9Sstevel@tonic-gate 	return ((uint8_t *)(pkt->options + 3));
1067c478bd9Sstevel@tonic-gate }
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate /*
1097c478bd9Sstevel@tonic-gate  *  Parameter request list.
1107c478bd9Sstevel@tonic-gate  */
1117c478bd9Sstevel@tonic-gate static void
1127c478bd9Sstevel@tonic-gate parameter_request_list(uint8_t **opt)
1137c478bd9Sstevel@tonic-gate {
1147c478bd9Sstevel@tonic-gate 	/*
1157c478bd9Sstevel@tonic-gate 	 * This parameter request list is used in the normal 4-packet
1167c478bd9Sstevel@tonic-gate 	 * DHCPDISCOVER/OFFER/REQUEST/ACK exchange; it must not contain
1177c478bd9Sstevel@tonic-gate 	 * CD_REQUESTED_IP_ADDR or CD_LEASE_TIME.
1187c478bd9Sstevel@tonic-gate 	 */
1197c478bd9Sstevel@tonic-gate 	static uint8_t	prlist[] = {
1207c478bd9Sstevel@tonic-gate 	    CD_REQUEST_LIST,	/* parameter request list option number */
1217c478bd9Sstevel@tonic-gate 	    4,			/* number of options requested */
1227c478bd9Sstevel@tonic-gate 	    CD_SUBNETMASK,
1237c478bd9Sstevel@tonic-gate 	    CD_ROUTER,
1247c478bd9Sstevel@tonic-gate 	    CD_HOSTNAME,
1257c478bd9Sstevel@tonic-gate 	    CD_VENDOR_SPEC
1267c478bd9Sstevel@tonic-gate 	    };
1277c478bd9Sstevel@tonic-gate 	if (opt && *opt) {
1287c478bd9Sstevel@tonic-gate 		bcopy(prlist, *opt, sizeof (prlist));
1297c478bd9Sstevel@tonic-gate 		*opt += sizeof (prlist);
1307c478bd9Sstevel@tonic-gate 	}
1317c478bd9Sstevel@tonic-gate }
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate /*
1347c478bd9Sstevel@tonic-gate  * Set hardware specific fields
1357c478bd9Sstevel@tonic-gate  */
1367c478bd9Sstevel@tonic-gate static void
1377c478bd9Sstevel@tonic-gate set_hw_spec_data(PKT *p, uint8_t **opt, uint8_t *pkttype)
1387c478bd9Sstevel@tonic-gate {
1397c478bd9Sstevel@tonic-gate 	char mfg[DHCP_MAX_OPT_SIZE + 1], cbuf[DHCP_MAX_OPT_SIZE + 1];
1407c478bd9Sstevel@tonic-gate 	uint8_t *tp, *dp;
1417c478bd9Sstevel@tonic-gate 	int adjust_len, len, i;
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate 	p->htype = mac_arp_type(mac_get_type());
1447c478bd9Sstevel@tonic-gate 	len = (uchar_t)mac_get_addr_len();
1457c478bd9Sstevel@tonic-gate 	if (len <= sizeof (p->chaddr)) {
1467c478bd9Sstevel@tonic-gate 		p->hlen = len;
1477c478bd9Sstevel@tonic-gate 		bcopy(mac_get_addr_buf(), p->chaddr, len);
1487c478bd9Sstevel@tonic-gate 	} else {
1497c478bd9Sstevel@tonic-gate 		uint8_t type = *(pkttype + 2);
1507c478bd9Sstevel@tonic-gate 		/*
1517c478bd9Sstevel@tonic-gate 		 * The mac address does not fit in the chaddr
1527c478bd9Sstevel@tonic-gate 		 * field, thus it can not be sent to the server,
1537c478bd9Sstevel@tonic-gate 		 * thus server can not unicast the reply. Per
1547c478bd9Sstevel@tonic-gate 		 * RFC 2131 4.4.1, client can set this bit in
1557c478bd9Sstevel@tonic-gate 		 * DISCOVER/REQUEST.
1567c478bd9Sstevel@tonic-gate 		 */
1577c478bd9Sstevel@tonic-gate 		if ((type == DISCOVER) || (type == REQUEST))
1587c478bd9Sstevel@tonic-gate 			p->flags = htons(BCAST_MASK);
1597c478bd9Sstevel@tonic-gate 	}
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 	if (opt && *opt) {
1627c478bd9Sstevel@tonic-gate 		if (dhcp_classid[0] == '\0') {
1637c478bd9Sstevel@tonic-gate 			/*
1647c478bd9Sstevel@tonic-gate 			 * Classids based on mfg name: Commas (,) are
1657c478bd9Sstevel@tonic-gate 			 * converted to periods (.), and spaces ( ) are removed.
1667c478bd9Sstevel@tonic-gate 			 */
1677c478bd9Sstevel@tonic-gate 			dhcp_classid[0] = CD_CLASS_ID;
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate 			(void) strncpy(mfg, get_mfg_name(), sizeof (mfg));
1707c478bd9Sstevel@tonic-gate 			if (strncmp(mfg, "SUNW", strlen("SUNW")) != 0) {
1717c478bd9Sstevel@tonic-gate 				len = strlen("SUNW.");
1727c478bd9Sstevel@tonic-gate 				(void) strcpy(cbuf, "SUNW.");
1737c478bd9Sstevel@tonic-gate 			} else {
1747c478bd9Sstevel@tonic-gate 				len = 0;
1757c478bd9Sstevel@tonic-gate 				cbuf[0] = '\0';
1767c478bd9Sstevel@tonic-gate 			}
1777c478bd9Sstevel@tonic-gate 			len += strlen(mfg);
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate 			if ((len + 2) < DHCP_MAX_OPT_SIZE) {
1807c478bd9Sstevel@tonic-gate 				tp = (uint8_t *)strcat(cbuf, mfg);
1817c478bd9Sstevel@tonic-gate 				dp = &dhcp_classid[2];
1827c478bd9Sstevel@tonic-gate 				adjust_len = 0;
1837c478bd9Sstevel@tonic-gate 				for (i = 0; i < len; i++, tp++) {
1847c478bd9Sstevel@tonic-gate 					if (*tp == ',') {
1857c478bd9Sstevel@tonic-gate 						*dp++ = '.';
1867c478bd9Sstevel@tonic-gate 					} else if (*tp == ' ') {
1877c478bd9Sstevel@tonic-gate 						adjust_len++;
1887c478bd9Sstevel@tonic-gate 					} else {
1897c478bd9Sstevel@tonic-gate 						*dp++ = *tp;
1907c478bd9Sstevel@tonic-gate 					}
1917c478bd9Sstevel@tonic-gate 				}
1927c478bd9Sstevel@tonic-gate 				len -= adjust_len;
1937c478bd9Sstevel@tonic-gate 				dhcp_classid[1] = (uint8_t)len;
1947c478bd9Sstevel@tonic-gate 			} else
1957c478bd9Sstevel@tonic-gate 				prom_panic("Not enough space for class id");
1967c478bd9Sstevel@tonic-gate #ifdef	DHCP_DEBUG
1977c478bd9Sstevel@tonic-gate 			printf("%s: Classid: %s\n", s_n, &dhcp_classid[2]);
1987c478bd9Sstevel@tonic-gate #endif	/* DHCP_DEBUG */
1997c478bd9Sstevel@tonic-gate 		}
2007c478bd9Sstevel@tonic-gate 		bcopy(dhcp_classid, *opt, dhcp_classid[1] + 2);
2017c478bd9Sstevel@tonic-gate 		*opt += dhcp_classid[1] + 2;
2027c478bd9Sstevel@tonic-gate 	}
2037c478bd9Sstevel@tonic-gate }
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate static void
2067c478bd9Sstevel@tonic-gate flush_list(void)
2077c478bd9Sstevel@tonic-gate {
2087c478bd9Sstevel@tonic-gate 	PKT_LIST *wk, *tmp;
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 	wk = list_hd;
2117c478bd9Sstevel@tonic-gate 	while (wk != NULL) {
2127c478bd9Sstevel@tonic-gate 		tmp = wk;
2137c478bd9Sstevel@tonic-gate 		wk = wk->next;
2147c478bd9Sstevel@tonic-gate 		bkmem_free((char *)tmp->pkt, tmp->len);
2157c478bd9Sstevel@tonic-gate 		bkmem_free((char *)tmp, sizeof (PKT_LIST));
2167c478bd9Sstevel@tonic-gate 	}
2177c478bd9Sstevel@tonic-gate 	list_hd = list_tl = NULL;
2187c478bd9Sstevel@tonic-gate 	pkt_counter = 0;
2197c478bd9Sstevel@tonic-gate }
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate static void
2227c478bd9Sstevel@tonic-gate remove_list(PKT_LIST *pl, int flag)
2237c478bd9Sstevel@tonic-gate {
2247c478bd9Sstevel@tonic-gate 	if (list_hd == NULL)
2257c478bd9Sstevel@tonic-gate 		return;
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 	if (list_hd == list_tl) {
2287c478bd9Sstevel@tonic-gate 		list_hd = list_tl = NULL;
2297c478bd9Sstevel@tonic-gate 	} else if (list_hd == pl) {
2307c478bd9Sstevel@tonic-gate 		list_hd = pl->next;
2317c478bd9Sstevel@tonic-gate 		list_hd->prev = NULL;
2327c478bd9Sstevel@tonic-gate 	} else if (list_tl == pl) {
2337c478bd9Sstevel@tonic-gate 		list_tl = list_tl->prev;
2347c478bd9Sstevel@tonic-gate 		list_tl->next = NULL;
2357c478bd9Sstevel@tonic-gate 	} else {
2367c478bd9Sstevel@tonic-gate 		pl->prev->next = pl->next;
2377c478bd9Sstevel@tonic-gate 		pl->next->prev = pl->prev;
2387c478bd9Sstevel@tonic-gate 	}
2397c478bd9Sstevel@tonic-gate 	pkt_counter--;
2407c478bd9Sstevel@tonic-gate 	if (flag) {
2417c478bd9Sstevel@tonic-gate 		bkmem_free((char *)pl->pkt, pl->len);
2427c478bd9Sstevel@tonic-gate 		bkmem_free((char *)pl, sizeof (PKT_LIST));
2437c478bd9Sstevel@tonic-gate 	}
2447c478bd9Sstevel@tonic-gate }
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate /*
2477c478bd9Sstevel@tonic-gate  * Collects BOOTP responses. Length has to be right, it has to be
2487c478bd9Sstevel@tonic-gate  * a BOOTP reply pkt, with the same XID and HW address as ours. Adds
2497c478bd9Sstevel@tonic-gate  * them to the pkt list.
2507c478bd9Sstevel@tonic-gate  *
2517c478bd9Sstevel@tonic-gate  * Returns 0 if no error processing packet, 1 if an error occurred and/or
2527c478bd9Sstevel@tonic-gate  * collection of replies should stop. Used in inet() calls.
2537c478bd9Sstevel@tonic-gate  */
2547c478bd9Sstevel@tonic-gate static int
2557c478bd9Sstevel@tonic-gate bootp_collect(int len)
2567c478bd9Sstevel@tonic-gate {
2577c478bd9Sstevel@tonic-gate 	PKT		*s = (PKT *)dhcp_snd_bufp;
2587c478bd9Sstevel@tonic-gate 	PKT		*r = (PKT *)dhcp_rcv_bufp;
2597c478bd9Sstevel@tonic-gate 	PKT_LIST	*pl;
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 	if (len < sizeof (PKT)) {
2627c478bd9Sstevel@tonic-gate 		dprintf("%s: BOOTP reply too small: %d\n", s_n, len);
2637c478bd9Sstevel@tonic-gate 		return (1);
2647c478bd9Sstevel@tonic-gate 	}
2657c478bd9Sstevel@tonic-gate 	if (r->op == BOOTREPLY && r->xid == s->xid &&
2667c478bd9Sstevel@tonic-gate 	    bcmp((caddr_t)s->chaddr, (caddr_t)r->chaddr, s->hlen) == 0) {
2677c478bd9Sstevel@tonic-gate 		/* Add a packet to the pkt list */
2687c478bd9Sstevel@tonic-gate 		if (pkt_counter > (MAX_PKT_LIST - 1))
2697c478bd9Sstevel@tonic-gate 			return (1);	/* got enough packets already */
2707c478bd9Sstevel@tonic-gate 		if (((pl = (PKT_LIST *)bkmem_zalloc(sizeof (PKT_LIST))) ==
2717c478bd9Sstevel@tonic-gate 		    NULL) || ((pl->pkt = (PKT *)bkmem_zalloc(len)) == NULL)) {
2727c478bd9Sstevel@tonic-gate 			errno = ENOMEM;
2737c478bd9Sstevel@tonic-gate 			if (pl != NULL)
2747c478bd9Sstevel@tonic-gate 				bkmem_free((char *)pl, sizeof (PKT_LIST));
2757c478bd9Sstevel@tonic-gate 			return (1);
2767c478bd9Sstevel@tonic-gate 		}
2777c478bd9Sstevel@tonic-gate 		bcopy(dhcp_rcv_bufp, pl->pkt, len);
2787c478bd9Sstevel@tonic-gate 		pl->len = len;
2797c478bd9Sstevel@tonic-gate 		if (list_hd == NULL) {
2807c478bd9Sstevel@tonic-gate 			list_hd = list_tl = pl;
2817c478bd9Sstevel@tonic-gate 			pl->prev = NULL;
2827c478bd9Sstevel@tonic-gate 		} else {
2837c478bd9Sstevel@tonic-gate 			list_tl->next = pl;
2847c478bd9Sstevel@tonic-gate 			pl->prev = list_tl;
2857c478bd9Sstevel@tonic-gate 			list_tl = pl;
2867c478bd9Sstevel@tonic-gate 		}
2877c478bd9Sstevel@tonic-gate 		pkt_counter++;
2887c478bd9Sstevel@tonic-gate 		pl->next = NULL;
2897c478bd9Sstevel@tonic-gate 	}
2907c478bd9Sstevel@tonic-gate 	return (0);
2917c478bd9Sstevel@tonic-gate }
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate /*
2947c478bd9Sstevel@tonic-gate  * Checks if BOOTP exchange(s) were successful. Returns 1 if they
2957c478bd9Sstevel@tonic-gate  * were, 0 otherwise. Used in inet() calls.
2967c478bd9Sstevel@tonic-gate  */
2977c478bd9Sstevel@tonic-gate static int
2987c478bd9Sstevel@tonic-gate bootp_success(void)
2997c478bd9Sstevel@tonic-gate {
3007c478bd9Sstevel@tonic-gate 	PKT	*s = (PKT *)dhcp_snd_bufp;
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 	if (list_hd != NULL) {
3037c478bd9Sstevel@tonic-gate 		/* remember the secs - we may need them later */
3047c478bd9Sstevel@tonic-gate 		dhcp_secs = ntohs(s->secs);
3057c478bd9Sstevel@tonic-gate 		return (1);
3067c478bd9Sstevel@tonic-gate 	}
3077c478bd9Sstevel@tonic-gate 	s->secs = htons((uint16_t)((prom_gettime() - dhcp_start_time)/1000));
3087c478bd9Sstevel@tonic-gate 	return (0);
3097c478bd9Sstevel@tonic-gate }
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate /*
3127c478bd9Sstevel@tonic-gate  * This function accesses the network. Opens a connection, and binds to
3137c478bd9Sstevel@tonic-gate  * it if a client binding doesn't already exist. If 'tries' is 0, then
3147c478bd9Sstevel@tonic-gate  * no reply is expected/returned. If 'tries' is non-zero, then 'tries'
3157c478bd9Sstevel@tonic-gate  * attempts are made to get a valid response. If 'tol' is not zero,
3167c478bd9Sstevel@tonic-gate  * then this function will wait for 'tol' milliseconds for more than one
3177c478bd9Sstevel@tonic-gate  * response to a transmit.
3187c478bd9Sstevel@tonic-gate  *
3197c478bd9Sstevel@tonic-gate  * Returns 0 for success, errno otherwise.
3207c478bd9Sstevel@tonic-gate  */
3217c478bd9Sstevel@tonic-gate static int
3227c478bd9Sstevel@tonic-gate inet(uint32_t size, struct in_addr *src, struct in_addr *dest, uint32_t tries,
3237c478bd9Sstevel@tonic-gate     uint32_t tol)
3247c478bd9Sstevel@tonic-gate {
3257c478bd9Sstevel@tonic-gate 	int			done = B_FALSE, flags, len;
3267c478bd9Sstevel@tonic-gate 	uint32_t		attempts = 0;
3277c478bd9Sstevel@tonic-gate 	int			sd;
3287c478bd9Sstevel@tonic-gate 	uint32_t		wait_time;	/* Max time collect replies */
3297c478bd9Sstevel@tonic-gate 	uint32_t		init_timeout;	/* Max time wait ANY reply */
3307c478bd9Sstevel@tonic-gate 	uint32_t		now;
3317c478bd9Sstevel@tonic-gate 	struct sockaddr_in	saddr, daddr;
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate 	if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
3347c478bd9Sstevel@tonic-gate 		dprintf("%s: Can't open a socket.\n", s_n);
3357c478bd9Sstevel@tonic-gate 		return (errno);
3367c478bd9Sstevel@tonic-gate 	}
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 	flags = 0;
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate 	bzero(&saddr, sizeof (struct sockaddr_in));
3417c478bd9Sstevel@tonic-gate 	saddr.sin_family = AF_INET;
3427c478bd9Sstevel@tonic-gate 	saddr.sin_port = htons(IPPORT_BOOTPC);
3437c478bd9Sstevel@tonic-gate 	saddr.sin_addr.s_addr = htonl(src->s_addr);
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 	if (bind(sd, (struct sockaddr *)&saddr, sizeof (saddr)) < 0) {
3467c478bd9Sstevel@tonic-gate 		dprintf("%s: Cannot bind to port %d, errno: %d\n",
3477c478bd9Sstevel@tonic-gate 		    s_n, IPPORT_BOOTPC, errno);
3487c478bd9Sstevel@tonic-gate 		(void) socket_close(sd);
3497c478bd9Sstevel@tonic-gate 		return (errno);
3507c478bd9Sstevel@tonic-gate 	}
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate 	if (ntohl(dest->s_addr) == INADDR_BROADCAST) {
3537c478bd9Sstevel@tonic-gate 		int dontroute = B_TRUE;
3547c478bd9Sstevel@tonic-gate 		(void) setsockopt(sd, SOL_SOCKET, SO_DONTROUTE,
3557c478bd9Sstevel@tonic-gate 		    (const void *)&dontroute, sizeof (dontroute));
3567c478bd9Sstevel@tonic-gate 	}
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	bzero(&daddr, sizeof (struct sockaddr_in));
3597c478bd9Sstevel@tonic-gate 	daddr.sin_family = AF_INET;
3607c478bd9Sstevel@tonic-gate 	daddr.sin_port = htons(IPPORT_BOOTPS);
3617c478bd9Sstevel@tonic-gate 	daddr.sin_addr.s_addr = htonl(dest->s_addr);
3627c478bd9Sstevel@tonic-gate 	wait_time = prom_gettime() + tol;
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	do {
3657c478bd9Sstevel@tonic-gate 		if (sendto(sd, (char *)dhcp_snd_bufp, size, flags,
3667c478bd9Sstevel@tonic-gate 		    (struct sockaddr *)&daddr, sizeof (daddr)) < 0) {
3677c478bd9Sstevel@tonic-gate 			dprintf("%s: sendto failed with errno: %d\n",
3687c478bd9Sstevel@tonic-gate 			    s_n, errno);
3697c478bd9Sstevel@tonic-gate 			(void) socket_close(sd);
3707c478bd9Sstevel@tonic-gate 			return (errno);
3717c478bd9Sstevel@tonic-gate 		}
3727c478bd9Sstevel@tonic-gate 		if (!tries)
3737c478bd9Sstevel@tonic-gate 			break;   /* don't bother to check for reply */
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate 		now = prom_gettime();
3767c478bd9Sstevel@tonic-gate 		if (timeout == 0)
3777c478bd9Sstevel@tonic-gate 			timeout = 4000;
3787c478bd9Sstevel@tonic-gate 		else {
3797c478bd9Sstevel@tonic-gate 			timeout <<= 1;
3807c478bd9Sstevel@tonic-gate 			if (timeout > 64000)
3817c478bd9Sstevel@tonic-gate 				timeout = 64000;
3827c478bd9Sstevel@tonic-gate 		}
3837c478bd9Sstevel@tonic-gate 		init_timeout = now + timeout;
3847c478bd9Sstevel@tonic-gate 		wait_time = now + tol;
3857c478bd9Sstevel@tonic-gate 		do {
3867c478bd9Sstevel@tonic-gate 			if ((len = recvfrom(sd, (char *)dhcp_rcv_bufp,
3877c478bd9Sstevel@tonic-gate 			    (int)dhcp_buf_size, MSG_DONTWAIT, NULL,
3887c478bd9Sstevel@tonic-gate 			    NULL)) < 0) {
3897c478bd9Sstevel@tonic-gate 				if (errno == EWOULDBLOCK)
3907c478bd9Sstevel@tonic-gate 					continue;	/* DONT WAIT */
3917c478bd9Sstevel@tonic-gate 				(void) socket_close(sd);
3927c478bd9Sstevel@tonic-gate 				flush_list();
3937c478bd9Sstevel@tonic-gate 				return (errno);
3947c478bd9Sstevel@tonic-gate 			}
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate 			if (bootp_collect(len))
3977c478bd9Sstevel@tonic-gate 				break;	/* Stop collecting */
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 			if (tol != 0) {
4007c478bd9Sstevel@tonic-gate 				if (wait_time < prom_gettime())
4017c478bd9Sstevel@tonic-gate 					break; /* collection timeout */
4027c478bd9Sstevel@tonic-gate 			}
4037c478bd9Sstevel@tonic-gate 		} while (prom_gettime() < init_timeout);
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 		if (bootp_success()) {
4067c478bd9Sstevel@tonic-gate 			done = B_TRUE;
4077c478bd9Sstevel@tonic-gate 			break;  /* got the goods */
4087c478bd9Sstevel@tonic-gate 		}
4097c478bd9Sstevel@tonic-gate 	} while (++attempts < tries);
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 	(void) socket_close(sd);
4127c478bd9Sstevel@tonic-gate 
4137c478bd9Sstevel@tonic-gate 	return (done ? 0 : DHCP_NO_DATA);
4147c478bd9Sstevel@tonic-gate }
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate /*
4177c478bd9Sstevel@tonic-gate  * Print the message from the server.
4187c478bd9Sstevel@tonic-gate  */
4197c478bd9Sstevel@tonic-gate static void
4207c478bd9Sstevel@tonic-gate prt_server_msg(DHCP_OPT *p)
4217c478bd9Sstevel@tonic-gate {
4227c478bd9Sstevel@tonic-gate 	int len = p->len;
4237c478bd9Sstevel@tonic-gate 	char scratch[DHCP_MAX_OPT_SIZE + 1];
4247c478bd9Sstevel@tonic-gate 
425*bcbe9155Sjg 	if (len > DHCP_MAX_OPT_SIZE)
4267c478bd9Sstevel@tonic-gate 		len = DHCP_MAX_OPT_SIZE;
4277c478bd9Sstevel@tonic-gate 	bcopy(p->value, scratch, len);
4287c478bd9Sstevel@tonic-gate 	scratch[len] = '\0';
4297c478bd9Sstevel@tonic-gate 	printf("%s: Message from server: '%s'\n", s_n, scratch);
4307c478bd9Sstevel@tonic-gate }
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate /*
4337c478bd9Sstevel@tonic-gate  * This function scans the list of OFFERS, and returns the "best" offer.
4347c478bd9Sstevel@tonic-gate  * The criteria used for determining this is:
4357c478bd9Sstevel@tonic-gate  *
4367c478bd9Sstevel@tonic-gate  * The best:
4377c478bd9Sstevel@tonic-gate  * DHCP OFFER (not BOOTP), same client_id as ours, same class_id,
4387c478bd9Sstevel@tonic-gate  * Longest lease, all the options we need.
4397c478bd9Sstevel@tonic-gate  *
4407c478bd9Sstevel@tonic-gate  * Not quite as good:
4417c478bd9Sstevel@tonic-gate  * DHCP OFFER, no class_id, short lease, only some of the options we need.
4427c478bd9Sstevel@tonic-gate  *
4437c478bd9Sstevel@tonic-gate  * We're really reach'in
4447c478bd9Sstevel@tonic-gate  * BOOTP reply.
4457c478bd9Sstevel@tonic-gate  *
4467c478bd9Sstevel@tonic-gate  * DON'T select an offer from a server that gave us a configuration we
4477c478bd9Sstevel@tonic-gate  * couldn't use. Take this server off the "bad" list when this is done.
4487c478bd9Sstevel@tonic-gate  * Next time, we could potentially retry this server's configuration.
4497c478bd9Sstevel@tonic-gate  *
4507c478bd9Sstevel@tonic-gate  * NOTE: perhaps this bad server should have a counter associated with it.
4517c478bd9Sstevel@tonic-gate  */
4527c478bd9Sstevel@tonic-gate static PKT_LIST *
4537c478bd9Sstevel@tonic-gate select_best(void)
4547c478bd9Sstevel@tonic-gate {
4557c478bd9Sstevel@tonic-gate 	PKT_LIST	*wk, *tk, *best;
4567c478bd9Sstevel@tonic-gate 	int		err = 0;
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 	/* Pass one. Scan for options, set appropriate opt field. */
4597c478bd9Sstevel@tonic-gate 	wk = list_hd;
4607c478bd9Sstevel@tonic-gate 	while (wk != NULL) {
4617c478bd9Sstevel@tonic-gate 		if ((err = dhcp_options_scan(wk, B_TRUE)) != 0) {
4627c478bd9Sstevel@tonic-gate 			/* Garbled Options. Nuke this pkt. */
4637c478bd9Sstevel@tonic-gate 			if (boothowto & RB_DEBUG) {
4647c478bd9Sstevel@tonic-gate 				switch (err) {
4657c478bd9Sstevel@tonic-gate 				case DHCP_WRONG_MSG_TYPE:
4667c478bd9Sstevel@tonic-gate 					printf("%s: Unexpected DHCP message.\n",
4677c478bd9Sstevel@tonic-gate 					    s_n);
4687c478bd9Sstevel@tonic-gate 					break;
4697c478bd9Sstevel@tonic-gate 				case DHCP_GARBLED_MSG_TYPE:
4707c478bd9Sstevel@tonic-gate 					printf(
4717c478bd9Sstevel@tonic-gate 					    "%s: Garbled DHCP message type.\n",
4727c478bd9Sstevel@tonic-gate 					    s_n);
4737c478bd9Sstevel@tonic-gate 					break;
4747c478bd9Sstevel@tonic-gate 				case DHCP_BAD_OPT_OVLD:
4757c478bd9Sstevel@tonic-gate 					printf("%s: Bad option overload.\n",
4767c478bd9Sstevel@tonic-gate 					    s_n);
4777c478bd9Sstevel@tonic-gate 					break;
4787c478bd9Sstevel@tonic-gate 				}
4797c478bd9Sstevel@tonic-gate 			}
4807c478bd9Sstevel@tonic-gate 			tk = wk;
4817c478bd9Sstevel@tonic-gate 			wk = wk->next;
4827c478bd9Sstevel@tonic-gate 			remove_list(tk, B_TRUE);
4837c478bd9Sstevel@tonic-gate 			continue;
4847c478bd9Sstevel@tonic-gate 		}
4857c478bd9Sstevel@tonic-gate 		wk = wk->next;
4867c478bd9Sstevel@tonic-gate 	}
4877c478bd9Sstevel@tonic-gate 
4887c478bd9Sstevel@tonic-gate 	/*
4897c478bd9Sstevel@tonic-gate 	 * Pass two. Pick out the best offer. Point system.
4907c478bd9Sstevel@tonic-gate 	 * What's important?
4917c478bd9Sstevel@tonic-gate 	 *	0) DHCP
4927c478bd9Sstevel@tonic-gate 	 *	1) No option overload
4937c478bd9Sstevel@tonic-gate 	 *	2) Encapsulated vendor option
4947c478bd9Sstevel@tonic-gate 	 *	3) Non-null sname and siaddr fields
4957c478bd9Sstevel@tonic-gate 	 *	4) Non-null file field
4967c478bd9Sstevel@tonic-gate 	 *	5) Hostname
4977c478bd9Sstevel@tonic-gate 	 *	6) Subnetmask
4987c478bd9Sstevel@tonic-gate 	 *	7) Router
4997c478bd9Sstevel@tonic-gate 	 */
5007c478bd9Sstevel@tonic-gate 	best = NULL;
5017c478bd9Sstevel@tonic-gate 	for (wk = list_hd; wk != NULL; wk = wk->next) {
5027c478bd9Sstevel@tonic-gate 		wk->offset = 0;
5037c478bd9Sstevel@tonic-gate 		if (wk->opts[CD_DHCP_TYPE] &&
5047c478bd9Sstevel@tonic-gate 		    wk->opts[CD_DHCP_TYPE]->len == 1) {
5057c478bd9Sstevel@tonic-gate 			if (*wk->opts[CD_DHCP_TYPE]->value != OFFER) {
5067c478bd9Sstevel@tonic-gate 				dprintf("%s: Unexpected DHCP message."
5077c478bd9Sstevel@tonic-gate 				    " Expected OFFER message.\n", s_n);
5087c478bd9Sstevel@tonic-gate 				continue;
5097c478bd9Sstevel@tonic-gate 			}
5107c478bd9Sstevel@tonic-gate 			if (!wk->opts[CD_LEASE_TIME]) {
5117c478bd9Sstevel@tonic-gate 				dprintf("%s: DHCP OFFER message without lease "
5127c478bd9Sstevel@tonic-gate 				    "time parameter.\n", s_n);
5137c478bd9Sstevel@tonic-gate 				continue;
5147c478bd9Sstevel@tonic-gate 			} else {
5157c478bd9Sstevel@tonic-gate 				if (wk->opts[CD_LEASE_TIME]->len != 4) {
5167c478bd9Sstevel@tonic-gate 					dprintf("%s: Lease expiration time is "
5177c478bd9Sstevel@tonic-gate 					    "garbled.\n", s_n);
5187c478bd9Sstevel@tonic-gate 					continue;
5197c478bd9Sstevel@tonic-gate 				}
5207c478bd9Sstevel@tonic-gate 			}
5217c478bd9Sstevel@tonic-gate 			if (!wk->opts[CD_SERVER_ID]) {
5227c478bd9Sstevel@tonic-gate 				dprintf("%s: DHCP OFFER message without server "
5237c478bd9Sstevel@tonic-gate 				    "id parameter.\n", s_n);
5247c478bd9Sstevel@tonic-gate 				continue;
5257c478bd9Sstevel@tonic-gate 			} else {
5267c478bd9Sstevel@tonic-gate 				if (wk->opts[CD_SERVER_ID]->len != 4) {
5277c478bd9Sstevel@tonic-gate 					dprintf("%s: Server identifier "
5287c478bd9Sstevel@tonic-gate 					    "parameter is garbled.\n", s_n);
5297c478bd9Sstevel@tonic-gate 					continue;
5307c478bd9Sstevel@tonic-gate 				}
5317c478bd9Sstevel@tonic-gate 			}
5327c478bd9Sstevel@tonic-gate 			/* Valid DHCP OFFER. See if we got our parameters. */
5337c478bd9Sstevel@tonic-gate 			dprintf("%s: Found valid DHCP OFFER message.\n", s_n);
5347c478bd9Sstevel@tonic-gate 
5357c478bd9Sstevel@tonic-gate 			wk->offset += 30;
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 			/*
5387c478bd9Sstevel@tonic-gate 			 * Also could be faked, though more difficult
5397c478bd9Sstevel@tonic-gate 			 * because the encapsulation is hard to encode
5407c478bd9Sstevel@tonic-gate 			 * on a BOOTP server; plus there's not as much
5417c478bd9Sstevel@tonic-gate 			 * real estate in the packet for options, so
5427c478bd9Sstevel@tonic-gate 			 * it's likely this option would get dropped.
5437c478bd9Sstevel@tonic-gate 			 */
5447c478bd9Sstevel@tonic-gate 			if (wk->opts[CD_VENDOR_SPEC])
5457c478bd9Sstevel@tonic-gate 				wk->offset += 80;
5467c478bd9Sstevel@tonic-gate 		} else
5477c478bd9Sstevel@tonic-gate 			dprintf("%s: Found valid BOOTP reply.\n", s_n);
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 		/*
5507c478bd9Sstevel@tonic-gate 		 * RFC1048 BOOTP?
5517c478bd9Sstevel@tonic-gate 		 */
5527c478bd9Sstevel@tonic-gate 		if (bcmp((caddr_t)wk->pkt->cookie, (caddr_t)magic,
5537c478bd9Sstevel@tonic-gate 		    sizeof (magic)) == 0) {
5547c478bd9Sstevel@tonic-gate 			wk->offset += 5;
5557c478bd9Sstevel@tonic-gate 			if (wk->opts[CD_SUBNETMASK])
5567c478bd9Sstevel@tonic-gate 				wk->offset++;
5577c478bd9Sstevel@tonic-gate 			if (wk->opts[CD_ROUTER])
5587c478bd9Sstevel@tonic-gate 				wk->offset++;
5597c478bd9Sstevel@tonic-gate 			if (wk->opts[CD_HOSTNAME])
5607c478bd9Sstevel@tonic-gate 				wk->offset += 5;
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate 			/*
5637c478bd9Sstevel@tonic-gate 			 * Prefer options that have diskless boot significance
5647c478bd9Sstevel@tonic-gate 			 */
5657c478bd9Sstevel@tonic-gate 			if (ntohl(wk->pkt->siaddr.s_addr) != INADDR_ANY)
5667c478bd9Sstevel@tonic-gate 				wk->offset += 10; /* server ip */
5677c478bd9Sstevel@tonic-gate 			if (wk->opts[CD_OPTION_OVERLOAD] == NULL) {
5687c478bd9Sstevel@tonic-gate 				if (wk->pkt->sname[0] != '\0')
5697c478bd9Sstevel@tonic-gate 					wk->offset += 10; /* server name */
5707c478bd9Sstevel@tonic-gate 				if (wk->pkt->file[0] != '\0')
5717c478bd9Sstevel@tonic-gate 					wk->offset += 5; /* File to load */
5727c478bd9Sstevel@tonic-gate 			}
5737c478bd9Sstevel@tonic-gate 		}
5747c478bd9Sstevel@tonic-gate #ifdef	DHCP_DEBUG
5757c478bd9Sstevel@tonic-gate 		printf("%s: This server configuration has '%d' points.\n", s_n,
5767c478bd9Sstevel@tonic-gate 			wk->offset);
5777c478bd9Sstevel@tonic-gate #endif	/* DHCP_DEBUG */
5787c478bd9Sstevel@tonic-gate 		if (!best)
5797c478bd9Sstevel@tonic-gate 			best = wk;
5807c478bd9Sstevel@tonic-gate 		else {
5817c478bd9Sstevel@tonic-gate 			if (best->offset < wk->offset)
5827c478bd9Sstevel@tonic-gate 				best = wk;
5837c478bd9Sstevel@tonic-gate 		}
5847c478bd9Sstevel@tonic-gate 	}
5857c478bd9Sstevel@tonic-gate 	if (best) {
5867c478bd9Sstevel@tonic-gate #ifdef	DHCP_DEBUG
5877c478bd9Sstevel@tonic-gate 		printf("%s: Found best: points: %d\n", s_n, best->offset);
5887c478bd9Sstevel@tonic-gate #endif	/* DHCP_DEBUG */
5897c478bd9Sstevel@tonic-gate 		remove_list(best, B_FALSE);
5907c478bd9Sstevel@tonic-gate 	} else {
5917c478bd9Sstevel@tonic-gate 		dprintf("%s: No valid BOOTP reply or DHCP OFFER was found.\n",
5927c478bd9Sstevel@tonic-gate 		    s_n);
5937c478bd9Sstevel@tonic-gate 	}
5947c478bd9Sstevel@tonic-gate 	flush_list();	/* toss the remaining list */
5957c478bd9Sstevel@tonic-gate 	return (best);
5967c478bd9Sstevel@tonic-gate }
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate /*
5997c478bd9Sstevel@tonic-gate  * Send a decline message to the generator of the DHCPACK.
6007c478bd9Sstevel@tonic-gate  */
6017c478bd9Sstevel@tonic-gate static void
6027c478bd9Sstevel@tonic-gate dhcp_decline(char *msg, PKT_LIST *pl)
6037c478bd9Sstevel@tonic-gate {
6047c478bd9Sstevel@tonic-gate 	PKT		*pkt;
6057c478bd9Sstevel@tonic-gate 	uint8_t		*opt, ulen;
6067c478bd9Sstevel@tonic-gate 	int		pkt_size;
6077c478bd9Sstevel@tonic-gate 	struct in_addr	nets, ours, t_server, t_yiaddr;
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 	if (pl != NULL) {
6107c478bd9Sstevel@tonic-gate 		if (pl->opts[CD_SERVER_ID] != NULL &&
6117c478bd9Sstevel@tonic-gate 		    pl->opts[CD_SERVER_ID]->len == sizeof (struct in_addr)) {
6127c478bd9Sstevel@tonic-gate 			bcopy(pl->opts[CD_SERVER_ID]->value,
6137c478bd9Sstevel@tonic-gate 			    &t_server, pl->opts[CD_SERVER_ID]->len);
6147c478bd9Sstevel@tonic-gate 		} else
6157c478bd9Sstevel@tonic-gate 			t_server.s_addr = htonl(INADDR_BROADCAST);
6167c478bd9Sstevel@tonic-gate 		t_yiaddr.s_addr = pl->pkt->yiaddr.s_addr;
6177c478bd9Sstevel@tonic-gate 	}
6187c478bd9Sstevel@tonic-gate 	pkt = (PKT *)dhcp_snd_bufp;
6197c478bd9Sstevel@tonic-gate 	opt = init_msg(pkt, opt_decline);
6207c478bd9Sstevel@tonic-gate 	set_hw_spec_data(pkt, &opt, opt_decline);
6217c478bd9Sstevel@tonic-gate 
6227c478bd9Sstevel@tonic-gate 	*opt++ = CD_SERVER_ID;
6237c478bd9Sstevel@tonic-gate 	*opt++ = sizeof (struct in_addr);
6247c478bd9Sstevel@tonic-gate 	bcopy(&t_server, opt, sizeof (struct in_addr));
6257c478bd9Sstevel@tonic-gate 	opt += sizeof (struct in_addr);
6267c478bd9Sstevel@tonic-gate 	nets.s_addr = htonl(INADDR_BROADCAST);
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate 	/*
6297c478bd9Sstevel@tonic-gate 	 * Use the given yiaddr in our ciaddr field so server can identify us.
6307c478bd9Sstevel@tonic-gate 	 */
6317c478bd9Sstevel@tonic-gate 	pkt->ciaddr.s_addr = t_yiaddr.s_addr;
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 	ipv4_getipaddr(&ours);	/* Could be 0, could be yiaddr */
6347c478bd9Sstevel@tonic-gate 	ours.s_addr = htonl(ours.s_addr);
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate 	ulen = (uint8_t)strlen(msg);
6377c478bd9Sstevel@tonic-gate 	*opt++ = CD_MESSAGE;
6387c478bd9Sstevel@tonic-gate 	*opt++ = ulen;
6397c478bd9Sstevel@tonic-gate 	bcopy(msg, opt, ulen);
6407c478bd9Sstevel@tonic-gate 	opt += ulen;
6417c478bd9Sstevel@tonic-gate 	*opt++ = CD_END;
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate 	pkt_size = (uint8_t *)opt - (uint8_t *)pkt;
6447c478bd9Sstevel@tonic-gate 	if (pkt_size < sizeof (PKT))
6457c478bd9Sstevel@tonic-gate 		pkt_size = sizeof (PKT);
6467c478bd9Sstevel@tonic-gate 
6477c478bd9Sstevel@tonic-gate 	timeout = 0;	/* reset timeout */
6487c478bd9Sstevel@tonic-gate 	(void) inet(pkt_size, &ours, &nets, 0, 0L);
6497c478bd9Sstevel@tonic-gate }
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate /*
6527c478bd9Sstevel@tonic-gate  * Implementings SELECTING state of DHCP client state machine.
6537c478bd9Sstevel@tonic-gate  */
6547c478bd9Sstevel@tonic-gate static int
6557c478bd9Sstevel@tonic-gate dhcp_selecting(void)
6567c478bd9Sstevel@tonic-gate {
6577c478bd9Sstevel@tonic-gate 	int		pkt_size;
6587c478bd9Sstevel@tonic-gate 	PKT		*pkt;
6597c478bd9Sstevel@tonic-gate 	uint8_t		*opt;
6607c478bd9Sstevel@tonic-gate 	uint16_t	size;
6617c478bd9Sstevel@tonic-gate 	uint32_t	lease;
6627c478bd9Sstevel@tonic-gate 	struct in_addr	nets, ours;
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate 	pkt = (PKT *)dhcp_snd_bufp;
6657c478bd9Sstevel@tonic-gate 	opt = init_msg(pkt, opt_discover);
6667c478bd9Sstevel@tonic-gate 	pkt->secs = htons((uint16_t)((prom_gettime() - dhcp_start_time)/1000));
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 	*opt++ = CD_MAX_DHCP_SIZE;
6697c478bd9Sstevel@tonic-gate 	*opt++ = sizeof (size);
6707c478bd9Sstevel@tonic-gate 	size = (uint16_t)(dhcp_buf_size - sizeof (struct ip) -
6717c478bd9Sstevel@tonic-gate 	    sizeof (struct udphdr));
6727c478bd9Sstevel@tonic-gate 	size = htons(size);
6737c478bd9Sstevel@tonic-gate 	bcopy(&size, opt, sizeof (size));
6747c478bd9Sstevel@tonic-gate 	opt += sizeof (size);
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate 	set_hw_spec_data(pkt, &opt, opt_discover);
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 	*opt++ = CD_LEASE_TIME;
6797c478bd9Sstevel@tonic-gate 	*opt++ = sizeof (lease);
6807c478bd9Sstevel@tonic-gate 	lease = htonl(DHCP_PERM);	/* ask for a permanent lease */
6817c478bd9Sstevel@tonic-gate 	bcopy(&lease, opt, sizeof (lease));
6827c478bd9Sstevel@tonic-gate 	opt += sizeof (lease);
6837c478bd9Sstevel@tonic-gate 
6847c478bd9Sstevel@tonic-gate 	/*
6857c478bd9Sstevel@tonic-gate 	 * If a clientid was set using dhcp_set_client_id(), add this
6867c478bd9Sstevel@tonic-gate 	 * to the options.
6877c478bd9Sstevel@tonic-gate 	 */
6887c478bd9Sstevel@tonic-gate 	if (dhcp_clientid_len > 0) {
6897c478bd9Sstevel@tonic-gate 		*opt++ = CD_CLIENT_ID;
6907c478bd9Sstevel@tonic-gate 		*opt++ = dhcp_clientid_len;
6917c478bd9Sstevel@tonic-gate 		bcopy(dhcp_clientid, opt, dhcp_clientid_len);
6927c478bd9Sstevel@tonic-gate 		opt += dhcp_clientid_len;
6937c478bd9Sstevel@tonic-gate 	}
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate 	parameter_request_list(&opt);
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate 	*opt++ = CD_END;
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate 	pkt_size = (uint8_t *)opt - (uint8_t *)pkt;
7007c478bd9Sstevel@tonic-gate 	if (pkt_size < sizeof (PKT))
7017c478bd9Sstevel@tonic-gate 		pkt_size = sizeof (PKT);
7027c478bd9Sstevel@tonic-gate 
7037c478bd9Sstevel@tonic-gate 	nets.s_addr = INADDR_BROADCAST;
7047c478bd9Sstevel@tonic-gate 	ours.s_addr = INADDR_ANY;
7057c478bd9Sstevel@tonic-gate 	timeout = 0;	/* reset timeout */
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate 	return (inet(pkt_size, &ours, &nets, DHCP_RETRIES, DHCP_WAIT));
7087c478bd9Sstevel@tonic-gate }
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate /*
7117c478bd9Sstevel@tonic-gate  * implements the REQUESTING state of the DHCP client state machine.
7127c478bd9Sstevel@tonic-gate  */
7137c478bd9Sstevel@tonic-gate static int
7147c478bd9Sstevel@tonic-gate dhcp_requesting(void)
7157c478bd9Sstevel@tonic-gate {
7167c478bd9Sstevel@tonic-gate 	PKT_LIST	*pl, *wk;
7177c478bd9Sstevel@tonic-gate 	PKT		*pkt, *pl_pkt;
7187c478bd9Sstevel@tonic-gate 	uint8_t		*opt;
7197c478bd9Sstevel@tonic-gate 	int		pkt_size, err;
7207c478bd9Sstevel@tonic-gate 	uint32_t	t_time;
7217c478bd9Sstevel@tonic-gate 	struct in_addr	nets, ours;
7227c478bd9Sstevel@tonic-gate 	DHCP_OPT	*doptp;
7237c478bd9Sstevel@tonic-gate 	uint16_t	size;
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate 	/* here we scan the list of OFFERS, select the best one. */
7267c478bd9Sstevel@tonic-gate 	state_pl = NULL;
7277c478bd9Sstevel@tonic-gate 
7287c478bd9Sstevel@tonic-gate 	if ((pl = select_best()) == NULL) {
7297c478bd9Sstevel@tonic-gate 		dprintf("%s: No valid BOOTP reply or DHCP OFFER was found.\n",
7307c478bd9Sstevel@tonic-gate 		    s_n);
7317c478bd9Sstevel@tonic-gate 		return (1);
7327c478bd9Sstevel@tonic-gate 	}
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate 	pl_pkt = pl->pkt;
7357c478bd9Sstevel@tonic-gate 
7367c478bd9Sstevel@tonic-gate 	/*
7377c478bd9Sstevel@tonic-gate 	 * Check to see if we got an OFFER pkt(s). If not, then We only got
7387c478bd9Sstevel@tonic-gate 	 * a response from a BOOTP server. We'll go to the bound state and
7397c478bd9Sstevel@tonic-gate 	 * try to use that configuration.
7407c478bd9Sstevel@tonic-gate 	 */
7417c478bd9Sstevel@tonic-gate 	if (pl->opts[CD_DHCP_TYPE] == NULL) {
7427c478bd9Sstevel@tonic-gate 		if (mac_call_arp(&pl_pkt->yiaddr, NULL, DHCP_ARP_TIMEOUT)) {
7437c478bd9Sstevel@tonic-gate 			/* Someone responded! go back to SELECTING state. */
7447c478bd9Sstevel@tonic-gate 			printf("%s: Some host already using BOOTP %s.\n", s_n,
7457c478bd9Sstevel@tonic-gate 			    inet_ntoa(pl_pkt->yiaddr));
7467c478bd9Sstevel@tonic-gate 			bkmem_free((char *)pl->pkt, pl->len);
7477c478bd9Sstevel@tonic-gate 			bkmem_free((char *)pl, sizeof (PKT_LIST));
7487c478bd9Sstevel@tonic-gate 			return (1);
7497c478bd9Sstevel@tonic-gate 		}
7507c478bd9Sstevel@tonic-gate 		state_pl = pl;
7517c478bd9Sstevel@tonic-gate 		return (0);
7527c478bd9Sstevel@tonic-gate 	}
7537c478bd9Sstevel@tonic-gate 
7547c478bd9Sstevel@tonic-gate 	/*
7557c478bd9Sstevel@tonic-gate 	 * if we got a message from the server, display it.
7567c478bd9Sstevel@tonic-gate 	 */
7577c478bd9Sstevel@tonic-gate 	if (pl->opts[CD_MESSAGE])
7587c478bd9Sstevel@tonic-gate 		prt_server_msg(pl->opts[CD_MESSAGE]);
7597c478bd9Sstevel@tonic-gate 	/*
7607c478bd9Sstevel@tonic-gate 	 * Assemble a DHCPREQUEST, with the ciaddr field set to 0, since we
7617c478bd9Sstevel@tonic-gate 	 * got here from DISCOVER state. Keep secs field the same for relay
7627c478bd9Sstevel@tonic-gate 	 * agents. We start with the DHCPOFFER packet we got, and the
7637c478bd9Sstevel@tonic-gate 	 * options contained in it to make a requested option list.
7647c478bd9Sstevel@tonic-gate 	 */
7657c478bd9Sstevel@tonic-gate 	pkt = (PKT *)dhcp_snd_bufp;
7667c478bd9Sstevel@tonic-gate 	opt = init_msg(pkt, opt_request);
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate 	/* Time from Successful DISCOVER message. */
7697c478bd9Sstevel@tonic-gate 	pkt->secs = htons((uint16_t)dhcp_secs);
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 	size = (uint16_t)(dhcp_buf_size - sizeof (struct ip) -
7727c478bd9Sstevel@tonic-gate 	    sizeof (struct udphdr));
7737c478bd9Sstevel@tonic-gate 	size = htons(size);
7747c478bd9Sstevel@tonic-gate 	*opt++ = CD_MAX_DHCP_SIZE;
7757c478bd9Sstevel@tonic-gate 	*opt++ = sizeof (size);
7767c478bd9Sstevel@tonic-gate 	bcopy(&size, opt, sizeof (size));
7777c478bd9Sstevel@tonic-gate 	opt += sizeof (size);
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate 	set_hw_spec_data(pkt, &opt, opt_request);
7807c478bd9Sstevel@tonic-gate 	*opt++ = CD_SERVER_ID;
7817c478bd9Sstevel@tonic-gate 	*opt++ = pl->opts[CD_SERVER_ID]->len;
7827c478bd9Sstevel@tonic-gate 	bcopy(pl->opts[CD_SERVER_ID]->value, opt,
7837c478bd9Sstevel@tonic-gate 	    pl->opts[CD_SERVER_ID]->len);
7847c478bd9Sstevel@tonic-gate 	opt += pl->opts[CD_SERVER_ID]->len;
7857c478bd9Sstevel@tonic-gate 
7867c478bd9Sstevel@tonic-gate 	/* our offered lease minus boot time */
7877c478bd9Sstevel@tonic-gate 	*opt++ = CD_LEASE_TIME;
7887c478bd9Sstevel@tonic-gate 	*opt++ = 4;
7897c478bd9Sstevel@tonic-gate 	bcopy(pl->opts[CD_LEASE_TIME]->value, &t_time,
7907c478bd9Sstevel@tonic-gate 	    sizeof (t_time));
7917c478bd9Sstevel@tonic-gate 	t_time = ntohl(t_time);
7927c478bd9Sstevel@tonic-gate 	if ((uint32_t)t_time != DHCP_PERM)
7937c478bd9Sstevel@tonic-gate 		t_time -= (uint32_t)dhcp_secs;
7947c478bd9Sstevel@tonic-gate 	t_time = htonl(t_time);
7957c478bd9Sstevel@tonic-gate 	bcopy(&t_time, opt, sizeof (t_time));
7967c478bd9Sstevel@tonic-gate 	opt += sizeof (t_time);
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate 	/* our offered IP address, as required. */
7997c478bd9Sstevel@tonic-gate 	*opt++ = CD_REQUESTED_IP_ADDR;
8007c478bd9Sstevel@tonic-gate 	*opt++ = sizeof (struct in_addr);
8017c478bd9Sstevel@tonic-gate 	bcopy(&pl_pkt->yiaddr, opt, sizeof (struct in_addr));
8027c478bd9Sstevel@tonic-gate 	opt += sizeof (struct in_addr);
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate 	/*
8057c478bd9Sstevel@tonic-gate 	 * If a clientid was set using dhcp_set_client_id(), add this
8067c478bd9Sstevel@tonic-gate 	 * to the options.
8077c478bd9Sstevel@tonic-gate 	 */
8087c478bd9Sstevel@tonic-gate 	if (dhcp_clientid_len > 0) {
8097c478bd9Sstevel@tonic-gate 		*opt++ = CD_CLIENT_ID;
8107c478bd9Sstevel@tonic-gate 		*opt++ = dhcp_clientid_len;
8117c478bd9Sstevel@tonic-gate 		bcopy(dhcp_clientid, opt, dhcp_clientid_len);
8127c478bd9Sstevel@tonic-gate 		opt += dhcp_clientid_len;
8137c478bd9Sstevel@tonic-gate 	}
8147c478bd9Sstevel@tonic-gate 
8157c478bd9Sstevel@tonic-gate 	parameter_request_list(&opt);
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 	*opt++ = CD_END;
8187c478bd9Sstevel@tonic-gate 
8197c478bd9Sstevel@tonic-gate 	/* Done with the OFFER pkt. */
8207c478bd9Sstevel@tonic-gate 	bkmem_free((char *)pl->pkt, pl->len);
8217c478bd9Sstevel@tonic-gate 	bkmem_free((char *)pl, sizeof (PKT_LIST));
8227c478bd9Sstevel@tonic-gate 
8237c478bd9Sstevel@tonic-gate 	/*
8247c478bd9Sstevel@tonic-gate 	 * We make 4 attempts here. We wait for 2 seconds to accumulate
8257c478bd9Sstevel@tonic-gate 	 * requests, just in case a BOOTP server is too fast!
8267c478bd9Sstevel@tonic-gate 	 */
8277c478bd9Sstevel@tonic-gate 	pkt_size = (uint8_t *)opt - (uint8_t *)pkt;
8287c478bd9Sstevel@tonic-gate 	if (pkt_size < sizeof (PKT))
8297c478bd9Sstevel@tonic-gate 		pkt_size = sizeof (PKT);
8307c478bd9Sstevel@tonic-gate 
8317c478bd9Sstevel@tonic-gate 	nets.s_addr = INADDR_BROADCAST;
8327c478bd9Sstevel@tonic-gate 	ours.s_addr = INADDR_ANY;
8337c478bd9Sstevel@tonic-gate 	timeout = 0;	/* reset timeout */
8347c478bd9Sstevel@tonic-gate 
8357c478bd9Sstevel@tonic-gate 	if ((err = inet(pkt_size, &ours, &nets, 4, (time_t)2L)) != 0)
8367c478bd9Sstevel@tonic-gate 		return (err);
8377c478bd9Sstevel@tonic-gate 	for (wk = list_hd; wk != NULL && state_pl == NULL; wk = wk->next) {
8387c478bd9Sstevel@tonic-gate 		if (dhcp_options_scan(wk, B_TRUE) != 0 ||
8397c478bd9Sstevel@tonic-gate 		    !wk->opts[CD_DHCP_TYPE])
8407c478bd9Sstevel@tonic-gate 			continue;	/* garbled options */
8417c478bd9Sstevel@tonic-gate 		switch (*wk->opts[CD_DHCP_TYPE]->value) {
8427c478bd9Sstevel@tonic-gate 		case ACK:
8437c478bd9Sstevel@tonic-gate 			remove_list(wk, B_FALSE);
8447c478bd9Sstevel@tonic-gate 			state_pl = wk;
8457c478bd9Sstevel@tonic-gate 			break;
8467c478bd9Sstevel@tonic-gate 		case NAK:
8477c478bd9Sstevel@tonic-gate 			printf("%s: rejected by DHCP server: %s\n",
8487c478bd9Sstevel@tonic-gate 			    s_n, inet_ntoa(*((struct in_addr *)wk->
8497c478bd9Sstevel@tonic-gate 			    opts[CD_SERVER_ID]->value)));
8507c478bd9Sstevel@tonic-gate 			if (wk->opts[CD_MESSAGE])
8517c478bd9Sstevel@tonic-gate 				prt_server_msg(wk->opts[CD_MESSAGE]);
8527c478bd9Sstevel@tonic-gate 			break;
8537c478bd9Sstevel@tonic-gate 		default:
8547c478bd9Sstevel@tonic-gate 			dprintf("%s: Unexpected DHCP message type.\n", s_n);
8557c478bd9Sstevel@tonic-gate 			break;
8567c478bd9Sstevel@tonic-gate 		}
8577c478bd9Sstevel@tonic-gate 	}
8587c478bd9Sstevel@tonic-gate 	flush_list();
8597c478bd9Sstevel@tonic-gate 	if (state_pl != NULL) {
8607c478bd9Sstevel@tonic-gate 		if (state_pl->opts[CD_MESSAGE])
8617c478bd9Sstevel@tonic-gate 			prt_server_msg(state_pl->opts[CD_MESSAGE]);
8627c478bd9Sstevel@tonic-gate 		pl_pkt = state_pl->pkt;
8637c478bd9Sstevel@tonic-gate 		/* arp our new address, just to make sure */
8647c478bd9Sstevel@tonic-gate 		if (!mac_call_arp(&pl_pkt->yiaddr, NULL, DHCP_ARP_TIMEOUT)) {
8657c478bd9Sstevel@tonic-gate 			/*
8667c478bd9Sstevel@tonic-gate 			 * No response. Check if lease is ok.
8677c478bd9Sstevel@tonic-gate 			 */
8687c478bd9Sstevel@tonic-gate 			doptp = state_pl->opts[CD_LEASE_TIME];
8697c478bd9Sstevel@tonic-gate 			if (state_pl->opts[CD_DHCP_TYPE] && (!doptp ||
8707c478bd9Sstevel@tonic-gate 			    (doptp->len % 4) != 0)) {
8717c478bd9Sstevel@tonic-gate 				dhcp_decline("Missing or corrupted lease time",
8727c478bd9Sstevel@tonic-gate 				    state_pl);
8737c478bd9Sstevel@tonic-gate 				err++;
8747c478bd9Sstevel@tonic-gate 			}
8757c478bd9Sstevel@tonic-gate 		} else {
8767c478bd9Sstevel@tonic-gate 			dhcp_decline("IP Address already being used!",
8777c478bd9Sstevel@tonic-gate 			    state_pl);
8787c478bd9Sstevel@tonic-gate 			err++;
8797c478bd9Sstevel@tonic-gate 		}
8807c478bd9Sstevel@tonic-gate 		if (err) {
8817c478bd9Sstevel@tonic-gate 			bkmem_free((char *)state_pl->pkt, state_pl->len);
8827c478bd9Sstevel@tonic-gate 			bkmem_free((char *)state_pl, sizeof (PKT_LIST));
8837c478bd9Sstevel@tonic-gate 			state_pl = NULL;
8847c478bd9Sstevel@tonic-gate 		} else
8857c478bd9Sstevel@tonic-gate 			return (0);	/* passes the tests! */
8867c478bd9Sstevel@tonic-gate 	}
8877c478bd9Sstevel@tonic-gate 	dprintf("%s: No valid DHCP acknowledge messages received.\n", s_n);
8887c478bd9Sstevel@tonic-gate 	return (1);
8897c478bd9Sstevel@tonic-gate }
8907c478bd9Sstevel@tonic-gate 
8917c478bd9Sstevel@tonic-gate /*
8927c478bd9Sstevel@tonic-gate  * Implements BOUND state of DHCP client state machine.
8937c478bd9Sstevel@tonic-gate  */
8947c478bd9Sstevel@tonic-gate static int
8957c478bd9Sstevel@tonic-gate dhcp_bound(void)
8967c478bd9Sstevel@tonic-gate {
8977c478bd9Sstevel@tonic-gate 	PKT		*pl_pkt = state_pl->pkt;
8987c478bd9Sstevel@tonic-gate 	DHCP_OPT	*doptp;
8997c478bd9Sstevel@tonic-gate 	uint8_t		*tp, *hp;
9007c478bd9Sstevel@tonic-gate 	int		items, i, fnd, k;
9017c478bd9Sstevel@tonic-gate 	char		hostname[MAXHOSTNAMELEN+1];
9027c478bd9Sstevel@tonic-gate 	struct in_addr	subnet, defr, savr, *ipp, xip;
9037c478bd9Sstevel@tonic-gate 
9047c478bd9Sstevel@tonic-gate #ifdef	DHCP_DEBUG
9057c478bd9Sstevel@tonic-gate 	if (dhcp_classid[0] != 0) {
9067c478bd9Sstevel@tonic-gate 		char 	scratch[DHCP_MAX_OPT_SIZE + 1];
9077c478bd9Sstevel@tonic-gate 
9087c478bd9Sstevel@tonic-gate 		bcopy(&dhcp_classid[2], scratch, dhcp_classid[1]);
9097c478bd9Sstevel@tonic-gate 		scratch[dhcp_classid[1]] = '\0';
9107c478bd9Sstevel@tonic-gate 		printf("Your machine is of the class: '%s'.\n", scratch);
9117c478bd9Sstevel@tonic-gate 	}
9127c478bd9Sstevel@tonic-gate #endif	/* DHCP_DEBUG */
9137c478bd9Sstevel@tonic-gate 
9147c478bd9Sstevel@tonic-gate 	/* First, set the bare essentials. (IP layer parameters) */
9157c478bd9Sstevel@tonic-gate 
9167c478bd9Sstevel@tonic-gate 	ipv4_getipaddr(&xip);
9177c478bd9Sstevel@tonic-gate 	if (xip.s_addr != INADDR_ANY)
9187c478bd9Sstevel@tonic-gate 		ipp = &xip;
9197c478bd9Sstevel@tonic-gate 	else {
9207c478bd9Sstevel@tonic-gate 		ipp = &pl_pkt->yiaddr;
9217c478bd9Sstevel@tonic-gate 		ipp->s_addr = ntohl(ipp->s_addr);
9227c478bd9Sstevel@tonic-gate 		ipv4_setipaddr(ipp);
9237c478bd9Sstevel@tonic-gate 	}
9247c478bd9Sstevel@tonic-gate 
9257c478bd9Sstevel@tonic-gate 	ipp->s_addr = htonl(ipp->s_addr);
9267c478bd9Sstevel@tonic-gate 
9277c478bd9Sstevel@tonic-gate 	if (boothowto & RB_VERBOSE)
9287c478bd9Sstevel@tonic-gate 		printf("%s: IP address is: %s\n", s_n, inet_ntoa(*ipp));
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate 	/*
9317c478bd9Sstevel@tonic-gate 	 * Ensure that the Boot NFS READ size, if given, is an int16_t.
9327c478bd9Sstevel@tonic-gate 	 */
9337c478bd9Sstevel@tonic-gate 	if (state_pl->vs[VS_BOOT_NFS_READSIZE] != NULL) {
9347c478bd9Sstevel@tonic-gate 		doptp = state_pl->vs[VS_BOOT_NFS_READSIZE];
9357c478bd9Sstevel@tonic-gate 		if (doptp->len != sizeof (int16_t))
9367c478bd9Sstevel@tonic-gate 			state_pl->vs[VS_BOOT_NFS_READSIZE] = NULL;
9377c478bd9Sstevel@tonic-gate 	}
9387c478bd9Sstevel@tonic-gate 
9397c478bd9Sstevel@tonic-gate 	/*
9407c478bd9Sstevel@tonic-gate 	 * Set subnetmask. Nice, but not required.
9417c478bd9Sstevel@tonic-gate 	 */
9427c478bd9Sstevel@tonic-gate 	if (state_pl->opts[CD_SUBNETMASK] != NULL) {
9437c478bd9Sstevel@tonic-gate 		doptp = state_pl->opts[CD_SUBNETMASK];
9447c478bd9Sstevel@tonic-gate 		if (doptp->len != 4)
9457c478bd9Sstevel@tonic-gate 			state_pl->opts[CD_SUBNETMASK] = NULL;
9467c478bd9Sstevel@tonic-gate 		else {
9477c478bd9Sstevel@tonic-gate 			bcopy(doptp->value, &subnet,
9487c478bd9Sstevel@tonic-gate 			    sizeof (struct in_addr));
9497c478bd9Sstevel@tonic-gate 			dprintf("%s: Setting netmask to: %s\n", s_n,
9507c478bd9Sstevel@tonic-gate 			    inet_ntoa(subnet));
9517c478bd9Sstevel@tonic-gate 			subnet.s_addr = ntohl(subnet.s_addr);
9527c478bd9Sstevel@tonic-gate 			ipv4_setnetmask(&subnet);
9537c478bd9Sstevel@tonic-gate 		}
9547c478bd9Sstevel@tonic-gate 	}
9557c478bd9Sstevel@tonic-gate 
9567c478bd9Sstevel@tonic-gate 	/*
9577c478bd9Sstevel@tonic-gate 	 * Set default IP TTL. Nice, but not required.
9587c478bd9Sstevel@tonic-gate 	 */
9597c478bd9Sstevel@tonic-gate 	if (state_pl->opts[CD_IPTTL] != NULL) {
9607c478bd9Sstevel@tonic-gate 		doptp = state_pl->opts[CD_IPTTL];
9617c478bd9Sstevel@tonic-gate 		if (doptp->len > 1)
9627c478bd9Sstevel@tonic-gate 			state_pl->opts[CD_IPTTL] = NULL;
9637c478bd9Sstevel@tonic-gate 		else {
9647c478bd9Sstevel@tonic-gate 			doptp = state_pl->opts[CD_IPTTL];
9657c478bd9Sstevel@tonic-gate 			ipv4_setmaxttl(*(uint8_t *)doptp->value);
9667c478bd9Sstevel@tonic-gate 			dprintf("%s: Setting IP TTL to: %d\n", s_n,
9677c478bd9Sstevel@tonic-gate 			    *(uint8_t *)doptp->value);
9687c478bd9Sstevel@tonic-gate 		}
9697c478bd9Sstevel@tonic-gate 	}
9707c478bd9Sstevel@tonic-gate 
9717c478bd9Sstevel@tonic-gate 	/*
9727c478bd9Sstevel@tonic-gate 	 * Set default router. Not required, although we'll know soon
9737c478bd9Sstevel@tonic-gate 	 * enough...
9747c478bd9Sstevel@tonic-gate 	 */
9757c478bd9Sstevel@tonic-gate 	if (state_pl->opts[CD_ROUTER] != NULL) {
9767c478bd9Sstevel@tonic-gate 		doptp = state_pl->opts[CD_ROUTER];
9777c478bd9Sstevel@tonic-gate 		if ((doptp->len % 4) != 0) {
9787c478bd9Sstevel@tonic-gate 			state_pl->opts[CD_ROUTER] = NULL;
9797c478bd9Sstevel@tonic-gate 		} else {
9807c478bd9Sstevel@tonic-gate 			if ((hp = (uint8_t *)bkmem_alloc(
9817c478bd9Sstevel@tonic-gate 			    mac_get_hdr_len())) == NULL) {
9827c478bd9Sstevel@tonic-gate 				errno = ENOMEM;
9837c478bd9Sstevel@tonic-gate 				return (-1);
9847c478bd9Sstevel@tonic-gate 			}
9857c478bd9Sstevel@tonic-gate 			items = doptp->len / sizeof (struct in_addr);
9867c478bd9Sstevel@tonic-gate 			tp = doptp->value;
9877c478bd9Sstevel@tonic-gate 			bcopy(tp, &savr, sizeof (struct in_addr));
9887c478bd9Sstevel@tonic-gate 			for (i = 0, fnd = 0; i < items; i++) {
9897c478bd9Sstevel@tonic-gate 				bcopy(tp, &defr, sizeof (struct in_addr));
9907c478bd9Sstevel@tonic-gate 				for (k = 0, fnd = 0; k < 2 && fnd == 0; k++) {
9917c478bd9Sstevel@tonic-gate 					fnd = mac_get_arp(&defr, hp,
9927c478bd9Sstevel@tonic-gate 					    mac_get_hdr_len(),
9937c478bd9Sstevel@tonic-gate 					    mac_get_arp_timeout());
9947c478bd9Sstevel@tonic-gate 				}
9957c478bd9Sstevel@tonic-gate 				if (fnd)
9967c478bd9Sstevel@tonic-gate 					break;
9977c478bd9Sstevel@tonic-gate 				dprintf(
9987c478bd9Sstevel@tonic-gate 				    "%s: Warning: Router %s is unreachable.\n",
9997c478bd9Sstevel@tonic-gate 				    s_n, inet_ntoa(defr));
10007c478bd9Sstevel@tonic-gate 				tp += sizeof (struct in_addr);
10017c478bd9Sstevel@tonic-gate 			}
10027c478bd9Sstevel@tonic-gate 			bkmem_free((char *)hp, mac_get_hdr_len());
10037c478bd9Sstevel@tonic-gate 
10047c478bd9Sstevel@tonic-gate 			/*
10057c478bd9Sstevel@tonic-gate 			 * If fnd is 0, we didn't find a working router. We'll
10067c478bd9Sstevel@tonic-gate 			 * still try to use first default router. If we got
10077c478bd9Sstevel@tonic-gate 			 * a bad router address (like not on the same net),
10087c478bd9Sstevel@tonic-gate 			 * we're hosed anyway.
10097c478bd9Sstevel@tonic-gate 			 */
10107c478bd9Sstevel@tonic-gate 			if (!fnd) {
10117c478bd9Sstevel@tonic-gate 				dprintf(
10127c478bd9Sstevel@tonic-gate 				    "%s: Warning: Using default router: %s\n",
10137c478bd9Sstevel@tonic-gate 				    s_n, inet_ntoa(savr));
10147c478bd9Sstevel@tonic-gate 				defr.s_addr = savr.s_addr;
10157c478bd9Sstevel@tonic-gate 			}
10167c478bd9Sstevel@tonic-gate 			/* ipv4_route expects network order IP addresses */
10177c478bd9Sstevel@tonic-gate 			(void) ipv4_route(IPV4_ADD_ROUTE, RT_DEFAULT, NULL,
10187c478bd9Sstevel@tonic-gate 			    &defr);
10197c478bd9Sstevel@tonic-gate 		}
10207c478bd9Sstevel@tonic-gate 	}
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate 	/*
10237c478bd9Sstevel@tonic-gate 	 * Set hostname. Not required.
10247c478bd9Sstevel@tonic-gate 	 */
10257c478bd9Sstevel@tonic-gate 	if (state_pl->opts[CD_HOSTNAME] != NULL) {
10267c478bd9Sstevel@tonic-gate 		doptp = state_pl->opts[CD_HOSTNAME];
10277c478bd9Sstevel@tonic-gate 		i = doptp->len;
10287c478bd9Sstevel@tonic-gate 		if (i > MAXHOSTNAMELEN)
10297c478bd9Sstevel@tonic-gate 			i = MAXHOSTNAMELEN;
10307c478bd9Sstevel@tonic-gate 		bcopy(doptp->value, hostname, i);
10317c478bd9Sstevel@tonic-gate 		hostname[i] = '\0';
10327c478bd9Sstevel@tonic-gate 		(void) sethostname(hostname, i);
10337c478bd9Sstevel@tonic-gate 		if (boothowto & RB_VERBOSE)
10347c478bd9Sstevel@tonic-gate 			printf("%s: Hostname is %s\n", s_n, hostname);
10357c478bd9Sstevel@tonic-gate 	}
10367c478bd9Sstevel@tonic-gate 
10377c478bd9Sstevel@tonic-gate 	/*
10387c478bd9Sstevel@tonic-gate 	 * We don't care about the lease time.... We can't enforce it anyway.
10397c478bd9Sstevel@tonic-gate 	 */
10407c478bd9Sstevel@tonic-gate 	return (0);
10417c478bd9Sstevel@tonic-gate }
10427c478bd9Sstevel@tonic-gate 
10437c478bd9Sstevel@tonic-gate /*
10447c478bd9Sstevel@tonic-gate  * Convert the DHCPACK into a pure ASCII boot property for use by the kernel
10457c478bd9Sstevel@tonic-gate  * later in the boot process.
10467c478bd9Sstevel@tonic-gate  */
10477c478bd9Sstevel@tonic-gate static void
10487c478bd9Sstevel@tonic-gate create_bootpresponse_bprop(PKT_LIST *pl)
10497c478bd9Sstevel@tonic-gate {
10507c478bd9Sstevel@tonic-gate 	uint_t	buflen;
10517c478bd9Sstevel@tonic-gate #if defined(__i386)
10527c478bd9Sstevel@tonic-gate 	extern struct bootops bootops;
10537c478bd9Sstevel@tonic-gate 	extern int bsetprop(struct bootops *, char *, caddr_t, int, phandle_t);
10547c478bd9Sstevel@tonic-gate #endif	/* __i386 */
10557c478bd9Sstevel@tonic-gate 
10567c478bd9Sstevel@tonic-gate 	if (pl == NULL || bootp_response != NULL)
10577c478bd9Sstevel@tonic-gate 		return;
10587c478bd9Sstevel@tonic-gate 
10597c478bd9Sstevel@tonic-gate 	buflen = (pl->len * 2) + 1;	/* extra space for null (1) */
10607c478bd9Sstevel@tonic-gate 	if ((bootp_response = bkmem_alloc(buflen)) == NULL)
10617c478bd9Sstevel@tonic-gate 		return;
10627c478bd9Sstevel@tonic-gate 
10637c478bd9Sstevel@tonic-gate 	if (octet_to_hexascii((uint8_t *)pl->pkt, pl->len, bootp_response,
10647c478bd9Sstevel@tonic-gate 	    &buflen) != 0) {
10657c478bd9Sstevel@tonic-gate 		bkmem_free(bootp_response, (pl->len * 2) + 1);
10667c478bd9Sstevel@tonic-gate 		bootp_response = NULL;
10677c478bd9Sstevel@tonic-gate 	}
10687c478bd9Sstevel@tonic-gate 
10697c478bd9Sstevel@tonic-gate #if defined(__i386)
10707c478bd9Sstevel@tonic-gate 	/* Use bsetprop to create the bootp-response property */
10717c478bd9Sstevel@tonic-gate 	if (bsetprop(&bootops, "bootp-response", bootp_response, 0, 0) !=
10727c478bd9Sstevel@tonic-gate 	    BOOT_SUCCESS) {
10737c478bd9Sstevel@tonic-gate 		bkmem_free(bootp_response, (pl->len * 2) + 1);
10747c478bd9Sstevel@tonic-gate 		bootp_response = NULL;
10757c478bd9Sstevel@tonic-gate 	}
10767c478bd9Sstevel@tonic-gate #elif defined(__sparc)
10777c478bd9Sstevel@tonic-gate 	prom_create_encoded_prop("bootp-response", pl->pkt, pl->len,
10787c478bd9Sstevel@tonic-gate 	    ENCODE_BYTES);
10797c478bd9Sstevel@tonic-gate #endif	/* __i386 */
10807c478bd9Sstevel@tonic-gate }
10817c478bd9Sstevel@tonic-gate 
10827c478bd9Sstevel@tonic-gate /*
10837c478bd9Sstevel@tonic-gate  * Examines /chosen node for "bootp-response" property. If it exists, this
10847c478bd9Sstevel@tonic-gate  * property is the DHCPACK cached there by the PROM's DHCP implementation.
10857c478bd9Sstevel@tonic-gate  *
10867c478bd9Sstevel@tonic-gate  * If cache_present is B_TRUE, we simply return B_TRUE if the property exists
10877c478bd9Sstevel@tonic-gate  * w/o decoding it. If cache_present is B_FALSE, we decode the packet and
10887c478bd9Sstevel@tonic-gate  * use it as our state packet for the jump to BOUND mode. Note that it's good
10897c478bd9Sstevel@tonic-gate  * enough that the cache exists w/o validation for determining if that's what
10907c478bd9Sstevel@tonic-gate  * the user intended.
10917c478bd9Sstevel@tonic-gate  *
10927c478bd9Sstevel@tonic-gate  * We'll short-circuit the DHCP exchange by accepting this packet. We build a
10937c478bd9Sstevel@tonic-gate  * PKT_LIST structure, and copy the bootp-response property value into a
10947c478bd9Sstevel@tonic-gate  * PKT buffer we allocated. We then scan the PKT for options, and then
10957c478bd9Sstevel@tonic-gate  * set state_pl to point to it.
10967c478bd9Sstevel@tonic-gate  *
10977c478bd9Sstevel@tonic-gate  * Returns B_TRUE if a packet was cached (and was processed correctly), false
10987c478bd9Sstevel@tonic-gate  * otherwise. The caller needs to make the state change from SELECTING to
10997c478bd9Sstevel@tonic-gate  * BOUND upon a B_TRUE return from this function.
11007c478bd9Sstevel@tonic-gate  */
11017c478bd9Sstevel@tonic-gate int
11027c478bd9Sstevel@tonic-gate prom_cached_reply(int cache_present)
11037c478bd9Sstevel@tonic-gate {
11047c478bd9Sstevel@tonic-gate 	PKT_LIST	*pl;
11057c478bd9Sstevel@tonic-gate 	int	len;
11067c478bd9Sstevel@tonic-gate #if defined(__i386)
11077c478bd9Sstevel@tonic-gate 	char	*ack;
11087c478bd9Sstevel@tonic-gate 	int	pxe_ack_cache(char **);
11097c478bd9Sstevel@tonic-gate 
11107c478bd9Sstevel@tonic-gate 	if ((len = pxe_ack_cache(&ack)) <= 0)
11117c478bd9Sstevel@tonic-gate 		return (B_FALSE);
11127c478bd9Sstevel@tonic-gate #else
11137c478bd9Sstevel@tonic-gate 	dnode_t	chosen;
11147c478bd9Sstevel@tonic-gate 	char	*prop = PROM_BOOT_CACHED;
11157c478bd9Sstevel@tonic-gate 
11167c478bd9Sstevel@tonic-gate 	chosen = prom_finddevice("/chosen");
11177c478bd9Sstevel@tonic-gate 	if (chosen == OBP_NONODE || chosen == OBP_BADNODE)
11187c478bd9Sstevel@tonic-gate 		chosen = prom_nextnode((dnode_t)0);	/* root node */
11197c478bd9Sstevel@tonic-gate 
11207c478bd9Sstevel@tonic-gate 	if ((len = prom_getproplen(chosen, prop)) <= 0)
11217c478bd9Sstevel@tonic-gate 		return (B_FALSE);
11227c478bd9Sstevel@tonic-gate #endif	/* __i386 */
11237c478bd9Sstevel@tonic-gate 
11247c478bd9Sstevel@tonic-gate 	if (cache_present)
11257c478bd9Sstevel@tonic-gate 		return (B_TRUE);
11267c478bd9Sstevel@tonic-gate 
11277c478bd9Sstevel@tonic-gate 	if (((pl = (PKT_LIST *)bkmem_zalloc(sizeof (PKT_LIST))) == NULL) ||
11287c478bd9Sstevel@tonic-gate 	    ((pl->pkt = (PKT *)bkmem_zalloc(len)) == NULL)) {
11297c478bd9Sstevel@tonic-gate 		errno = ENOMEM;
11307c478bd9Sstevel@tonic-gate 		if (pl != NULL)
11317c478bd9Sstevel@tonic-gate 			bkmem_free((char *)pl, sizeof (PKT_LIST));
11327c478bd9Sstevel@tonic-gate 		return (B_FALSE);
11337c478bd9Sstevel@tonic-gate 	}
11347c478bd9Sstevel@tonic-gate 
11357c478bd9Sstevel@tonic-gate #if defined(__i386)
11367c478bd9Sstevel@tonic-gate 	bcopy(ack, pl->pkt, len);
11377c478bd9Sstevel@tonic-gate #else
11387c478bd9Sstevel@tonic-gate 	(void) prom_getprop(chosen, prop, (caddr_t)pl->pkt);
11397c478bd9Sstevel@tonic-gate #endif	/* __i386 */
11407c478bd9Sstevel@tonic-gate 
11417c478bd9Sstevel@tonic-gate 	pl->len = len;
11427c478bd9Sstevel@tonic-gate 
11437c478bd9Sstevel@tonic-gate 	if (dhcp_options_scan(pl, B_TRUE) != 0) {
11447c478bd9Sstevel@tonic-gate 		/* garbled packet */
11457c478bd9Sstevel@tonic-gate 		bkmem_free((char *)pl->pkt, pl->len);
11467c478bd9Sstevel@tonic-gate 		bkmem_free((char *)pl, sizeof (PKT_LIST));
11477c478bd9Sstevel@tonic-gate 		return (B_FALSE);
11487c478bd9Sstevel@tonic-gate 	}
11497c478bd9Sstevel@tonic-gate 
11507c478bd9Sstevel@tonic-gate 	state_pl = pl;
11517c478bd9Sstevel@tonic-gate 	return (B_TRUE);
11527c478bd9Sstevel@tonic-gate }
11537c478bd9Sstevel@tonic-gate 
11547c478bd9Sstevel@tonic-gate /*
11557c478bd9Sstevel@tonic-gate  * Perform DHCP to acquire what we used to get using rarp/bootparams.
11567c478bd9Sstevel@tonic-gate  * Returns 0 for success, nonzero otherwise.
11577c478bd9Sstevel@tonic-gate  *
11587c478bd9Sstevel@tonic-gate  * DHCP client state machine.
11597c478bd9Sstevel@tonic-gate  */
11607c478bd9Sstevel@tonic-gate int
11617c478bd9Sstevel@tonic-gate dhcp(void)
11627c478bd9Sstevel@tonic-gate {
11637c478bd9Sstevel@tonic-gate 	int	err = 0;
11647c478bd9Sstevel@tonic-gate 	int	done = B_FALSE;
11657c478bd9Sstevel@tonic-gate 
11667c478bd9Sstevel@tonic-gate 	dhcp_buf_size = mac_get_mtu();
11677c478bd9Sstevel@tonic-gate 	dhcp_snd_bufp = (PKT *)bkmem_alloc(dhcp_buf_size);
11687c478bd9Sstevel@tonic-gate 	dhcp_rcv_bufp = (PKT *)bkmem_alloc(dhcp_buf_size);
11697c478bd9Sstevel@tonic-gate 
11707c478bd9Sstevel@tonic-gate 	if (dhcp_snd_bufp == NULL || dhcp_rcv_bufp == NULL) {
11717c478bd9Sstevel@tonic-gate 		if (dhcp_snd_bufp != NULL)
11727c478bd9Sstevel@tonic-gate 			bkmem_free((char *)dhcp_snd_bufp, dhcp_buf_size);
11737c478bd9Sstevel@tonic-gate 		if (dhcp_rcv_bufp != NULL)
11747c478bd9Sstevel@tonic-gate 			bkmem_free((char *)dhcp_rcv_bufp, dhcp_buf_size);
11757c478bd9Sstevel@tonic-gate 		errno = ENOMEM;
11767c478bd9Sstevel@tonic-gate 		return (-1);
11777c478bd9Sstevel@tonic-gate 	}
11787c478bd9Sstevel@tonic-gate 
11797c478bd9Sstevel@tonic-gate 	while (err == 0 && !done) {
11807c478bd9Sstevel@tonic-gate 		switch (dhcp_state) {
11817c478bd9Sstevel@tonic-gate 		case INIT:
11827c478bd9Sstevel@tonic-gate 
11837c478bd9Sstevel@tonic-gate 			s_n = "INIT";
11847c478bd9Sstevel@tonic-gate 			if (prom_cached_reply(B_FALSE)) {
11857c478bd9Sstevel@tonic-gate 				dprintf("%s: Using PROM cached BOOT reply...\n",
11867c478bd9Sstevel@tonic-gate 				    s_n);
11877c478bd9Sstevel@tonic-gate 				dhcp_state = BOUND;
11887c478bd9Sstevel@tonic-gate 			} else {
11897c478bd9Sstevel@tonic-gate 				dhcp_state = SELECTING;
11907c478bd9Sstevel@tonic-gate 				dhcp_start_time = prom_gettime();
11917c478bd9Sstevel@tonic-gate 			}
11927c478bd9Sstevel@tonic-gate 			break;
11937c478bd9Sstevel@tonic-gate 
11947c478bd9Sstevel@tonic-gate 		case SELECTING:
11957c478bd9Sstevel@tonic-gate 
11967c478bd9Sstevel@tonic-gate 			s_n = "SELECTING";
11977c478bd9Sstevel@tonic-gate 			err = dhcp_selecting();
11987c478bd9Sstevel@tonic-gate 			switch (err) {
11997c478bd9Sstevel@tonic-gate 			case 0:
12007c478bd9Sstevel@tonic-gate 				dhcp_state = REQUESTING;
12017c478bd9Sstevel@tonic-gate 				break;
12027c478bd9Sstevel@tonic-gate 			case DHCP_NO_DATA:
12037c478bd9Sstevel@tonic-gate 				dprintf(
12047c478bd9Sstevel@tonic-gate 				    "%s: No DHCP response after %d tries.\n",
12057c478bd9Sstevel@tonic-gate 				    s_n, DHCP_RETRIES);
12067c478bd9Sstevel@tonic-gate 				break;
12077c478bd9Sstevel@tonic-gate 			default:
12087c478bd9Sstevel@tonic-gate 				/* major network problems */
12097c478bd9Sstevel@tonic-gate 				dprintf("%s: Network transaction failed: %d\n",
12107c478bd9Sstevel@tonic-gate 				    s_n, err);
12117c478bd9Sstevel@tonic-gate 				break;
12127c478bd9Sstevel@tonic-gate 			}
12137c478bd9Sstevel@tonic-gate 			break;
12147c478bd9Sstevel@tonic-gate 
12157c478bd9Sstevel@tonic-gate 		case REQUESTING:
12167c478bd9Sstevel@tonic-gate 
12177c478bd9Sstevel@tonic-gate 			s_n = "REQUESTING";
12187c478bd9Sstevel@tonic-gate 			err = dhcp_requesting();
12197c478bd9Sstevel@tonic-gate 			switch (err) {
12207c478bd9Sstevel@tonic-gate 			case 0:
12217c478bd9Sstevel@tonic-gate 				dhcp_state = BOUND;
12227c478bd9Sstevel@tonic-gate 				break;
12237c478bd9Sstevel@tonic-gate 			case DHCP_NO_DATA:
12247c478bd9Sstevel@tonic-gate 				dprintf("%s: Request timed out.\n", s_n);
12257c478bd9Sstevel@tonic-gate 				dhcp_state = SELECTING;
12267c478bd9Sstevel@tonic-gate 				err = 0;
12277c478bd9Sstevel@tonic-gate 				(void) sleep(10);
12287c478bd9Sstevel@tonic-gate 				break;
12297c478bd9Sstevel@tonic-gate 			default:
12307c478bd9Sstevel@tonic-gate 				/* major network problems */
12317c478bd9Sstevel@tonic-gate 				dprintf("%s: Network transaction failed: %d\n",
12327c478bd9Sstevel@tonic-gate 				    s_n, err);
12337c478bd9Sstevel@tonic-gate 				break;
12347c478bd9Sstevel@tonic-gate 			}
12357c478bd9Sstevel@tonic-gate 			break;
12367c478bd9Sstevel@tonic-gate 
12377c478bd9Sstevel@tonic-gate 		case BOUND:
12387c478bd9Sstevel@tonic-gate 
12397c478bd9Sstevel@tonic-gate 			/*
12407c478bd9Sstevel@tonic-gate 			 * We just "give up" if bound state fails.
12417c478bd9Sstevel@tonic-gate 			 */
12427c478bd9Sstevel@tonic-gate 			s_n = "BOUND";
12437c478bd9Sstevel@tonic-gate 			if ((err = dhcp_bound()) == 0) {
12447c478bd9Sstevel@tonic-gate 				dhcp_state = CONFIGURED;
12457c478bd9Sstevel@tonic-gate 			}
12467c478bd9Sstevel@tonic-gate 			break;
12477c478bd9Sstevel@tonic-gate 
12487c478bd9Sstevel@tonic-gate 		case CONFIGURED:
12497c478bd9Sstevel@tonic-gate 			s_n = "CONFIGURED";
12507c478bd9Sstevel@tonic-gate 			create_bootpresponse_bprop(state_pl);
12517c478bd9Sstevel@tonic-gate 			done = B_TRUE;
12527c478bd9Sstevel@tonic-gate 			break;
12537c478bd9Sstevel@tonic-gate 		}
12547c478bd9Sstevel@tonic-gate 	}
12557c478bd9Sstevel@tonic-gate 	bkmem_free((char *)dhcp_snd_bufp, dhcp_buf_size);
12567c478bd9Sstevel@tonic-gate 	bkmem_free((char *)dhcp_rcv_bufp, dhcp_buf_size);
12577c478bd9Sstevel@tonic-gate 
12587c478bd9Sstevel@tonic-gate 	return (err);
12597c478bd9Sstevel@tonic-gate }
12607c478bd9Sstevel@tonic-gate 
12617c478bd9Sstevel@tonic-gate /*
12627c478bd9Sstevel@tonic-gate  * Returns a copy of the DHCP-supplied value of the parameter requested
12637c478bd9Sstevel@tonic-gate  * by code.
12647c478bd9Sstevel@tonic-gate  */
12657c478bd9Sstevel@tonic-gate boolean_t
12667c478bd9Sstevel@tonic-gate dhcp_getinfo(uchar_t optcat, uint16_t code, uint16_t optsize, void *value,
12677c478bd9Sstevel@tonic-gate     size_t *vallenp)
12687c478bd9Sstevel@tonic-gate {
12697c478bd9Sstevel@tonic-gate 	size_t		len = *vallenp;
12707c478bd9Sstevel@tonic-gate 
12717c478bd9Sstevel@tonic-gate 	if (dhcp_getinfo_pl(state_pl, optcat, code,
12727c478bd9Sstevel@tonic-gate 				optsize, value, &len)) {
12737c478bd9Sstevel@tonic-gate 
12747c478bd9Sstevel@tonic-gate 		if (len <= *vallenp) {
12757c478bd9Sstevel@tonic-gate 			*vallenp = len;
12767c478bd9Sstevel@tonic-gate 			return (B_TRUE);
12777c478bd9Sstevel@tonic-gate 		}
12787c478bd9Sstevel@tonic-gate 	}
12797c478bd9Sstevel@tonic-gate 
12807c478bd9Sstevel@tonic-gate 	return (B_FALSE);
12817c478bd9Sstevel@tonic-gate }
12827c478bd9Sstevel@tonic-gate 
12837c478bd9Sstevel@tonic-gate /*
12847c478bd9Sstevel@tonic-gate  * Sets the clientid option.
12857c478bd9Sstevel@tonic-gate  */
12867c478bd9Sstevel@tonic-gate void
12877c478bd9Sstevel@tonic-gate dhcp_set_client_id(uint8_t *clientid, uint8_t clientid_len)
12887c478bd9Sstevel@tonic-gate {
12897c478bd9Sstevel@tonic-gate 	bcopy(clientid, dhcp_clientid, clientid_len);
12907c478bd9Sstevel@tonic-gate 	dhcp_clientid_len = clientid_len;
12917c478bd9Sstevel@tonic-gate }
1292