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