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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * ADOPTING state of the client state machine. This is used only during 26 * diskless boot with IPv4. 27 */ 28 29 #include <sys/types.h> 30 #include <string.h> 31 #include <unistd.h> 32 #include <stdlib.h> 33 #include <signal.h> 34 #include <sys/socket.h> 35 #include <net/if_arp.h> 36 #include <netinet/in.h> 37 #include <sys/systeminfo.h> 38 #include <netinet/inetutil.h> 39 #include <netinet/dhcp.h> 40 #include <dhcpmsg.h> 41 #include <libdevinfo.h> 42 43 #include "agent.h" 44 #include "async.h" 45 #include "util.h" 46 #include "packet.h" 47 #include "interface.h" 48 #include "states.h" 49 50 51 typedef struct { 52 char dk_if_name[IFNAMSIZ]; 53 char dk_ack[1]; 54 } dhcp_kcache_t; 55 56 static int get_dhcp_kcache(dhcp_kcache_t **, size_t *); 57 58 static boolean_t get_prom_prop(const char *, const char *, uchar_t **, 59 uint_t *); 60 61 /* 62 * dhcp_adopt(): adopts the interface managed by the kernel for diskless boot 63 * 64 * input: void 65 * output: boolean_t: B_TRUE success, B_FALSE on failure 66 */ 67 68 boolean_t 69 dhcp_adopt(void) 70 { 71 int retval; 72 dhcp_kcache_t *kcache = NULL; 73 size_t kcache_size; 74 PKT_LIST *plp = NULL; 75 dhcp_lif_t *lif; 76 dhcp_smach_t *dsmp = NULL; 77 uint_t client_id_len; 78 79 retval = get_dhcp_kcache(&kcache, &kcache_size); 80 if (retval == 0 || kcache_size < sizeof (dhcp_kcache_t)) { 81 dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot fetch kernel cache"); 82 goto failure; 83 } 84 85 dhcpmsg(MSG_DEBUG, "dhcp_adopt: fetched %s kcache", kcache->dk_if_name); 86 87 /* 88 * convert the kernel's ACK into binary 89 */ 90 91 plp = alloc_pkt_entry(strlen(kcache->dk_ack) / 2, B_FALSE); 92 if (plp == NULL) 93 goto failure; 94 95 dhcpmsg(MSG_DEBUG, "dhcp_adopt: allocated ACK of %d bytes", plp->len); 96 97 if (hexascii_to_octet(kcache->dk_ack, plp->len * 2, plp->pkt, 98 &plp->len) != 0) { 99 dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot convert kernel ACK"); 100 goto failure; 101 } 102 103 if (dhcp_options_scan(plp, B_TRUE) != 0) { 104 dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot parse kernel ACK"); 105 goto failure; 106 } 107 108 /* 109 * make an interface to represent the "cached interface" in 110 * the kernel, hook up the ACK packet we made, and send out 111 * the extend request (to attempt to renew the lease). 112 * 113 * we do a send_extend() instead of doing a dhcp_init_reboot() 114 * because although dhcp_init_reboot() is more correct from a 115 * protocol perspective, it introduces a window where a 116 * diskless client has no IP address but may need to page in 117 * more of this program. we could mlockall(), but that's 118 * going to be a mess, especially with handling malloc() and 119 * stack growth, so it's easier to just renew(). the only 120 * catch here is that if we are not granted a renewal, we're 121 * totally hosed and can only bail out. 122 */ 123 124 if ((lif = attach_lif(kcache->dk_if_name, B_FALSE, &retval)) == NULL) { 125 dhcpmsg(MSG_ERROR, "dhcp_adopt: unable to attach %s: %d", 126 kcache->dk_if_name, retval); 127 goto failure; 128 } 129 130 if ((dsmp = insert_smach(lif, &retval)) == NULL) { 131 dhcpmsg(MSG_ERROR, "dhcp_adopt: unable to create state " 132 "machine for %s: %d", kcache->dk_if_name, retval); 133 goto failure; 134 } 135 136 /* 137 * If the agent is adopting a lease, then OBP is initially 138 * searched for a client-id. 139 */ 140 141 dhcpmsg(MSG_DEBUG, "dhcp_adopt: getting /chosen:clientid property"); 142 143 client_id_len = 0; 144 if (!get_prom_prop("chosen", "client-id", &dsmp->dsm_cid, 145 &client_id_len)) { 146 /* 147 * a failure occurred trying to acquire the client-id 148 */ 149 150 dhcpmsg(MSG_DEBUG, 151 "dhcp_adopt: cannot allocate client id for %s", 152 dsmp->dsm_name); 153 goto failure; 154 } else if (dsmp->dsm_hwtype == ARPHRD_IB && dsmp->dsm_cid == NULL) { 155 /* 156 * when the interface is infiniband and the agent 157 * is adopting the lease there must be an OBP 158 * client-id. 159 */ 160 161 dhcpmsg(MSG_DEBUG, "dhcp_adopt: no /chosen:clientid id for %s", 162 dsmp->dsm_name); 163 goto failure; 164 } 165 166 dsmp->dsm_cidlen = client_id_len; 167 168 if (set_lif_dhcp(lif) != DHCP_IPC_SUCCESS) 169 goto failure; 170 171 if (!set_smach_state(dsmp, ADOPTING)) 172 goto failure; 173 dsmp->dsm_dflags = DHCP_IF_PRIMARY; 174 175 /* 176 * move to BOUND and use the information in our ACK packet. 177 * adoption will continue after DAD via dhcp_adopt_complete. 178 */ 179 180 if (!dhcp_bound(dsmp, plp)) { 181 dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot use cached packet"); 182 goto failure; 183 } 184 185 free(kcache); 186 return (B_TRUE); 187 188 failure: 189 /* Note: no need to free lif; dsmp holds reference */ 190 if (dsmp != NULL) 191 remove_smach(dsmp); 192 free(kcache); 193 free_pkt_entry(plp); 194 return (B_FALSE); 195 } 196 197 /* 198 * dhcp_adopt_complete(): completes interface adoption process after kernel 199 * duplicate address detection (DAD) is done. 200 * 201 * input: dhcp_smach_t *: the state machine on which a lease is being adopted 202 * output: none 203 */ 204 205 void 206 dhcp_adopt_complete(dhcp_smach_t *dsmp) 207 { 208 dhcpmsg(MSG_DEBUG, "dhcp_adopt_complete: completing adoption"); 209 210 if (async_start(dsmp, DHCP_EXTEND, B_FALSE) == 0) { 211 dhcpmsg(MSG_CRIT, "dhcp_adopt_complete: async_start failed"); 212 return; 213 } 214 215 if (dhcp_extending(dsmp) == 0) { 216 dhcpmsg(MSG_CRIT, 217 "dhcp_adopt_complete: cannot send renew request"); 218 return; 219 } 220 221 if (grandparent != (pid_t)0) { 222 dhcpmsg(MSG_DEBUG, "adoption complete, signalling parent (%ld)" 223 " to exit.", grandparent); 224 (void) kill(grandparent, SIGALRM); 225 } 226 } 227 228 /* 229 * get_dhcp_kcache(): fetches the DHCP ACK and interface name from the kernel 230 * 231 * input: dhcp_kcache_t **: a dynamically-allocated cache packet 232 * size_t *: the length of that packet (on return) 233 * output: int: nonzero on success, zero on failure 234 */ 235 236 static int 237 get_dhcp_kcache(dhcp_kcache_t **kernel_cachep, size_t *kcache_size) 238 { 239 char dummy; 240 long size; 241 242 size = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy)); 243 if (size == -1) 244 return (0); 245 246 *kcache_size = size; 247 *kernel_cachep = malloc(*kcache_size); 248 if (*kernel_cachep == NULL) 249 return (0); 250 251 (void) sysinfo(SI_DHCP_CACHE, (caddr_t)*kernel_cachep, size); 252 return (1); 253 } 254 255 /* 256 * get_prom_prop(): get the value of the named property on the named node in 257 * devinfo root. 258 * 259 * input: const char *: The name of the node containing the property. 260 * const char *: The name of the property. 261 * uchar_t **: The property value, modified iff B_TRUE is returned. 262 * If no value is found the value is set to NULL. 263 * uint_t *: The length of the property value 264 * output: boolean_t: Returns B_TRUE if successful (no problems), 265 * otherwise B_FALSE. 266 * note: The memory allocated by this function must be freed by 267 * the caller. 268 */ 269 270 static boolean_t 271 get_prom_prop(const char *nodename, const char *propname, uchar_t **propvaluep, 272 uint_t *lenp) 273 { 274 di_node_t root_node; 275 di_node_t node; 276 di_prom_handle_t phdl = DI_PROM_HANDLE_NIL; 277 di_prom_prop_t pp; 278 uchar_t *value = NULL; 279 unsigned int len = 0; 280 boolean_t success = B_TRUE; 281 282 /* 283 * locate root node 284 */ 285 286 if ((root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL || 287 (phdl = di_prom_init()) == DI_PROM_HANDLE_NIL) { 288 dhcpmsg(MSG_DEBUG, "get_prom_prop: property root node " 289 "not found"); 290 goto get_prom_prop_cleanup; 291 } 292 293 /* 294 * locate nodename within '/' 295 */ 296 297 for (node = di_child_node(root_node); 298 node != DI_NODE_NIL; 299 node = di_sibling_node(node)) { 300 if (strcmp(di_node_name(node), nodename) == 0) { 301 break; 302 } 303 } 304 305 if (node == DI_NODE_NIL) { 306 dhcpmsg(MSG_DEBUG, "get_prom_prop: node not found"); 307 goto get_prom_prop_cleanup; 308 } 309 310 /* 311 * scan all properties of /nodename for the 'propname' property 312 */ 313 314 for (pp = di_prom_prop_next(phdl, node, DI_PROM_PROP_NIL); 315 pp != DI_PROM_PROP_NIL; 316 pp = di_prom_prop_next(phdl, node, pp)) { 317 318 dhcpmsg(MSG_DEBUG, "get_prom_prop: property = %s", 319 di_prom_prop_name(pp)); 320 321 if (strcmp(propname, di_prom_prop_name(pp)) == 0) { 322 break; 323 } 324 } 325 326 if (pp == DI_PROM_PROP_NIL) { 327 dhcpmsg(MSG_DEBUG, "get_prom_prop: property not found"); 328 goto get_prom_prop_cleanup; 329 } 330 331 /* 332 * get the property; allocate some memory copy it out 333 */ 334 335 len = di_prom_prop_data(pp, (uchar_t **)&value); 336 337 if (value == NULL) { 338 /* 339 * property data read problems 340 */ 341 342 success = B_FALSE; 343 dhcpmsg(MSG_ERR, "get_prom_prop: cannot read property data"); 344 goto get_prom_prop_cleanup; 345 } 346 347 if (propvaluep != NULL) { 348 /* 349 * allocate somewhere to copy the property value to 350 */ 351 352 *propvaluep = calloc(len, sizeof (uchar_t)); 353 354 if (*propvaluep == NULL) { 355 /* 356 * allocation problems 357 */ 358 359 success = B_FALSE; 360 dhcpmsg(MSG_ERR, "get_prom_prop: cannot allocate " 361 "memory for property value"); 362 goto get_prom_prop_cleanup; 363 } 364 365 /* 366 * copy data out 367 */ 368 369 (void) memcpy(*propvaluep, value, len); 370 371 /* 372 * copy out the length if a suitable pointer has 373 * been supplied 374 */ 375 376 if (lenp != NULL) { 377 *lenp = len; 378 } 379 380 dhcpmsg(MSG_DEBUG, "get_prom_prop: property value " 381 "length = %d", len); 382 } 383 384 get_prom_prop_cleanup: 385 386 if (phdl != DI_PROM_HANDLE_NIL) { 387 di_prom_fini(phdl); 388 } 389 390 if (root_node != DI_NODE_NIL) { 391 di_fini(root_node); 392 } 393 394 return (success); 395 } 396