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