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 DHCP_NO_DATA:
11757c478bd9Sstevel@tonic-gate dprintf(
11767c478bd9Sstevel@tonic-gate "%s: No DHCP response after %d tries.\n",
11777c478bd9Sstevel@tonic-gate s_n, DHCP_RETRIES);
11787c478bd9Sstevel@tonic-gate break;
11797c478bd9Sstevel@tonic-gate default:
11807c478bd9Sstevel@tonic-gate /* major network problems */
11817c478bd9Sstevel@tonic-gate dprintf("%s: Network transaction failed: %d\n",
11827c478bd9Sstevel@tonic-gate s_n, err);
11837c478bd9Sstevel@tonic-gate break;
11847c478bd9Sstevel@tonic-gate }
11857c478bd9Sstevel@tonic-gate break;
11867c478bd9Sstevel@tonic-gate
11877c478bd9Sstevel@tonic-gate case REQUESTING:
11887c478bd9Sstevel@tonic-gate
11897c478bd9Sstevel@tonic-gate s_n = "REQUESTING";
11907c478bd9Sstevel@tonic-gate err = dhcp_requesting();
11917c478bd9Sstevel@tonic-gate switch (err) {
11927c478bd9Sstevel@tonic-gate case 0:
11937c478bd9Sstevel@tonic-gate dhcp_state = BOUND;
11947c478bd9Sstevel@tonic-gate break;
11957c478bd9Sstevel@tonic-gate case DHCP_NO_DATA:
11967c478bd9Sstevel@tonic-gate dprintf("%s: Request timed out.\n", s_n);
11977c478bd9Sstevel@tonic-gate dhcp_state = SELECTING;
11987c478bd9Sstevel@tonic-gate err = 0;
11997c478bd9Sstevel@tonic-gate (void) sleep(10);
12007c478bd9Sstevel@tonic-gate break;
12017c478bd9Sstevel@tonic-gate default:
12027c478bd9Sstevel@tonic-gate /* major network problems */
12037c478bd9Sstevel@tonic-gate dprintf("%s: Network transaction failed: %d\n",
12047c478bd9Sstevel@tonic-gate s_n, err);
12057c478bd9Sstevel@tonic-gate break;
12067c478bd9Sstevel@tonic-gate }
12077c478bd9Sstevel@tonic-gate break;
12087c478bd9Sstevel@tonic-gate
12097c478bd9Sstevel@tonic-gate case BOUND:
12107c478bd9Sstevel@tonic-gate
12117c478bd9Sstevel@tonic-gate /*
12127c478bd9Sstevel@tonic-gate * We just "give up" if bound state fails.
12137c478bd9Sstevel@tonic-gate */
12147c478bd9Sstevel@tonic-gate s_n = "BOUND";
12157c478bd9Sstevel@tonic-gate if ((err = dhcp_bound()) == 0) {
12167c478bd9Sstevel@tonic-gate dhcp_state = CONFIGURED;
12177c478bd9Sstevel@tonic-gate }
12187c478bd9Sstevel@tonic-gate break;
12197c478bd9Sstevel@tonic-gate
12207c478bd9Sstevel@tonic-gate case CONFIGURED:
12217c478bd9Sstevel@tonic-gate s_n = "CONFIGURED";
12227c478bd9Sstevel@tonic-gate create_bootpresponse_bprop(state_pl);
12237c478bd9Sstevel@tonic-gate done = B_TRUE;
12247c478bd9Sstevel@tonic-gate break;
12257c478bd9Sstevel@tonic-gate }
12267c478bd9Sstevel@tonic-gate }
12277c478bd9Sstevel@tonic-gate bkmem_free((char *)dhcp_snd_bufp, dhcp_buf_size);
12287c478bd9Sstevel@tonic-gate bkmem_free((char *)dhcp_rcv_bufp, dhcp_buf_size);
12297c478bd9Sstevel@tonic-gate
12307c478bd9Sstevel@tonic-gate return (err);
12317c478bd9Sstevel@tonic-gate }
12327c478bd9Sstevel@tonic-gate
12337c478bd9Sstevel@tonic-gate /*
12347c478bd9Sstevel@tonic-gate * Returns a copy of the DHCP-supplied value of the parameter requested
12357c478bd9Sstevel@tonic-gate * by code.
12367c478bd9Sstevel@tonic-gate */
12377c478bd9Sstevel@tonic-gate boolean_t
dhcp_getinfo(uchar_t optcat,uint16_t code,uint16_t optsize,void * value,size_t * vallenp)12387c478bd9Sstevel@tonic-gate dhcp_getinfo(uchar_t optcat, uint16_t code, uint16_t optsize, void *value,
12397c478bd9Sstevel@tonic-gate size_t *vallenp)
12407c478bd9Sstevel@tonic-gate {
12417c478bd9Sstevel@tonic-gate size_t len = *vallenp;
12427c478bd9Sstevel@tonic-gate
12437c478bd9Sstevel@tonic-gate if (dhcp_getinfo_pl(state_pl, optcat, code,
1244*4a634bb8Sga optsize, value, &len)) {
12457c478bd9Sstevel@tonic-gate
12467c478bd9Sstevel@tonic-gate if (len <= *vallenp) {
12477c478bd9Sstevel@tonic-gate *vallenp = len;
12487c478bd9Sstevel@tonic-gate return (B_TRUE);
12497c478bd9Sstevel@tonic-gate }
12507c478bd9Sstevel@tonic-gate }
12517c478bd9Sstevel@tonic-gate
12527c478bd9Sstevel@tonic-gate return (B_FALSE);
12537c478bd9Sstevel@tonic-gate }
12547c478bd9Sstevel@tonic-gate
12557c478bd9Sstevel@tonic-gate /*
12567c478bd9Sstevel@tonic-gate * Sets the clientid option.
12577c478bd9Sstevel@tonic-gate */
12587c478bd9Sstevel@tonic-gate void
dhcp_set_client_id(uint8_t * clientid,uint8_t clientid_len)12597c478bd9Sstevel@tonic-gate dhcp_set_client_id(uint8_t *clientid, uint8_t clientid_len)
12607c478bd9Sstevel@tonic-gate {
12617c478bd9Sstevel@tonic-gate bcopy(clientid, dhcp_clientid, clientid_len);
12627c478bd9Sstevel@tonic-gate dhcp_clientid_len = clientid_len;
12637c478bd9Sstevel@tonic-gate }
1264