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