1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * ADOPTING state of the client state machine. 27 */ 28 29 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 #include <sys/types.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <stdlib.h> 35 #include <sys/sockio.h> 36 #include <sys/socket.h> 37 #include <netinet/in.h> 38 #include <sys/systeminfo.h> 39 #include <netinet/inetutil.h> 40 #include <netinet/dhcp.h> 41 #include <dhcpmsg.h> 42 43 #include "async.h" 44 #include "util.h" 45 #include "packet.h" 46 #include "interface.h" 47 #include "states.h" 48 49 50 typedef struct { 51 char dk_if_name[IFNAMSIZ]; 52 char dk_ack[1]; 53 } dhcp_kcache_t; 54 55 static int get_dhcp_kcache(dhcp_kcache_t **, size_t *); 56 57 /* 58 * dhcp_adopt(): adopts the interface managed by the kernel for diskless boot 59 * 60 * input: void 61 * output: int: nonzero on success, zero on failure 62 */ 63 64 int 65 dhcp_adopt(void) 66 { 67 int retval; 68 dhcp_kcache_t *kcache = NULL; 69 size_t kcache_size; 70 PKT_LIST *plp = NULL; 71 struct ifslist *ifsp; 72 73 retval = get_dhcp_kcache(&kcache, &kcache_size); 74 if (retval == 0 || kcache_size < sizeof (dhcp_kcache_t)) { 75 dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot fetch kernel cache"); 76 goto failure; 77 } 78 79 dhcpmsg(MSG_DEBUG, "dhcp_adopt: fetched %s kcache", kcache->dk_if_name); 80 81 /* 82 * convert the kernel's ACK into binary 83 */ 84 85 plp = calloc(1, sizeof (PKT_LIST)); 86 if (plp == NULL) 87 goto failure; 88 89 plp->len = strlen(kcache->dk_ack) / 2; 90 plp->pkt = malloc(plp->len); 91 if (plp->pkt == NULL) 92 goto failure; 93 94 dhcpmsg(MSG_DEBUG, "dhcp_adopt: allocated ACK of %d bytes", plp->len); 95 96 if (hexascii_to_octet(kcache->dk_ack, plp->len * 2, plp->pkt, &plp->len) 97 != 0) { 98 dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot convert kernel ACK"); 99 goto failure; 100 } 101 102 if (dhcp_options_scan(plp, B_TRUE) != 0) { 103 dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot parse kernel ACK"); 104 goto failure; 105 } 106 107 /* 108 * make an interface to represent the "cached interface" in 109 * the kernel, hook up the ACK packet we made, and send out 110 * the extend request (to attempt to renew the lease). 111 * 112 * we do a send_extend() instead of doing a dhcp_init_reboot() 113 * because although dhcp_init_reboot() is more correct from a 114 * protocol perspective, it introduces a window where a 115 * diskless client has no IP address but may need to page in 116 * more of this program. we could mlockall(), but that's 117 * going to be a mess, especially with handling malloc() and 118 * stack growth, so it's easier to just renew(). the only 119 * catch here is that if we are not granted a renewal, we're 120 * totally hosed and can only bail out. 121 */ 122 123 ifsp = insert_ifs(kcache->dk_if_name, B_TRUE, &retval); 124 if (ifsp == NULL) 125 goto failure; 126 127 ifsp->if_state = ADOPTING; 128 ifsp->if_dflags |= DHCP_IF_PRIMARY; 129 130 /* 131 * move to BOUND and use the information in our ACK packet 132 */ 133 134 if (dhcp_bound(ifsp, plp) == 0) { 135 dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot use cached packet"); 136 goto failure; 137 } 138 139 if (async_start(ifsp, DHCP_EXTEND, B_FALSE) == 0) { 140 dhcpmsg(MSG_CRIT, "dhcp_adopt: async_start failed"); 141 goto failure; 142 } 143 144 if (dhcp_extending(ifsp) == 0) { 145 dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot send renew request"); 146 goto failure; 147 } 148 149 free(kcache); 150 return (1); 151 152 failure: 153 free(kcache); 154 if (plp != NULL) 155 free(plp->pkt); 156 free(plp); 157 return (0); 158 } 159 160 /* 161 * get_dhcp_kcache(): fetches the DHCP ACK and interface name from the kernel 162 * 163 * input: dhcp_kcache_t **: a dynamically-allocated cache packet 164 * size_t *: the length of that packet (on return) 165 * output: int: nonzero on success, zero on failure 166 */ 167 168 static int 169 get_dhcp_kcache(dhcp_kcache_t **kernel_cachep, size_t *kcache_size) 170 { 171 char dummy; 172 long size; 173 174 size = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy)); 175 if (size == -1) 176 return (0); 177 178 *kcache_size = size; 179 *kernel_cachep = malloc(*kcache_size); 180 if (*kernel_cachep == NULL) 181 return (0); 182 183 (void) sysinfo(SI_DHCP_CACHE, (caddr_t)*kernel_cachep, size); 184 return (1); 185 } 186