xref: /onnv-gate/usr/src/stand/lib/inet/dhcpv4.c (revision 6747:393cf276a040)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*6747Sga159272  * Common Development and Distribution License (the "License").
6*6747Sga159272  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*6747Sga159272  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  *
250Sstevel@tonic-gate  * Standalone dhcp client.
260Sstevel@tonic-gate  */
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #include <sys/types.h>
310Sstevel@tonic-gate #include <sys/salib.h>
320Sstevel@tonic-gate #include <sys/bootconf.h>
330Sstevel@tonic-gate #include <sys/bootcmn.h>
340Sstevel@tonic-gate #include <sys/socket.h>
350Sstevel@tonic-gate #include <sys/isa_defs.h>
360Sstevel@tonic-gate #include <netinet/in.h>
370Sstevel@tonic-gate #include <netinet/in_systm.h>
380Sstevel@tonic-gate #include <netinet/inetutil.h>
390Sstevel@tonic-gate #include <netinet/dhcp.h>
400Sstevel@tonic-gate #include <netinet/ip.h>
410Sstevel@tonic-gate #include <netinet/udp.h>
420Sstevel@tonic-gate #include <dhcp_impl.h>
430Sstevel@tonic-gate #include <net/if_types.h>
440Sstevel@tonic-gate #include <sys/promif.h>
450Sstevel@tonic-gate #include <sys/platnames.h>
460Sstevel@tonic-gate #include <socket_inet.h>
470Sstevel@tonic-gate 
480Sstevel@tonic-gate #include "ipv4.h"
490Sstevel@tonic-gate #include "mac.h"
500Sstevel@tonic-gate #include <sys/bootdebug.h>
510Sstevel@tonic-gate #include "dhcpv4.h"
520Sstevel@tonic-gate 
530Sstevel@tonic-gate static char		*s_n = "INIT";
540Sstevel@tonic-gate static enum DHCPSTATE 	dhcp_state = INIT;
550Sstevel@tonic-gate 
560Sstevel@tonic-gate static PKT		*dhcp_snd_bufp, *dhcp_rcv_bufp;
570Sstevel@tonic-gate static int		dhcp_buf_size;
580Sstevel@tonic-gate 
590Sstevel@tonic-gate static const uint8_t	magic[] = BOOTMAGIC;	/* RFC1048 */
600Sstevel@tonic-gate static uint8_t		opt_discover[]  = { CD_DHCP_TYPE, 1, DISCOVER };
610Sstevel@tonic-gate static uint8_t		opt_request[]   = { CD_DHCP_TYPE, 1, REQUEST };
620Sstevel@tonic-gate static uint8_t		opt_decline[]   = { CD_DHCP_TYPE, 1, DECLINE };
630Sstevel@tonic-gate 
640Sstevel@tonic-gate static uint8_t		dhcp_classid[DHCP_MAX_OPT_SIZE + 3];
650Sstevel@tonic-gate static uint8_t		dhcp_clientid[DHCP_MAX_CID_LEN];
660Sstevel@tonic-gate static uint8_t		dhcp_clientid_len = 0;
670Sstevel@tonic-gate 
680Sstevel@tonic-gate static uint32_t		dhcp_start_time;	/* start time (msecs */
690Sstevel@tonic-gate static time_t		dhcp_secs;
700Sstevel@tonic-gate static uint32_t		timeout;	/* timeout in milliseconds */
710Sstevel@tonic-gate 
720Sstevel@tonic-gate static int		pkt_counter;
730Sstevel@tonic-gate PKT_LIST		*list_tl, *list_hd;
740Sstevel@tonic-gate PKT_LIST		*state_pl = NULL;
750Sstevel@tonic-gate 
760Sstevel@tonic-gate #define	PROM_BOOT_CACHED	"bootp-response"
770Sstevel@tonic-gate extern char	*bootp_response;	/* bootprop.c */
780Sstevel@tonic-gate extern int	pagesize;
790Sstevel@tonic-gate 
800Sstevel@tonic-gate /*
810Sstevel@tonic-gate  * Do whatever reset actions/initialization actions are generic for every
820Sstevel@tonic-gate  * DHCP/bootp message. Set the message type.
830Sstevel@tonic-gate  *
840Sstevel@tonic-gate  * Returns: the updated options ptr.
850Sstevel@tonic-gate  */
860Sstevel@tonic-gate static uint8_t *
init_msg(PKT * pkt,uint8_t * pkttype)870Sstevel@tonic-gate init_msg(PKT *pkt, uint8_t *pkttype)
880Sstevel@tonic-gate {
890Sstevel@tonic-gate 	static uint32_t xid;
900Sstevel@tonic-gate 
910Sstevel@tonic-gate 	bzero(pkt, dhcp_buf_size);
920Sstevel@tonic-gate 	bcopy(magic, pkt->cookie, sizeof (pkt->cookie));
930Sstevel@tonic-gate 	pkt->op = BOOTREQUEST;
940Sstevel@tonic-gate 	if (xid == 0)
950Sstevel@tonic-gate 		bcopy(mac_get_addr_buf()+2, &xid, 4);
960Sstevel@tonic-gate 	else
970Sstevel@tonic-gate 		xid++;
980Sstevel@tonic-gate 	pkt->xid = xid;
990Sstevel@tonic-gate 	bcopy(pkttype, pkt->options, 3);
1000Sstevel@tonic-gate 	return ((uint8_t *)(pkt->options + 3));
1010Sstevel@tonic-gate }
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate /*
1040Sstevel@tonic-gate  *  Parameter request list.
1050Sstevel@tonic-gate  */
1060Sstevel@tonic-gate static void
parameter_request_list(uint8_t ** opt)1070Sstevel@tonic-gate parameter_request_list(uint8_t **opt)
1080Sstevel@tonic-gate {
1090Sstevel@tonic-gate 	/*
1100Sstevel@tonic-gate 	 * This parameter request list is used in the normal 4-packet
1110Sstevel@tonic-gate 	 * DHCPDISCOVER/OFFER/REQUEST/ACK exchange; it must not contain
1120Sstevel@tonic-gate 	 * CD_REQUESTED_IP_ADDR or CD_LEASE_TIME.
1130Sstevel@tonic-gate 	 */
1140Sstevel@tonic-gate 	static uint8_t	prlist[] = {
1150Sstevel@tonic-gate 	    CD_REQUEST_LIST,	/* parameter request list option number */
1160Sstevel@tonic-gate 	    4,			/* number of options requested */
1170Sstevel@tonic-gate 	    CD_SUBNETMASK,
1180Sstevel@tonic-gate 	    CD_ROUTER,
1190Sstevel@tonic-gate 	    CD_HOSTNAME,
1200Sstevel@tonic-gate 	    CD_VENDOR_SPEC
1210Sstevel@tonic-gate 	    };
1220Sstevel@tonic-gate 	if (opt && *opt) {
1230Sstevel@tonic-gate 		bcopy(prlist, *opt, sizeof (prlist));
1240Sstevel@tonic-gate 		*opt += sizeof (prlist);
1250Sstevel@tonic-gate 	}
1260Sstevel@tonic-gate }
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate /*
1290Sstevel@tonic-gate  * Set hardware specific fields
1300Sstevel@tonic-gate  */
1310Sstevel@tonic-gate static void
set_hw_spec_data(PKT * p,uint8_t ** opt,uint8_t * pkttype)1320Sstevel@tonic-gate set_hw_spec_data(PKT *p, uint8_t **opt, uint8_t *pkttype)
1330Sstevel@tonic-gate {
1340Sstevel@tonic-gate 	char mfg[DHCP_MAX_OPT_SIZE + 1], cbuf[DHCP_MAX_OPT_SIZE + 1];
1350Sstevel@tonic-gate 	uint8_t *tp, *dp;
1360Sstevel@tonic-gate 	int adjust_len, len, i;
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate 	p->htype = mac_arp_type(mac_get_type());
1390Sstevel@tonic-gate 	len = (uchar_t)mac_get_addr_len();
1400Sstevel@tonic-gate 	if (len <= sizeof (p->chaddr)) {
1410Sstevel@tonic-gate 		p->hlen = len;
1420Sstevel@tonic-gate 		bcopy(mac_get_addr_buf(), p->chaddr, len);
1430Sstevel@tonic-gate 	} else {
1440Sstevel@tonic-gate 		uint8_t type = *(pkttype + 2);
1450Sstevel@tonic-gate 		/*
1460Sstevel@tonic-gate 		 * The mac address does not fit in the chaddr
1470Sstevel@tonic-gate 		 * field, thus it can not be sent to the server,
1480Sstevel@tonic-gate 		 * thus server can not unicast the reply. Per
1490Sstevel@tonic-gate 		 * RFC 2131 4.4.1, client can set this bit in
1500Sstevel@tonic-gate 		 * DISCOVER/REQUEST.
1510Sstevel@tonic-gate 		 */
1520Sstevel@tonic-gate 		if ((type == DISCOVER) || (type == REQUEST))
1530Sstevel@tonic-gate 			p->flags = htons(BCAST_MASK);
1540Sstevel@tonic-gate 	}
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate 	if (opt && *opt) {
1570Sstevel@tonic-gate 		if (dhcp_classid[0] == '\0') {
1580Sstevel@tonic-gate 			/*
1590Sstevel@tonic-gate 			 * Classids based on mfg name: Commas (,) are
1600Sstevel@tonic-gate 			 * converted to periods (.), and spaces ( ) are removed.
1610Sstevel@tonic-gate 			 */
1620Sstevel@tonic-gate 			dhcp_classid[0] = CD_CLASS_ID;
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 			(void) strncpy(mfg, get_mfg_name(), sizeof (mfg));
1650Sstevel@tonic-gate 			if (strncmp(mfg, "SUNW", strlen("SUNW")) != 0) {
1660Sstevel@tonic-gate 				len = strlen("SUNW.");
1670Sstevel@tonic-gate 				(void) strcpy(cbuf, "SUNW.");
1680Sstevel@tonic-gate 			} else {
1690Sstevel@tonic-gate 				len = 0;
1700Sstevel@tonic-gate 				cbuf[0] = '\0';
1710Sstevel@tonic-gate 			}
1720Sstevel@tonic-gate 			len += strlen(mfg);
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate 			if ((len + 2) < DHCP_MAX_OPT_SIZE) {
1750Sstevel@tonic-gate 				tp = (uint8_t *)strcat(cbuf, mfg);
1760Sstevel@tonic-gate 				dp = &dhcp_classid[2];
1770Sstevel@tonic-gate 				adjust_len = 0;
1780Sstevel@tonic-gate 				for (i = 0; i < len; i++, tp++) {
1790Sstevel@tonic-gate 					if (*tp == ',') {
1800Sstevel@tonic-gate 						*dp++ = '.';
1810Sstevel@tonic-gate 					} else if (*tp == ' ') {
1820Sstevel@tonic-gate 						adjust_len++;
1830Sstevel@tonic-gate 					} else {
1840Sstevel@tonic-gate 						*dp++ = *tp;
1850Sstevel@tonic-gate 					}
1860Sstevel@tonic-gate 				}
1870Sstevel@tonic-gate 				len -= adjust_len;
1880Sstevel@tonic-gate 				dhcp_classid[1] = (uint8_t)len;
1890Sstevel@tonic-gate 			} else
1900Sstevel@tonic-gate 				prom_panic("Not enough space for class id");
1910Sstevel@tonic-gate #ifdef	DHCP_DEBUG
1920Sstevel@tonic-gate 			printf("%s: Classid: %s\n", s_n, &dhcp_classid[2]);
1930Sstevel@tonic-gate #endif	/* DHCP_DEBUG */
1940Sstevel@tonic-gate 		}
1950Sstevel@tonic-gate 		bcopy(dhcp_classid, *opt, dhcp_classid[1] + 2);
1960Sstevel@tonic-gate 		*opt += dhcp_classid[1] + 2;
1970Sstevel@tonic-gate 	}
1980Sstevel@tonic-gate }
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate static void
flush_list(void)2010Sstevel@tonic-gate flush_list(void)
2020Sstevel@tonic-gate {
2030Sstevel@tonic-gate 	PKT_LIST *wk, *tmp;
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate 	wk = list_hd;
2060Sstevel@tonic-gate 	while (wk != NULL) {
2070Sstevel@tonic-gate 		tmp = wk;
2080Sstevel@tonic-gate 		wk = wk->next;
2090Sstevel@tonic-gate 		bkmem_free((char *)tmp->pkt, tmp->len);
2100Sstevel@tonic-gate 		bkmem_free((char *)tmp, sizeof (PKT_LIST));
2110Sstevel@tonic-gate 	}
2120Sstevel@tonic-gate 	list_hd = list_tl = NULL;
2130Sstevel@tonic-gate 	pkt_counter = 0;
2140Sstevel@tonic-gate }
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate static void
remove_list(PKT_LIST * pl,int flag)2170Sstevel@tonic-gate remove_list(PKT_LIST *pl, int flag)
2180Sstevel@tonic-gate {
2190Sstevel@tonic-gate 	if (list_hd == NULL)
2200Sstevel@tonic-gate 		return;
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	if (list_hd == list_tl) {
2230Sstevel@tonic-gate 		list_hd = list_tl = NULL;
2240Sstevel@tonic-gate 	} else if (list_hd == pl) {
2250Sstevel@tonic-gate 		list_hd = pl->next;
2260Sstevel@tonic-gate 		list_hd->prev = NULL;
2270Sstevel@tonic-gate 	} else if (list_tl == pl) {
2280Sstevel@tonic-gate 		list_tl = list_tl->prev;
2290Sstevel@tonic-gate 		list_tl->next = NULL;
2300Sstevel@tonic-gate 	} else {
2310Sstevel@tonic-gate 		pl->prev->next = pl->next;
2320Sstevel@tonic-gate 		pl->next->prev = pl->prev;
2330Sstevel@tonic-gate 	}
2340Sstevel@tonic-gate 	pkt_counter--;
2350Sstevel@tonic-gate 	if (flag) {
2360Sstevel@tonic-gate 		bkmem_free((char *)pl->pkt, pl->len);
2370Sstevel@tonic-gate 		bkmem_free((char *)pl, sizeof (PKT_LIST));
2380Sstevel@tonic-gate 	}
2390Sstevel@tonic-gate }
2400Sstevel@tonic-gate 
2410Sstevel@tonic-gate /*
2420Sstevel@tonic-gate  * Collects BOOTP responses. Length has to be right, it has to be
2430Sstevel@tonic-gate  * a BOOTP reply pkt, with the same XID and HW address as ours. Adds
2440Sstevel@tonic-gate  * them to the pkt list.
2450Sstevel@tonic-gate  *
2460Sstevel@tonic-gate  * Returns 0 if no error processing packet, 1 if an error occurred and/or
2470Sstevel@tonic-gate  * collection of replies should stop. Used in inet() calls.
2480Sstevel@tonic-gate  */
2490Sstevel@tonic-gate static int
bootp_collect(int len)2500Sstevel@tonic-gate bootp_collect(int len)
2510Sstevel@tonic-gate {
2520Sstevel@tonic-gate 	PKT		*s = (PKT *)dhcp_snd_bufp;
2530Sstevel@tonic-gate 	PKT		*r = (PKT *)dhcp_rcv_bufp;
2540Sstevel@tonic-gate 	PKT_LIST	*pl;
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 	if (len < sizeof (PKT)) {
2570Sstevel@tonic-gate 		dprintf("%s: BOOTP reply too small: %d\n", s_n, len);
2580Sstevel@tonic-gate 		return (1);
2590Sstevel@tonic-gate 	}
2600Sstevel@tonic-gate 	if (r->op == BOOTREPLY && r->xid == s->xid &&
2610Sstevel@tonic-gate 	    bcmp((caddr_t)s->chaddr, (caddr_t)r->chaddr, s->hlen) == 0) {
2620Sstevel@tonic-gate 		/* Add a packet to the pkt list */
2630Sstevel@tonic-gate 		if (pkt_counter > (MAX_PKT_LIST - 1))
2640Sstevel@tonic-gate 			return (1);	/* got enough packets already */
2650Sstevel@tonic-gate 		if (((pl = (PKT_LIST *)bkmem_zalloc(sizeof (PKT_LIST))) ==
2660Sstevel@tonic-gate 		    NULL) || ((pl->pkt = (PKT *)bkmem_zalloc(len)) == NULL)) {
2670Sstevel@tonic-gate 			errno = ENOMEM;
2680Sstevel@tonic-gate 			if (pl != NULL)
2690Sstevel@tonic-gate 				bkmem_free((char *)pl, sizeof (PKT_LIST));
2700Sstevel@tonic-gate 			return (1);
2710Sstevel@tonic-gate 		}
2720Sstevel@tonic-gate 		bcopy(dhcp_rcv_bufp, pl->pkt, len);
2730Sstevel@tonic-gate 		pl->len = len;
2740Sstevel@tonic-gate 		if (list_hd == NULL) {
2750Sstevel@tonic-gate 			list_hd = list_tl = pl;
2760Sstevel@tonic-gate 			pl->prev = NULL;
2770Sstevel@tonic-gate 		} else {
2780Sstevel@tonic-gate 			list_tl->next = pl;
2790Sstevel@tonic-gate 			pl->prev = list_tl;
2800Sstevel@tonic-gate 			list_tl = pl;
2810Sstevel@tonic-gate 		}
2820Sstevel@tonic-gate 		pkt_counter++;
2830Sstevel@tonic-gate 		pl->next = NULL;
2840Sstevel@tonic-gate 	}
2850Sstevel@tonic-gate 	return (0);
2860Sstevel@tonic-gate }
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate /*
2890Sstevel@tonic-gate  * Checks if BOOTP exchange(s) were successful. Returns 1 if they
2900Sstevel@tonic-gate  * were, 0 otherwise. Used in inet() calls.
2910Sstevel@tonic-gate  */
2920Sstevel@tonic-gate static int
bootp_success(void)2930Sstevel@tonic-gate bootp_success(void)
2940Sstevel@tonic-gate {
2950Sstevel@tonic-gate 	PKT	*s = (PKT *)dhcp_snd_bufp;
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 	if (list_hd != NULL) {
2980Sstevel@tonic-gate 		/* remember the secs - we may need them later */
2990Sstevel@tonic-gate 		dhcp_secs = ntohs(s->secs);
3000Sstevel@tonic-gate 		return (1);
3010Sstevel@tonic-gate 	}
3020Sstevel@tonic-gate 	s->secs = htons((uint16_t)((prom_gettime() - dhcp_start_time)/1000));
3030Sstevel@tonic-gate 	return (0);
3040Sstevel@tonic-gate }
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate /*
3070Sstevel@tonic-gate  * This function accesses the network. Opens a connection, and binds to
3080Sstevel@tonic-gate  * it if a client binding doesn't already exist. If 'tries' is 0, then
3090Sstevel@tonic-gate  * no reply is expected/returned. If 'tries' is non-zero, then 'tries'
3100Sstevel@tonic-gate  * attempts are made to get a valid response. If 'tol' is not zero,
3110Sstevel@tonic-gate  * then this function will wait for 'tol' milliseconds for more than one
3120Sstevel@tonic-gate  * response to a transmit.
3130Sstevel@tonic-gate  *
3140Sstevel@tonic-gate  * Returns 0 for success, errno otherwise.
3150Sstevel@tonic-gate  */
3160Sstevel@tonic-gate static int
inet(uint32_t size,struct in_addr * src,struct in_addr * dest,uint32_t tries,uint32_t tol)3170Sstevel@tonic-gate inet(uint32_t size, struct in_addr *src, struct in_addr *dest, uint32_t tries,
3180Sstevel@tonic-gate     uint32_t tol)
3190Sstevel@tonic-gate {
3200Sstevel@tonic-gate 	int			done = B_FALSE, flags, len;
3210Sstevel@tonic-gate 	uint32_t		attempts = 0;
3220Sstevel@tonic-gate 	int			sd;
3230Sstevel@tonic-gate 	uint32_t		wait_time;	/* Max time collect replies */
3240Sstevel@tonic-gate 	uint32_t		init_timeout;	/* Max time wait ANY reply */
3250Sstevel@tonic-gate 	uint32_t		now;
3260Sstevel@tonic-gate 	struct sockaddr_in	saddr, daddr;
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 	if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
3290Sstevel@tonic-gate 		dprintf("%s: Can't open a socket.\n", s_n);
3300Sstevel@tonic-gate 		return (errno);
3310Sstevel@tonic-gate 	}
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate 	flags = 0;
3340Sstevel@tonic-gate 
3350Sstevel@tonic-gate 	bzero(&saddr, sizeof (struct sockaddr_in));
3360Sstevel@tonic-gate 	saddr.sin_family = AF_INET;
3370Sstevel@tonic-gate 	saddr.sin_port = htons(IPPORT_BOOTPC);
3380Sstevel@tonic-gate 	saddr.sin_addr.s_addr = htonl(src->s_addr);
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate 	if (bind(sd, (struct sockaddr *)&saddr, sizeof (saddr)) < 0) {
3410Sstevel@tonic-gate 		dprintf("%s: Cannot bind to port %d, errno: %d\n",
3420Sstevel@tonic-gate 		    s_n, IPPORT_BOOTPC, errno);
3430Sstevel@tonic-gate 		(void) socket_close(sd);
3440Sstevel@tonic-gate 		return (errno);
3450Sstevel@tonic-gate 	}
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 	if (ntohl(dest->s_addr) == INADDR_BROADCAST) {
3480Sstevel@tonic-gate 		int dontroute = B_TRUE;
3490Sstevel@tonic-gate 		(void) setsockopt(sd, SOL_SOCKET, SO_DONTROUTE,
3500Sstevel@tonic-gate 		    (const void *)&dontroute, sizeof (dontroute));
3510Sstevel@tonic-gate 	}
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 	bzero(&daddr, sizeof (struct sockaddr_in));
3540Sstevel@tonic-gate 	daddr.sin_family = AF_INET;
3550Sstevel@tonic-gate 	daddr.sin_port = htons(IPPORT_BOOTPS);
3560Sstevel@tonic-gate 	daddr.sin_addr.s_addr = htonl(dest->s_addr);
3570Sstevel@tonic-gate 	wait_time = prom_gettime() + tol;
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate 	do {
3600Sstevel@tonic-gate 		if (sendto(sd, (char *)dhcp_snd_bufp, size, flags,
3610Sstevel@tonic-gate 		    (struct sockaddr *)&daddr, sizeof (daddr)) < 0) {
3620Sstevel@tonic-gate 			dprintf("%s: sendto failed with errno: %d\n",
3630Sstevel@tonic-gate 			    s_n, errno);
3640Sstevel@tonic-gate 			(void) socket_close(sd);
3650Sstevel@tonic-gate 			return (errno);
3660Sstevel@tonic-gate 		}
3670Sstevel@tonic-gate 		if (!tries)
3680Sstevel@tonic-gate 			break;   /* don't bother to check for reply */
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 		now = prom_gettime();
3710Sstevel@tonic-gate 		if (timeout == 0)
3720Sstevel@tonic-gate 			timeout = 4000;
3730Sstevel@tonic-gate 		else {
3740Sstevel@tonic-gate 			timeout <<= 1;
3750Sstevel@tonic-gate 			if (timeout > 64000)
3760Sstevel@tonic-gate 				timeout = 64000;
3770Sstevel@tonic-gate 		}
3780Sstevel@tonic-gate 		init_timeout = now + timeout;
3790Sstevel@tonic-gate 		wait_time = now + tol;
3800Sstevel@tonic-gate 		do {
3810Sstevel@tonic-gate 			if ((len = recvfrom(sd, (char *)dhcp_rcv_bufp,
3820Sstevel@tonic-gate 			    (int)dhcp_buf_size, MSG_DONTWAIT, NULL,
3830Sstevel@tonic-gate 			    NULL)) < 0) {
3840Sstevel@tonic-gate 				if (errno == EWOULDBLOCK)
3850Sstevel@tonic-gate 					continue;	/* DONT WAIT */
3860Sstevel@tonic-gate 				(void) socket_close(sd);
3870Sstevel@tonic-gate 				flush_list();
3880Sstevel@tonic-gate 				return (errno);
3890Sstevel@tonic-gate 			}
3900Sstevel@tonic-gate 
3910Sstevel@tonic-gate 			if (bootp_collect(len))
3920Sstevel@tonic-gate 				break;	/* Stop collecting */
3930Sstevel@tonic-gate 
3940Sstevel@tonic-gate 			if (tol != 0) {
3950Sstevel@tonic-gate 				if (wait_time < prom_gettime())
3960Sstevel@tonic-gate 					break; /* collection timeout */
3970Sstevel@tonic-gate 			}
3980Sstevel@tonic-gate 		} while (prom_gettime() < init_timeout);
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate 		if (bootp_success()) {
4010Sstevel@tonic-gate 			done = B_TRUE;
4020Sstevel@tonic-gate 			break;  /* got the goods */
4030Sstevel@tonic-gate 		}
4040Sstevel@tonic-gate 	} while (++attempts < tries);
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate 	(void) socket_close(sd);
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate 	return (done ? 0 : DHCP_NO_DATA);
4090Sstevel@tonic-gate }
4100Sstevel@tonic-gate 
4110Sstevel@tonic-gate /*
4120Sstevel@tonic-gate  * Print the message from the server.
4130Sstevel@tonic-gate  */
4140Sstevel@tonic-gate static void
prt_server_msg(DHCP_OPT * p)4150Sstevel@tonic-gate prt_server_msg(DHCP_OPT *p)
4160Sstevel@tonic-gate {
4170Sstevel@tonic-gate 	int len = p->len;
4180Sstevel@tonic-gate 	char scratch[DHCP_MAX_OPT_SIZE + 1];
4190Sstevel@tonic-gate 
420231Sjg 	if (len > DHCP_MAX_OPT_SIZE)
4210Sstevel@tonic-gate 		len = DHCP_MAX_OPT_SIZE;
4220Sstevel@tonic-gate 	bcopy(p->value, scratch, len);
4230Sstevel@tonic-gate 	scratch[len] = '\0';
4240Sstevel@tonic-gate 	printf("%s: Message from server: '%s'\n", s_n, scratch);
4250Sstevel@tonic-gate }
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate /*
4280Sstevel@tonic-gate  * This function scans the list of OFFERS, and returns the "best" offer.
4290Sstevel@tonic-gate  * The criteria used for determining this is:
4300Sstevel@tonic-gate  *
4310Sstevel@tonic-gate  * The best:
4320Sstevel@tonic-gate  * DHCP OFFER (not BOOTP), same client_id as ours, same class_id,
4330Sstevel@tonic-gate  * Longest lease, all the options we need.
4340Sstevel@tonic-gate  *
4350Sstevel@tonic-gate  * Not quite as good:
4360Sstevel@tonic-gate  * DHCP OFFER, no class_id, short lease, only some of the options we need.
4370Sstevel@tonic-gate  *
4380Sstevel@tonic-gate  * We're really reach'in
4390Sstevel@tonic-gate  * BOOTP reply.
4400Sstevel@tonic-gate  *
4410Sstevel@tonic-gate  * DON'T select an offer from a server that gave us a configuration we
4420Sstevel@tonic-gate  * couldn't use. Take this server off the "bad" list when this is done.
4430Sstevel@tonic-gate  * Next time, we could potentially retry this server's configuration.
4440Sstevel@tonic-gate  *
4450Sstevel@tonic-gate  * NOTE: perhaps this bad server should have a counter associated with it.
4460Sstevel@tonic-gate  */
4470Sstevel@tonic-gate static PKT_LIST *
select_best(void)4480Sstevel@tonic-gate select_best(void)
4490Sstevel@tonic-gate {
4500Sstevel@tonic-gate 	PKT_LIST	*wk, *tk, *best;
4510Sstevel@tonic-gate 	int		err = 0;
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate 	/* Pass one. Scan for options, set appropriate opt field. */
4540Sstevel@tonic-gate 	wk = list_hd;
4550Sstevel@tonic-gate 	while (wk != NULL) {
4560Sstevel@tonic-gate 		if ((err = dhcp_options_scan(wk, B_TRUE)) != 0) {
4570Sstevel@tonic-gate 			/* Garbled Options. Nuke this pkt. */
4580Sstevel@tonic-gate 			if (boothowto & RB_DEBUG) {
4590Sstevel@tonic-gate 				switch (err) {
4600Sstevel@tonic-gate 				case DHCP_WRONG_MSG_TYPE:
4610Sstevel@tonic-gate 					printf("%s: Unexpected DHCP message.\n",
4620Sstevel@tonic-gate 					    s_n);
4630Sstevel@tonic-gate 					break;
4640Sstevel@tonic-gate 				case DHCP_GARBLED_MSG_TYPE:
4650Sstevel@tonic-gate 					printf(
4660Sstevel@tonic-gate 					    "%s: Garbled DHCP message type.\n",
4670Sstevel@tonic-gate 					    s_n);
4680Sstevel@tonic-gate 					break;
4690Sstevel@tonic-gate 				case DHCP_BAD_OPT_OVLD:
4700Sstevel@tonic-gate 					printf("%s: Bad option overload.\n",
4710Sstevel@tonic-gate 					    s_n);
4720Sstevel@tonic-gate 					break;
4730Sstevel@tonic-gate 				}
4740Sstevel@tonic-gate 			}
4750Sstevel@tonic-gate 			tk = wk;
4760Sstevel@tonic-gate 			wk = wk->next;
4770Sstevel@tonic-gate 			remove_list(tk, B_TRUE);
4780Sstevel@tonic-gate 			continue;
4790Sstevel@tonic-gate 		}
4800Sstevel@tonic-gate 		wk = wk->next;
4810Sstevel@tonic-gate 	}
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate 	/*
4840Sstevel@tonic-gate 	 * Pass two. Pick out the best offer. Point system.
4850Sstevel@tonic-gate 	 * What's important?
4860Sstevel@tonic-gate 	 *	0) DHCP
4870Sstevel@tonic-gate 	 *	1) No option overload
4880Sstevel@tonic-gate 	 *	2) Encapsulated vendor option
4890Sstevel@tonic-gate 	 *	3) Non-null sname and siaddr fields
4900Sstevel@tonic-gate 	 *	4) Non-null file field
4910Sstevel@tonic-gate 	 *	5) Hostname
4920Sstevel@tonic-gate 	 *	6) Subnetmask
4930Sstevel@tonic-gate 	 *	7) Router
4940Sstevel@tonic-gate 	 */
4950Sstevel@tonic-gate 	best = NULL;
4960Sstevel@tonic-gate 	for (wk = list_hd; wk != NULL; wk = wk->next) {
4970Sstevel@tonic-gate 		wk->offset = 0;
4980Sstevel@tonic-gate 		if (wk->opts[CD_DHCP_TYPE] &&
4990Sstevel@tonic-gate 		    wk->opts[CD_DHCP_TYPE]->len == 1) {
5000Sstevel@tonic-gate 			if (*wk->opts[CD_DHCP_TYPE]->value != OFFER) {
5010Sstevel@tonic-gate 				dprintf("%s: Unexpected DHCP message."
5020Sstevel@tonic-gate 				    " Expected OFFER message.\n", s_n);
5030Sstevel@tonic-gate 				continue;
5040Sstevel@tonic-gate 			}
5050Sstevel@tonic-gate 			if (!wk->opts[CD_LEASE_TIME]) {
5060Sstevel@tonic-gate 				dprintf("%s: DHCP OFFER message without lease "
5070Sstevel@tonic-gate 				    "time parameter.\n", s_n);
5080Sstevel@tonic-gate 				continue;
5090Sstevel@tonic-gate 			} else {
5100Sstevel@tonic-gate 				if (wk->opts[CD_LEASE_TIME]->len != 4) {
5110Sstevel@tonic-gate 					dprintf("%s: Lease expiration time is "
5120Sstevel@tonic-gate 					    "garbled.\n", s_n);
5130Sstevel@tonic-gate 					continue;
5140Sstevel@tonic-gate 				}
5150Sstevel@tonic-gate 			}
5160Sstevel@tonic-gate 			if (!wk->opts[CD_SERVER_ID]) {
5170Sstevel@tonic-gate 				dprintf("%s: DHCP OFFER message without server "
5180Sstevel@tonic-gate 				    "id parameter.\n", s_n);
5190Sstevel@tonic-gate 				continue;
5200Sstevel@tonic-gate 			} else {
5210Sstevel@tonic-gate 				if (wk->opts[CD_SERVER_ID]->len != 4) {
5220Sstevel@tonic-gate 					dprintf("%s: Server identifier "
5230Sstevel@tonic-gate 					    "parameter is garbled.\n", s_n);
5240Sstevel@tonic-gate 					continue;
5250Sstevel@tonic-gate 				}
5260Sstevel@tonic-gate 			}
5270Sstevel@tonic-gate 			/* Valid DHCP OFFER. See if we got our parameters. */
5280Sstevel@tonic-gate 			dprintf("%s: Found valid DHCP OFFER message.\n", s_n);
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 			wk->offset += 30;
5310Sstevel@tonic-gate 
5320Sstevel@tonic-gate 			/*
5330Sstevel@tonic-gate 			 * Also could be faked, though more difficult
5340Sstevel@tonic-gate 			 * because the encapsulation is hard to encode
5350Sstevel@tonic-gate 			 * on a BOOTP server; plus there's not as much
5360Sstevel@tonic-gate 			 * real estate in the packet for options, so
5370Sstevel@tonic-gate 			 * it's likely this option would get dropped.
5380Sstevel@tonic-gate 			 */
5390Sstevel@tonic-gate 			if (wk->opts[CD_VENDOR_SPEC])
5400Sstevel@tonic-gate 				wk->offset += 80;
5410Sstevel@tonic-gate 		} else
5420Sstevel@tonic-gate 			dprintf("%s: Found valid BOOTP reply.\n", s_n);
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 		/*
5450Sstevel@tonic-gate 		 * RFC1048 BOOTP?
5460Sstevel@tonic-gate 		 */
5470Sstevel@tonic-gate 		if (bcmp((caddr_t)wk->pkt->cookie, (caddr_t)magic,
5480Sstevel@tonic-gate 		    sizeof (magic)) == 0) {
5490Sstevel@tonic-gate 			wk->offset += 5;
5500Sstevel@tonic-gate 			if (wk->opts[CD_SUBNETMASK])
5510Sstevel@tonic-gate 				wk->offset++;
5520Sstevel@tonic-gate 			if (wk->opts[CD_ROUTER])
5530Sstevel@tonic-gate 				wk->offset++;
5540Sstevel@tonic-gate 			if (wk->opts[CD_HOSTNAME])
5550Sstevel@tonic-gate 				wk->offset += 5;
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 			/*
5580Sstevel@tonic-gate 			 * Prefer options that have diskless boot significance
5590Sstevel@tonic-gate 			 */
5600Sstevel@tonic-gate 			if (ntohl(wk->pkt->siaddr.s_addr) != INADDR_ANY)
5610Sstevel@tonic-gate 				wk->offset += 10; /* server ip */
5620Sstevel@tonic-gate 			if (wk->opts[CD_OPTION_OVERLOAD] == NULL) {
5630Sstevel@tonic-gate 				if (wk->pkt->sname[0] != '\0')
5640Sstevel@tonic-gate 					wk->offset += 10; /* server name */
5650Sstevel@tonic-gate 				if (wk->pkt->file[0] != '\0')
5660Sstevel@tonic-gate 					wk->offset += 5; /* File to load */
5670Sstevel@tonic-gate 			}
5680Sstevel@tonic-gate 		}
5690Sstevel@tonic-gate #ifdef	DHCP_DEBUG
5700Sstevel@tonic-gate 		printf("%s: This server configuration has '%d' points.\n", s_n,
571*6747Sga159272 		    wk->offset);
5720Sstevel@tonic-gate #endif	/* DHCP_DEBUG */
5730Sstevel@tonic-gate 		if (!best)
5740Sstevel@tonic-gate 			best = wk;
5750Sstevel@tonic-gate 		else {
5760Sstevel@tonic-gate 			if (best->offset < wk->offset)
5770Sstevel@tonic-gate 				best = wk;
5780Sstevel@tonic-gate 		}
5790Sstevel@tonic-gate 	}
5800Sstevel@tonic-gate 	if (best) {
5810Sstevel@tonic-gate #ifdef	DHCP_DEBUG
5820Sstevel@tonic-gate 		printf("%s: Found best: points: %d\n", s_n, best->offset);
5830Sstevel@tonic-gate #endif	/* DHCP_DEBUG */
5840Sstevel@tonic-gate 		remove_list(best, B_FALSE);
5850Sstevel@tonic-gate 	} else {
5860Sstevel@tonic-gate 		dprintf("%s: No valid BOOTP reply or DHCP OFFER was found.\n",
5870Sstevel@tonic-gate 		    s_n);
5880Sstevel@tonic-gate 	}
5890Sstevel@tonic-gate 	flush_list();	/* toss the remaining list */
5900Sstevel@tonic-gate 	return (best);
5910Sstevel@tonic-gate }
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate /*
5940Sstevel@tonic-gate  * Send a decline message to the generator of the DHCPACK.
5950Sstevel@tonic-gate  */
5960Sstevel@tonic-gate static void
dhcp_decline(char * msg,PKT_LIST * pl)5970Sstevel@tonic-gate dhcp_decline(char *msg, PKT_LIST *pl)
5980Sstevel@tonic-gate {
5990Sstevel@tonic-gate 	PKT		*pkt;
6000Sstevel@tonic-gate 	uint8_t		*opt, ulen;
6010Sstevel@tonic-gate 	int		pkt_size;
6020Sstevel@tonic-gate 	struct in_addr	nets, ours, t_server, t_yiaddr;
6030Sstevel@tonic-gate 
6040Sstevel@tonic-gate 	if (pl != NULL) {
6050Sstevel@tonic-gate 		if (pl->opts[CD_SERVER_ID] != NULL &&
6060Sstevel@tonic-gate 		    pl->opts[CD_SERVER_ID]->len == sizeof (struct in_addr)) {
6070Sstevel@tonic-gate 			bcopy(pl->opts[CD_SERVER_ID]->value,
6080Sstevel@tonic-gate 			    &t_server, pl->opts[CD_SERVER_ID]->len);
6090Sstevel@tonic-gate 		} else
6100Sstevel@tonic-gate 			t_server.s_addr = htonl(INADDR_BROADCAST);
6110Sstevel@tonic-gate 		t_yiaddr.s_addr = pl->pkt->yiaddr.s_addr;
6120Sstevel@tonic-gate 	}
6130Sstevel@tonic-gate 	pkt = (PKT *)dhcp_snd_bufp;
6140Sstevel@tonic-gate 	opt = init_msg(pkt, opt_decline);
6150Sstevel@tonic-gate 	set_hw_spec_data(pkt, &opt, opt_decline);
6160Sstevel@tonic-gate 
6170Sstevel@tonic-gate 	*opt++ = CD_SERVER_ID;
6180Sstevel@tonic-gate 	*opt++ = sizeof (struct in_addr);
6190Sstevel@tonic-gate 	bcopy(&t_server, opt, sizeof (struct in_addr));
6200Sstevel@tonic-gate 	opt += sizeof (struct in_addr);
6210Sstevel@tonic-gate 	nets.s_addr = htonl(INADDR_BROADCAST);
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate 	/*
6240Sstevel@tonic-gate 	 * Use the given yiaddr in our ciaddr field so server can identify us.
6250Sstevel@tonic-gate 	 */
6260Sstevel@tonic-gate 	pkt->ciaddr.s_addr = t_yiaddr.s_addr;
6270Sstevel@tonic-gate 
6280Sstevel@tonic-gate 	ipv4_getipaddr(&ours);	/* Could be 0, could be yiaddr */
6290Sstevel@tonic-gate 	ours.s_addr = htonl(ours.s_addr);
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate 	ulen = (uint8_t)strlen(msg);
6320Sstevel@tonic-gate 	*opt++ = CD_MESSAGE;
6330Sstevel@tonic-gate 	*opt++ = ulen;
6340Sstevel@tonic-gate 	bcopy(msg, opt, ulen);
6350Sstevel@tonic-gate 	opt += ulen;
6360Sstevel@tonic-gate 	*opt++ = CD_END;
6370Sstevel@tonic-gate 
6380Sstevel@tonic-gate 	pkt_size = (uint8_t *)opt - (uint8_t *)pkt;
6390Sstevel@tonic-gate 	if (pkt_size < sizeof (PKT))
6400Sstevel@tonic-gate 		pkt_size = sizeof (PKT);
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate 	timeout = 0;	/* reset timeout */
6430Sstevel@tonic-gate 	(void) inet(pkt_size, &ours, &nets, 0, 0L);
6440Sstevel@tonic-gate }
6450Sstevel@tonic-gate 
6460Sstevel@tonic-gate /*
6470Sstevel@tonic-gate  * Implementings SELECTING state of DHCP client state machine.
6480Sstevel@tonic-gate  */
6490Sstevel@tonic-gate static int
dhcp_selecting(void)6500Sstevel@tonic-gate dhcp_selecting(void)
6510Sstevel@tonic-gate {
6520Sstevel@tonic-gate 	int		pkt_size;
6530Sstevel@tonic-gate 	PKT		*pkt;
6540Sstevel@tonic-gate 	uint8_t		*opt;
6550Sstevel@tonic-gate 	uint16_t	size;
6560Sstevel@tonic-gate 	uint32_t	lease;
6570Sstevel@tonic-gate 	struct in_addr	nets, ours;
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate 	pkt = (PKT *)dhcp_snd_bufp;
6600Sstevel@tonic-gate 	opt = init_msg(pkt, opt_discover);
6610Sstevel@tonic-gate 	pkt->secs = htons((uint16_t)((prom_gettime() - dhcp_start_time)/1000));
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 	*opt++ = CD_MAX_DHCP_SIZE;
6640Sstevel@tonic-gate 	*opt++ = sizeof (size);
6650Sstevel@tonic-gate 	size = (uint16_t)(dhcp_buf_size - sizeof (struct ip) -
6660Sstevel@tonic-gate 	    sizeof (struct udphdr));
6670Sstevel@tonic-gate 	size = htons(size);
6680Sstevel@tonic-gate 	bcopy(&size, opt, sizeof (size));
6690Sstevel@tonic-gate 	opt += sizeof (size);
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate 	set_hw_spec_data(pkt, &opt, opt_discover);
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate 	*opt++ = CD_LEASE_TIME;
6740Sstevel@tonic-gate 	*opt++ = sizeof (lease);
6750Sstevel@tonic-gate 	lease = htonl(DHCP_PERM);	/* ask for a permanent lease */
6760Sstevel@tonic-gate 	bcopy(&lease, opt, sizeof (lease));
6770Sstevel@tonic-gate 	opt += sizeof (lease);
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate 	/*
6800Sstevel@tonic-gate 	 * If a clientid was set using dhcp_set_client_id(), add this
6810Sstevel@tonic-gate 	 * to the options.
6820Sstevel@tonic-gate 	 */
6830Sstevel@tonic-gate 	if (dhcp_clientid_len > 0) {
6840Sstevel@tonic-gate 		*opt++ = CD_CLIENT_ID;
6850Sstevel@tonic-gate 		*opt++ = dhcp_clientid_len;
6860Sstevel@tonic-gate 		bcopy(dhcp_clientid, opt, dhcp_clientid_len);
6870Sstevel@tonic-gate 		opt += dhcp_clientid_len;
6880Sstevel@tonic-gate 	}
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate 	parameter_request_list(&opt);
6910Sstevel@tonic-gate 
6920Sstevel@tonic-gate 	*opt++ = CD_END;
6930Sstevel@tonic-gate 
6940Sstevel@tonic-gate 	pkt_size = (uint8_t *)opt - (uint8_t *)pkt;
6950Sstevel@tonic-gate 	if (pkt_size < sizeof (PKT))
6960Sstevel@tonic-gate 		pkt_size = sizeof (PKT);
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate 	nets.s_addr = INADDR_BROADCAST;
6990Sstevel@tonic-gate 	ours.s_addr = INADDR_ANY;
7000Sstevel@tonic-gate 	timeout = 0;	/* reset timeout */
7010Sstevel@tonic-gate 
7020Sstevel@tonic-gate 	return (inet(pkt_size, &ours, &nets, DHCP_RETRIES, DHCP_WAIT));
7030Sstevel@tonic-gate }
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate /*
7060Sstevel@tonic-gate  * implements the REQUESTING state of the DHCP client state machine.
7070Sstevel@tonic-gate  */
7080Sstevel@tonic-gate static int
dhcp_requesting(void)7090Sstevel@tonic-gate dhcp_requesting(void)
7100Sstevel@tonic-gate {
7110Sstevel@tonic-gate 	PKT_LIST	*pl, *wk;
7120Sstevel@tonic-gate 	PKT		*pkt, *pl_pkt;
7130Sstevel@tonic-gate 	uint8_t		*opt;
7140Sstevel@tonic-gate 	int		pkt_size, err;
7150Sstevel@tonic-gate 	uint32_t	t_time;
7160Sstevel@tonic-gate 	struct in_addr	nets, ours;
7170Sstevel@tonic-gate 	DHCP_OPT	*doptp;
7180Sstevel@tonic-gate 	uint16_t	size;
7190Sstevel@tonic-gate 
7200Sstevel@tonic-gate 	/* here we scan the list of OFFERS, select the best one. */
7210Sstevel@tonic-gate 	state_pl = NULL;
7220Sstevel@tonic-gate 
7230Sstevel@tonic-gate 	if ((pl = select_best()) == NULL) {
7240Sstevel@tonic-gate 		dprintf("%s: No valid BOOTP reply or DHCP OFFER was found.\n",
7250Sstevel@tonic-gate 		    s_n);
7260Sstevel@tonic-gate 		return (1);
7270Sstevel@tonic-gate 	}
7280Sstevel@tonic-gate 
7290Sstevel@tonic-gate 	pl_pkt = pl->pkt;
7300Sstevel@tonic-gate 
7310Sstevel@tonic-gate 	/*
7320Sstevel@tonic-gate 	 * Check to see if we got an OFFER pkt(s). If not, then We only got
7330Sstevel@tonic-gate 	 * a response from a BOOTP server. We'll go to the bound state and
7340Sstevel@tonic-gate 	 * try to use that configuration.
7350Sstevel@tonic-gate 	 */
7360Sstevel@tonic-gate 	if (pl->opts[CD_DHCP_TYPE] == NULL) {
7370Sstevel@tonic-gate 		if (mac_call_arp(&pl_pkt->yiaddr, NULL, DHCP_ARP_TIMEOUT)) {
7380Sstevel@tonic-gate 			/* Someone responded! go back to SELECTING state. */
7390Sstevel@tonic-gate 			printf("%s: Some host already using BOOTP %s.\n", s_n,
7400Sstevel@tonic-gate 			    inet_ntoa(pl_pkt->yiaddr));
7410Sstevel@tonic-gate 			bkmem_free((char *)pl->pkt, pl->len);
7420Sstevel@tonic-gate 			bkmem_free((char *)pl, sizeof (PKT_LIST));
7430Sstevel@tonic-gate 			return (1);
7440Sstevel@tonic-gate 		}
7450Sstevel@tonic-gate 		state_pl = pl;
7460Sstevel@tonic-gate 		return (0);
7470Sstevel@tonic-gate 	}
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate 	/*
7500Sstevel@tonic-gate 	 * if we got a message from the server, display it.
7510Sstevel@tonic-gate 	 */
7520Sstevel@tonic-gate 	if (pl->opts[CD_MESSAGE])
7530Sstevel@tonic-gate 		prt_server_msg(pl->opts[CD_MESSAGE]);
7540Sstevel@tonic-gate 	/*
7550Sstevel@tonic-gate 	 * Assemble a DHCPREQUEST, with the ciaddr field set to 0, since we
7560Sstevel@tonic-gate 	 * got here from DISCOVER state. Keep secs field the same for relay
7570Sstevel@tonic-gate 	 * agents. We start with the DHCPOFFER packet we got, and the
7580Sstevel@tonic-gate 	 * options contained in it to make a requested option list.
7590Sstevel@tonic-gate 	 */
7600Sstevel@tonic-gate 	pkt = (PKT *)dhcp_snd_bufp;
7610Sstevel@tonic-gate 	opt = init_msg(pkt, opt_request);
7620Sstevel@tonic-gate 
7630Sstevel@tonic-gate 	/* Time from Successful DISCOVER message. */
7640Sstevel@tonic-gate 	pkt->secs = htons((uint16_t)dhcp_secs);
7650Sstevel@tonic-gate 
7660Sstevel@tonic-gate 	size = (uint16_t)(dhcp_buf_size - sizeof (struct ip) -
7670Sstevel@tonic-gate 	    sizeof (struct udphdr));
7680Sstevel@tonic-gate 	size = htons(size);
7690Sstevel@tonic-gate 	*opt++ = CD_MAX_DHCP_SIZE;
7700Sstevel@tonic-gate 	*opt++ = sizeof (size);
7710Sstevel@tonic-gate 	bcopy(&size, opt, sizeof (size));
7720Sstevel@tonic-gate 	opt += sizeof (size);
7730Sstevel@tonic-gate 
7740Sstevel@tonic-gate 	set_hw_spec_data(pkt, &opt, opt_request);
7750Sstevel@tonic-gate 	*opt++ = CD_SERVER_ID;
7760Sstevel@tonic-gate 	*opt++ = pl->opts[CD_SERVER_ID]->len;
7770Sstevel@tonic-gate 	bcopy(pl->opts[CD_SERVER_ID]->value, opt,
7780Sstevel@tonic-gate 	    pl->opts[CD_SERVER_ID]->len);
7790Sstevel@tonic-gate 	opt += pl->opts[CD_SERVER_ID]->len;
7800Sstevel@tonic-gate 
7810Sstevel@tonic-gate 	/* our offered lease minus boot time */
7820Sstevel@tonic-gate 	*opt++ = CD_LEASE_TIME;
7830Sstevel@tonic-gate 	*opt++ = 4;
7840Sstevel@tonic-gate 	bcopy(pl->opts[CD_LEASE_TIME]->value, &t_time,
7850Sstevel@tonic-gate 	    sizeof (t_time));
7860Sstevel@tonic-gate 	t_time = ntohl(t_time);
7870Sstevel@tonic-gate 	if ((uint32_t)t_time != DHCP_PERM)
7880Sstevel@tonic-gate 		t_time -= (uint32_t)dhcp_secs;
7890Sstevel@tonic-gate 	t_time = htonl(t_time);
7900Sstevel@tonic-gate 	bcopy(&t_time, opt, sizeof (t_time));
7910Sstevel@tonic-gate 	opt += sizeof (t_time);
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 	/* our offered IP address, as required. */
7940Sstevel@tonic-gate 	*opt++ = CD_REQUESTED_IP_ADDR;
7950Sstevel@tonic-gate 	*opt++ = sizeof (struct in_addr);
7960Sstevel@tonic-gate 	bcopy(&pl_pkt->yiaddr, opt, sizeof (struct in_addr));
7970Sstevel@tonic-gate 	opt += sizeof (struct in_addr);
7980Sstevel@tonic-gate 
7990Sstevel@tonic-gate 	/*
8000Sstevel@tonic-gate 	 * If a clientid was set using dhcp_set_client_id(), add this
8010Sstevel@tonic-gate 	 * to the options.
8020Sstevel@tonic-gate 	 */
8030Sstevel@tonic-gate 	if (dhcp_clientid_len > 0) {
8040Sstevel@tonic-gate 		*opt++ = CD_CLIENT_ID;
8050Sstevel@tonic-gate 		*opt++ = dhcp_clientid_len;
8060Sstevel@tonic-gate 		bcopy(dhcp_clientid, opt, dhcp_clientid_len);
8070Sstevel@tonic-gate 		opt += dhcp_clientid_len;
8080Sstevel@tonic-gate 	}
8090Sstevel@tonic-gate 
8100Sstevel@tonic-gate 	parameter_request_list(&opt);
8110Sstevel@tonic-gate 
8120Sstevel@tonic-gate 	*opt++ = CD_END;
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate 	/* Done with the OFFER pkt. */
8150Sstevel@tonic-gate 	bkmem_free((char *)pl->pkt, pl->len);
8160Sstevel@tonic-gate 	bkmem_free((char *)pl, sizeof (PKT_LIST));
8170Sstevel@tonic-gate 
8180Sstevel@tonic-gate 	/*
8190Sstevel@tonic-gate 	 * We make 4 attempts here. We wait for 2 seconds to accumulate
8200Sstevel@tonic-gate 	 * requests, just in case a BOOTP server is too fast!
8210Sstevel@tonic-gate 	 */
8220Sstevel@tonic-gate 	pkt_size = (uint8_t *)opt - (uint8_t *)pkt;
8230Sstevel@tonic-gate 	if (pkt_size < sizeof (PKT))
8240Sstevel@tonic-gate 		pkt_size = sizeof (PKT);
8250Sstevel@tonic-gate 
8260Sstevel@tonic-gate 	nets.s_addr = INADDR_BROADCAST;
8270Sstevel@tonic-gate 	ours.s_addr = INADDR_ANY;
8280Sstevel@tonic-gate 	timeout = 0;	/* reset timeout */
8290Sstevel@tonic-gate 
8300Sstevel@tonic-gate 	if ((err = inet(pkt_size, &ours, &nets, 4, (time_t)2L)) != 0)
8310Sstevel@tonic-gate 		return (err);
8320Sstevel@tonic-gate 	for (wk = list_hd; wk != NULL && state_pl == NULL; wk = wk->next) {
8330Sstevel@tonic-gate 		if (dhcp_options_scan(wk, B_TRUE) != 0 ||
8340Sstevel@tonic-gate 		    !wk->opts[CD_DHCP_TYPE])
8350Sstevel@tonic-gate 			continue;	/* garbled options */
8360Sstevel@tonic-gate 		switch (*wk->opts[CD_DHCP_TYPE]->value) {
8370Sstevel@tonic-gate 		case ACK:
8380Sstevel@tonic-gate 			remove_list(wk, B_FALSE);
8390Sstevel@tonic-gate 			state_pl = wk;
8400Sstevel@tonic-gate 			break;
8410Sstevel@tonic-gate 		case NAK:
8420Sstevel@tonic-gate 			printf("%s: rejected by DHCP server: %s\n",
8430Sstevel@tonic-gate 			    s_n, inet_ntoa(*((struct in_addr *)wk->
8440Sstevel@tonic-gate 			    opts[CD_SERVER_ID]->value)));
8450Sstevel@tonic-gate 			if (wk->opts[CD_MESSAGE])
8460Sstevel@tonic-gate 				prt_server_msg(wk->opts[CD_MESSAGE]);
8470Sstevel@tonic-gate 			break;
8480Sstevel@tonic-gate 		default:
8490Sstevel@tonic-gate 			dprintf("%s: Unexpected DHCP message type.\n", s_n);
8500Sstevel@tonic-gate 			break;
8510Sstevel@tonic-gate 		}
8520Sstevel@tonic-gate 	}
8530Sstevel@tonic-gate 	flush_list();
8540Sstevel@tonic-gate 	if (state_pl != NULL) {
8550Sstevel@tonic-gate 		if (state_pl->opts[CD_MESSAGE])
8560Sstevel@tonic-gate 			prt_server_msg(state_pl->opts[CD_MESSAGE]);
8570Sstevel@tonic-gate 		pl_pkt = state_pl->pkt;
8580Sstevel@tonic-gate 		/* arp our new address, just to make sure */
8590Sstevel@tonic-gate 		if (!mac_call_arp(&pl_pkt->yiaddr, NULL, DHCP_ARP_TIMEOUT)) {
8600Sstevel@tonic-gate 			/*
8610Sstevel@tonic-gate 			 * No response. Check if lease is ok.
8620Sstevel@tonic-gate 			 */
8630Sstevel@tonic-gate 			doptp = state_pl->opts[CD_LEASE_TIME];
8640Sstevel@tonic-gate 			if (state_pl->opts[CD_DHCP_TYPE] && (!doptp ||
8650Sstevel@tonic-gate 			    (doptp->len % 4) != 0)) {
8660Sstevel@tonic-gate 				dhcp_decline("Missing or corrupted lease time",
8670Sstevel@tonic-gate 				    state_pl);
8680Sstevel@tonic-gate 				err++;
8690Sstevel@tonic-gate 			}
8700Sstevel@tonic-gate 		} else {
8710Sstevel@tonic-gate 			dhcp_decline("IP Address already being used!",
8720Sstevel@tonic-gate 			    state_pl);
8730Sstevel@tonic-gate 			err++;
8740Sstevel@tonic-gate 		}
8750Sstevel@tonic-gate 		if (err) {
8760Sstevel@tonic-gate 			bkmem_free((char *)state_pl->pkt, state_pl->len);
8770Sstevel@tonic-gate 			bkmem_free((char *)state_pl, sizeof (PKT_LIST));
8780Sstevel@tonic-gate 			state_pl = NULL;
8790Sstevel@tonic-gate 		} else
8800Sstevel@tonic-gate 			return (0);	/* passes the tests! */
8810Sstevel@tonic-gate 	}
8820Sstevel@tonic-gate 	dprintf("%s: No valid DHCP acknowledge messages received.\n", s_n);
8830Sstevel@tonic-gate 	return (1);
8840Sstevel@tonic-gate }
8850Sstevel@tonic-gate 
8860Sstevel@tonic-gate /*
8870Sstevel@tonic-gate  * Implements BOUND state of DHCP client state machine.
8880Sstevel@tonic-gate  */
8890Sstevel@tonic-gate static int
dhcp_bound(void)8900Sstevel@tonic-gate dhcp_bound(void)
8910Sstevel@tonic-gate {
8920Sstevel@tonic-gate 	PKT		*pl_pkt = state_pl->pkt;
8930Sstevel@tonic-gate 	DHCP_OPT	*doptp;
8940Sstevel@tonic-gate 	uint8_t		*tp, *hp;
8950Sstevel@tonic-gate 	int		items, i, fnd, k;
8960Sstevel@tonic-gate 	char		hostname[MAXHOSTNAMELEN+1];
8970Sstevel@tonic-gate 	struct in_addr	subnet, defr, savr, *ipp, xip;
8980Sstevel@tonic-gate 
8990Sstevel@tonic-gate #ifdef	DHCP_DEBUG
9000Sstevel@tonic-gate 	if (dhcp_classid[0] != 0) {
9010Sstevel@tonic-gate 		char 	scratch[DHCP_MAX_OPT_SIZE + 1];
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 		bcopy(&dhcp_classid[2], scratch, dhcp_classid[1]);
9040Sstevel@tonic-gate 		scratch[dhcp_classid[1]] = '\0';
9050Sstevel@tonic-gate 		printf("Your machine is of the class: '%s'.\n", scratch);
9060Sstevel@tonic-gate 	}
9070Sstevel@tonic-gate #endif	/* DHCP_DEBUG */
9080Sstevel@tonic-gate 
9090Sstevel@tonic-gate 	/* First, set the bare essentials. (IP layer parameters) */
9100Sstevel@tonic-gate 
9110Sstevel@tonic-gate 	ipv4_getipaddr(&xip);
9120Sstevel@tonic-gate 	if (xip.s_addr != INADDR_ANY)
9130Sstevel@tonic-gate 		ipp = &xip;
9140Sstevel@tonic-gate 	else {
9150Sstevel@tonic-gate 		ipp = &pl_pkt->yiaddr;
9160Sstevel@tonic-gate 		ipp->s_addr = ntohl(ipp->s_addr);
9170Sstevel@tonic-gate 		ipv4_setipaddr(ipp);
9180Sstevel@tonic-gate 	}
9190Sstevel@tonic-gate 
9200Sstevel@tonic-gate 	ipp->s_addr = htonl(ipp->s_addr);
9210Sstevel@tonic-gate 
9220Sstevel@tonic-gate 	if (boothowto & RB_VERBOSE)
9230Sstevel@tonic-gate 		printf("%s: IP address is: %s\n", s_n, inet_ntoa(*ipp));
9240Sstevel@tonic-gate 
9250Sstevel@tonic-gate 	/*
9260Sstevel@tonic-gate 	 * Ensure that the Boot NFS READ size, if given, is an int16_t.
9270Sstevel@tonic-gate 	 */
9280Sstevel@tonic-gate 	if (state_pl->vs[VS_BOOT_NFS_READSIZE] != NULL) {
9290Sstevel@tonic-gate 		doptp = state_pl->vs[VS_BOOT_NFS_READSIZE];
9300Sstevel@tonic-gate 		if (doptp->len != sizeof (int16_t))
9310Sstevel@tonic-gate 			state_pl->vs[VS_BOOT_NFS_READSIZE] = NULL;
9320Sstevel@tonic-gate 	}
9330Sstevel@tonic-gate 
9340Sstevel@tonic-gate 	/*
9350Sstevel@tonic-gate 	 * Set subnetmask. Nice, but not required.
9360Sstevel@tonic-gate 	 */
9370Sstevel@tonic-gate 	if (state_pl->opts[CD_SUBNETMASK] != NULL) {
9380Sstevel@tonic-gate 		doptp = state_pl->opts[CD_SUBNETMASK];
9390Sstevel@tonic-gate 		if (doptp->len != 4)
9400Sstevel@tonic-gate 			state_pl->opts[CD_SUBNETMASK] = NULL;
9410Sstevel@tonic-gate 		else {
9420Sstevel@tonic-gate 			bcopy(doptp->value, &subnet,
9430Sstevel@tonic-gate 			    sizeof (struct in_addr));
9440Sstevel@tonic-gate 			dprintf("%s: Setting netmask to: %s\n", s_n,
9450Sstevel@tonic-gate 			    inet_ntoa(subnet));
9460Sstevel@tonic-gate 			subnet.s_addr = ntohl(subnet.s_addr);
9470Sstevel@tonic-gate 			ipv4_setnetmask(&subnet);
9480Sstevel@tonic-gate 		}
9490Sstevel@tonic-gate 	}
9500Sstevel@tonic-gate 
9510Sstevel@tonic-gate 	/*
9520Sstevel@tonic-gate 	 * Set default IP TTL. Nice, but not required.
9530Sstevel@tonic-gate 	 */
9540Sstevel@tonic-gate 	if (state_pl->opts[CD_IPTTL] != NULL) {
9550Sstevel@tonic-gate 		doptp = state_pl->opts[CD_IPTTL];
9560Sstevel@tonic-gate 		if (doptp->len > 1)
9570Sstevel@tonic-gate 			state_pl->opts[CD_IPTTL] = NULL;
9580Sstevel@tonic-gate 		else {
9590Sstevel@tonic-gate 			doptp = state_pl->opts[CD_IPTTL];
9600Sstevel@tonic-gate 			ipv4_setmaxttl(*(uint8_t *)doptp->value);
9610Sstevel@tonic-gate 			dprintf("%s: Setting IP TTL to: %d\n", s_n,
9620Sstevel@tonic-gate 			    *(uint8_t *)doptp->value);
9630Sstevel@tonic-gate 		}
9640Sstevel@tonic-gate 	}
9650Sstevel@tonic-gate 
9660Sstevel@tonic-gate 	/*
9670Sstevel@tonic-gate 	 * Set default router. Not required, although we'll know soon
9680Sstevel@tonic-gate 	 * enough...
9690Sstevel@tonic-gate 	 */
9700Sstevel@tonic-gate 	if (state_pl->opts[CD_ROUTER] != NULL) {
9710Sstevel@tonic-gate 		doptp = state_pl->opts[CD_ROUTER];
9720Sstevel@tonic-gate 		if ((doptp->len % 4) != 0) {
9730Sstevel@tonic-gate 			state_pl->opts[CD_ROUTER] = NULL;
9740Sstevel@tonic-gate 		} else {
9750Sstevel@tonic-gate 			if ((hp = (uint8_t *)bkmem_alloc(
9760Sstevel@tonic-gate 			    mac_get_hdr_len())) == NULL) {
9770Sstevel@tonic-gate 				errno = ENOMEM;
9780Sstevel@tonic-gate 				return (-1);
9790Sstevel@tonic-gate 			}
9800Sstevel@tonic-gate 			items = doptp->len / sizeof (struct in_addr);
9810Sstevel@tonic-gate 			tp = doptp->value;
9820Sstevel@tonic-gate 			bcopy(tp, &savr, sizeof (struct in_addr));
9830Sstevel@tonic-gate 			for (i = 0, fnd = 0; i < items; i++) {
9840Sstevel@tonic-gate 				bcopy(tp, &defr, sizeof (struct in_addr));
9850Sstevel@tonic-gate 				for (k = 0, fnd = 0; k < 2 && fnd == 0; k++) {
9860Sstevel@tonic-gate 					fnd = mac_get_arp(&defr, hp,
9870Sstevel@tonic-gate 					    mac_get_hdr_len(),
9880Sstevel@tonic-gate 					    mac_get_arp_timeout());
9890Sstevel@tonic-gate 				}
9900Sstevel@tonic-gate 				if (fnd)
9910Sstevel@tonic-gate 					break;
9920Sstevel@tonic-gate 				dprintf(
9930Sstevel@tonic-gate 				    "%s: Warning: Router %s is unreachable.\n",
9940Sstevel@tonic-gate 				    s_n, inet_ntoa(defr));
9950Sstevel@tonic-gate 				tp += sizeof (struct in_addr);
9960Sstevel@tonic-gate 			}
9970Sstevel@tonic-gate 			bkmem_free((char *)hp, mac_get_hdr_len());
9980Sstevel@tonic-gate 
9990Sstevel@tonic-gate 			/*
10000Sstevel@tonic-gate 			 * If fnd is 0, we didn't find a working router. We'll
10010Sstevel@tonic-gate 			 * still try to use first default router. If we got
10020Sstevel@tonic-gate 			 * a bad router address (like not on the same net),
10030Sstevel@tonic-gate 			 * we're hosed anyway.
10040Sstevel@tonic-gate 			 */
10050Sstevel@tonic-gate 			if (!fnd) {
10060Sstevel@tonic-gate 				dprintf(
10070Sstevel@tonic-gate 				    "%s: Warning: Using default router: %s\n",
10080Sstevel@tonic-gate 				    s_n, inet_ntoa(savr));
10090Sstevel@tonic-gate 				defr.s_addr = savr.s_addr;
10100Sstevel@tonic-gate 			}
10110Sstevel@tonic-gate 			/* ipv4_route expects network order IP addresses */
10120Sstevel@tonic-gate 			(void) ipv4_route(IPV4_ADD_ROUTE, RT_DEFAULT, NULL,
10130Sstevel@tonic-gate 			    &defr);
10140Sstevel@tonic-gate 		}
10150Sstevel@tonic-gate 	}
10160Sstevel@tonic-gate 
10170Sstevel@tonic-gate 	/*
10180Sstevel@tonic-gate 	 * Set hostname. Not required.
10190Sstevel@tonic-gate 	 */
10200Sstevel@tonic-gate 	if (state_pl->opts[CD_HOSTNAME] != NULL) {
10210Sstevel@tonic-gate 		doptp = state_pl->opts[CD_HOSTNAME];
10220Sstevel@tonic-gate 		i = doptp->len;
10230Sstevel@tonic-gate 		if (i > MAXHOSTNAMELEN)
10240Sstevel@tonic-gate 			i = MAXHOSTNAMELEN;
10250Sstevel@tonic-gate 		bcopy(doptp->value, hostname, i);
10260Sstevel@tonic-gate 		hostname[i] = '\0';
10270Sstevel@tonic-gate 		(void) sethostname(hostname, i);
10280Sstevel@tonic-gate 		if (boothowto & RB_VERBOSE)
10290Sstevel@tonic-gate 			printf("%s: Hostname is %s\n", s_n, hostname);
10300Sstevel@tonic-gate 	}
10310Sstevel@tonic-gate 
10320Sstevel@tonic-gate 	/*
10330Sstevel@tonic-gate 	 * We don't care about the lease time.... We can't enforce it anyway.
10340Sstevel@tonic-gate 	 */
10350Sstevel@tonic-gate 	return (0);
10360Sstevel@tonic-gate }
10370Sstevel@tonic-gate 
10380Sstevel@tonic-gate /*
10390Sstevel@tonic-gate  * Convert the DHCPACK into a pure ASCII boot property for use by the kernel
10400Sstevel@tonic-gate  * later in the boot process.
10410Sstevel@tonic-gate  */
10420Sstevel@tonic-gate static void
create_bootpresponse_bprop(PKT_LIST * pl)10430Sstevel@tonic-gate create_bootpresponse_bprop(PKT_LIST *pl)
10440Sstevel@tonic-gate {
10450Sstevel@tonic-gate 	uint_t	buflen;
10460Sstevel@tonic-gate 
10470Sstevel@tonic-gate 	if (pl == NULL || bootp_response != NULL)
10480Sstevel@tonic-gate 		return;
10490Sstevel@tonic-gate 
10500Sstevel@tonic-gate 	buflen = (pl->len * 2) + 1;	/* extra space for null (1) */
10510Sstevel@tonic-gate 	if ((bootp_response = bkmem_alloc(buflen)) == NULL)
10520Sstevel@tonic-gate 		return;
10530Sstevel@tonic-gate 
10540Sstevel@tonic-gate 	if (octet_to_hexascii((uint8_t *)pl->pkt, pl->len, bootp_response,
10550Sstevel@tonic-gate 	    &buflen) != 0) {
10560Sstevel@tonic-gate 		bkmem_free(bootp_response, (pl->len * 2) + 1);
10570Sstevel@tonic-gate 		bootp_response = NULL;
10580Sstevel@tonic-gate 	}
10590Sstevel@tonic-gate 
1060*6747Sga159272 #if defined(__sparc)
10610Sstevel@tonic-gate 	prom_create_encoded_prop("bootp-response", pl->pkt, pl->len,
10620Sstevel@tonic-gate 	    ENCODE_BYTES);
1063*6747Sga159272 #endif	/* __sparc */
10640Sstevel@tonic-gate }
10650Sstevel@tonic-gate 
10660Sstevel@tonic-gate /*
10670Sstevel@tonic-gate  * Examines /chosen node for "bootp-response" property. If it exists, this
10680Sstevel@tonic-gate  * property is the DHCPACK cached there by the PROM's DHCP implementation.
10690Sstevel@tonic-gate  *
10700Sstevel@tonic-gate  * If cache_present is B_TRUE, we simply return B_TRUE if the property exists
10710Sstevel@tonic-gate  * w/o decoding it. If cache_present is B_FALSE, we decode the packet and
10720Sstevel@tonic-gate  * use it as our state packet for the jump to BOUND mode. Note that it's good
10730Sstevel@tonic-gate  * enough that the cache exists w/o validation for determining if that's what
10740Sstevel@tonic-gate  * the user intended.
10750Sstevel@tonic-gate  *
10760Sstevel@tonic-gate  * We'll short-circuit the DHCP exchange by accepting this packet. We build a
10770Sstevel@tonic-gate  * PKT_LIST structure, and copy the bootp-response property value into a
10780Sstevel@tonic-gate  * PKT buffer we allocated. We then scan the PKT for options, and then
10790Sstevel@tonic-gate  * set state_pl to point to it.
10800Sstevel@tonic-gate  *
10810Sstevel@tonic-gate  * Returns B_TRUE if a packet was cached (and was processed correctly), false
10820Sstevel@tonic-gate  * otherwise. The caller needs to make the state change from SELECTING to
10830Sstevel@tonic-gate  * BOUND upon a B_TRUE return from this function.
10840Sstevel@tonic-gate  */
10850Sstevel@tonic-gate int
prom_cached_reply(int cache_present)10860Sstevel@tonic-gate prom_cached_reply(int cache_present)
10870Sstevel@tonic-gate {
10880Sstevel@tonic-gate 	PKT_LIST	*pl;
10890Sstevel@tonic-gate 	int	len;
1090789Sahrens 	pnode_t	chosen;
10910Sstevel@tonic-gate 	char	*prop = PROM_BOOT_CACHED;
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate 	chosen = prom_finddevice("/chosen");
10940Sstevel@tonic-gate 	if (chosen == OBP_NONODE || chosen == OBP_BADNODE)
1095789Sahrens 		chosen = prom_nextnode((pnode_t)0);	/* root node */
10960Sstevel@tonic-gate 
10970Sstevel@tonic-gate 	if ((len = prom_getproplen(chosen, prop)) <= 0)
10980Sstevel@tonic-gate 		return (B_FALSE);
10990Sstevel@tonic-gate 
11000Sstevel@tonic-gate 	if (cache_present)
11010Sstevel@tonic-gate 		return (B_TRUE);
11020Sstevel@tonic-gate 
11030Sstevel@tonic-gate 	if (((pl = (PKT_LIST *)bkmem_zalloc(sizeof (PKT_LIST))) == NULL) ||
11040Sstevel@tonic-gate 	    ((pl->pkt = (PKT *)bkmem_zalloc(len)) == NULL)) {
11050Sstevel@tonic-gate 		errno = ENOMEM;
11060Sstevel@tonic-gate 		if (pl != NULL)
11070Sstevel@tonic-gate 			bkmem_free((char *)pl, sizeof (PKT_LIST));
11080Sstevel@tonic-gate 		return (B_FALSE);
11090Sstevel@tonic-gate 	}
11100Sstevel@tonic-gate 
11110Sstevel@tonic-gate 	(void) prom_getprop(chosen, prop, (caddr_t)pl->pkt);
11120Sstevel@tonic-gate 
11130Sstevel@tonic-gate 	pl->len = len;
11140Sstevel@tonic-gate 
11150Sstevel@tonic-gate 	if (dhcp_options_scan(pl, B_TRUE) != 0) {
11160Sstevel@tonic-gate 		/* garbled packet */
11170Sstevel@tonic-gate 		bkmem_free((char *)pl->pkt, pl->len);
11180Sstevel@tonic-gate 		bkmem_free((char *)pl, sizeof (PKT_LIST));
11190Sstevel@tonic-gate 		return (B_FALSE);
11200Sstevel@tonic-gate 	}
11210Sstevel@tonic-gate 
11220Sstevel@tonic-gate 	state_pl = pl;
11230Sstevel@tonic-gate 	return (B_TRUE);
11240Sstevel@tonic-gate }
11250Sstevel@tonic-gate 
11260Sstevel@tonic-gate /*
11270Sstevel@tonic-gate  * Perform DHCP to acquire what we used to get using rarp/bootparams.
11280Sstevel@tonic-gate  * Returns 0 for success, nonzero otherwise.
11290Sstevel@tonic-gate  *
11300Sstevel@tonic-gate  * DHCP client state machine.
11310Sstevel@tonic-gate  */
11320Sstevel@tonic-gate int
dhcp(void)11330Sstevel@tonic-gate dhcp(void)
11340Sstevel@tonic-gate {
11350Sstevel@tonic-gate 	int	err = 0;
11360Sstevel@tonic-gate 	int	done = B_FALSE;
11370Sstevel@tonic-gate 
11380Sstevel@tonic-gate 	dhcp_buf_size = mac_get_mtu();
11390Sstevel@tonic-gate 	dhcp_snd_bufp = (PKT *)bkmem_alloc(dhcp_buf_size);
11400Sstevel@tonic-gate 	dhcp_rcv_bufp = (PKT *)bkmem_alloc(dhcp_buf_size);
11410Sstevel@tonic-gate 
11420Sstevel@tonic-gate 	if (dhcp_snd_bufp == NULL || dhcp_rcv_bufp == NULL) {
11430Sstevel@tonic-gate 		if (dhcp_snd_bufp != NULL)
11440Sstevel@tonic-gate 			bkmem_free((char *)dhcp_snd_bufp, dhcp_buf_size);
11450Sstevel@tonic-gate 		if (dhcp_rcv_bufp != NULL)
11460Sstevel@tonic-gate 			bkmem_free((char *)dhcp_rcv_bufp, dhcp_buf_size);
11470Sstevel@tonic-gate 		errno = ENOMEM;
11480Sstevel@tonic-gate 		return (-1);
11490Sstevel@tonic-gate 	}
11500Sstevel@tonic-gate 
11510Sstevel@tonic-gate 	while (err == 0 && !done) {
11520Sstevel@tonic-gate 		switch (dhcp_state) {
11530Sstevel@tonic-gate 		case INIT:
11540Sstevel@tonic-gate 
11550Sstevel@tonic-gate 			s_n = "INIT";
11560Sstevel@tonic-gate 			if (prom_cached_reply(B_FALSE)) {
11570Sstevel@tonic-gate 				dprintf("%s: Using PROM cached BOOT reply...\n",
11580Sstevel@tonic-gate 				    s_n);
11590Sstevel@tonic-gate 				dhcp_state = BOUND;
11600Sstevel@tonic-gate 			} else {
11610Sstevel@tonic-gate 				dhcp_state = SELECTING;
11620Sstevel@tonic-gate 				dhcp_start_time = prom_gettime();
11630Sstevel@tonic-gate 			}
11640Sstevel@tonic-gate 			break;
11650Sstevel@tonic-gate 
11660Sstevel@tonic-gate 		case SELECTING:
11670Sstevel@tonic-gate 
11680Sstevel@tonic-gate 			s_n = "SELECTING";
11690Sstevel@tonic-gate 			err = dhcp_selecting();
11700Sstevel@tonic-gate 			switch (err) {
11710Sstevel@tonic-gate 			case 0:
11720Sstevel@tonic-gate 				dhcp_state = REQUESTING;
11730Sstevel@tonic-gate 				break;
11740Sstevel@tonic-gate 			case DHCP_NO_DATA:
11750Sstevel@tonic-gate 				dprintf(
11760Sstevel@tonic-gate 				    "%s: No DHCP response after %d tries.\n",
11770Sstevel@tonic-gate 				    s_n, DHCP_RETRIES);
11780Sstevel@tonic-gate 				break;
11790Sstevel@tonic-gate 			default:
11800Sstevel@tonic-gate 				/* major network problems */
11810Sstevel@tonic-gate 				dprintf("%s: Network transaction failed: %d\n",
11820Sstevel@tonic-gate 				    s_n, err);
11830Sstevel@tonic-gate 				break;
11840Sstevel@tonic-gate 			}
11850Sstevel@tonic-gate 			break;
11860Sstevel@tonic-gate 
11870Sstevel@tonic-gate 		case REQUESTING:
11880Sstevel@tonic-gate 
11890Sstevel@tonic-gate 			s_n = "REQUESTING";
11900Sstevel@tonic-gate 			err = dhcp_requesting();
11910Sstevel@tonic-gate 			switch (err) {
11920Sstevel@tonic-gate 			case 0:
11930Sstevel@tonic-gate 				dhcp_state = BOUND;
11940Sstevel@tonic-gate 				break;
11950Sstevel@tonic-gate 			case DHCP_NO_DATA:
11960Sstevel@tonic-gate 				dprintf("%s: Request timed out.\n", s_n);
11970Sstevel@tonic-gate 				dhcp_state = SELECTING;
11980Sstevel@tonic-gate 				err = 0;
11990Sstevel@tonic-gate 				(void) sleep(10);
12000Sstevel@tonic-gate 				break;
12010Sstevel@tonic-gate 			default:
12020Sstevel@tonic-gate 				/* major network problems */
12030Sstevel@tonic-gate 				dprintf("%s: Network transaction failed: %d\n",
12040Sstevel@tonic-gate 				    s_n, err);
12050Sstevel@tonic-gate 				break;
12060Sstevel@tonic-gate 			}
12070Sstevel@tonic-gate 			break;
12080Sstevel@tonic-gate 
12090Sstevel@tonic-gate 		case BOUND:
12100Sstevel@tonic-gate 
12110Sstevel@tonic-gate 			/*
12120Sstevel@tonic-gate 			 * We just "give up" if bound state fails.
12130Sstevel@tonic-gate 			 */
12140Sstevel@tonic-gate 			s_n = "BOUND";
12150Sstevel@tonic-gate 			if ((err = dhcp_bound()) == 0) {
12160Sstevel@tonic-gate 				dhcp_state = CONFIGURED;
12170Sstevel@tonic-gate 			}
12180Sstevel@tonic-gate 			break;
12190Sstevel@tonic-gate 
12200Sstevel@tonic-gate 		case CONFIGURED:
12210Sstevel@tonic-gate 			s_n = "CONFIGURED";
12220Sstevel@tonic-gate 			create_bootpresponse_bprop(state_pl);
12230Sstevel@tonic-gate 			done = B_TRUE;
12240Sstevel@tonic-gate 			break;
12250Sstevel@tonic-gate 		}
12260Sstevel@tonic-gate 	}
12270Sstevel@tonic-gate 	bkmem_free((char *)dhcp_snd_bufp, dhcp_buf_size);
12280Sstevel@tonic-gate 	bkmem_free((char *)dhcp_rcv_bufp, dhcp_buf_size);
12290Sstevel@tonic-gate 
12300Sstevel@tonic-gate 	return (err);
12310Sstevel@tonic-gate }
12320Sstevel@tonic-gate 
12330Sstevel@tonic-gate /*
12340Sstevel@tonic-gate  * Returns a copy of the DHCP-supplied value of the parameter requested
12350Sstevel@tonic-gate  * by code.
12360Sstevel@tonic-gate  */
12370Sstevel@tonic-gate boolean_t
dhcp_getinfo(uchar_t optcat,uint16_t code,uint16_t optsize,void * value,size_t * vallenp)12380Sstevel@tonic-gate dhcp_getinfo(uchar_t optcat, uint16_t code, uint16_t optsize, void *value,
12390Sstevel@tonic-gate     size_t *vallenp)
12400Sstevel@tonic-gate {
12410Sstevel@tonic-gate 	size_t		len = *vallenp;
12420Sstevel@tonic-gate 
12430Sstevel@tonic-gate 	if (dhcp_getinfo_pl(state_pl, optcat, code,
1244*6747Sga159272 	    optsize, value, &len)) {
12450Sstevel@tonic-gate 
12460Sstevel@tonic-gate 		if (len <= *vallenp) {
12470Sstevel@tonic-gate 			*vallenp = len;
12480Sstevel@tonic-gate 			return (B_TRUE);
12490Sstevel@tonic-gate 		}
12500Sstevel@tonic-gate 	}
12510Sstevel@tonic-gate 
12520Sstevel@tonic-gate 	return (B_FALSE);
12530Sstevel@tonic-gate }
12540Sstevel@tonic-gate 
12550Sstevel@tonic-gate /*
12560Sstevel@tonic-gate  * Sets the clientid option.
12570Sstevel@tonic-gate  */
12580Sstevel@tonic-gate void
dhcp_set_client_id(uint8_t * clientid,uint8_t clientid_len)12590Sstevel@tonic-gate dhcp_set_client_id(uint8_t *clientid, uint8_t clientid_len)
12600Sstevel@tonic-gate {
12610Sstevel@tonic-gate 	bcopy(clientid, dhcp_clientid, clientid_len);
12620Sstevel@tonic-gate 	dhcp_clientid_len = clientid_len;
12630Sstevel@tonic-gate }
1264