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