10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 52612Scarlsonj * Common Development and Distribution License (the "License"). 62612Scarlsonj * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*3431Scarlsonj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate * 25*3431Scarlsonj * ADOPTING state of the client state machine. This is used only during 26*3431Scarlsonj * diskless boot with IPv4. 270Sstevel@tonic-gate */ 280Sstevel@tonic-gate 290Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 300Sstevel@tonic-gate 310Sstevel@tonic-gate #include <sys/types.h> 320Sstevel@tonic-gate #include <string.h> 330Sstevel@tonic-gate #include <unistd.h> 340Sstevel@tonic-gate #include <stdlib.h> 352612Scarlsonj #include <signal.h> 360Sstevel@tonic-gate #include <sys/socket.h> 37*3431Scarlsonj #include <net/if_arp.h> 380Sstevel@tonic-gate #include <netinet/in.h> 390Sstevel@tonic-gate #include <sys/systeminfo.h> 400Sstevel@tonic-gate #include <netinet/inetutil.h> 410Sstevel@tonic-gate #include <netinet/dhcp.h> 420Sstevel@tonic-gate #include <dhcpmsg.h> 43*3431Scarlsonj #include <libdevinfo.h> 440Sstevel@tonic-gate 45*3431Scarlsonj #include "agent.h" 460Sstevel@tonic-gate #include "async.h" 470Sstevel@tonic-gate #include "util.h" 480Sstevel@tonic-gate #include "packet.h" 490Sstevel@tonic-gate #include "interface.h" 500Sstevel@tonic-gate #include "states.h" 510Sstevel@tonic-gate 520Sstevel@tonic-gate 530Sstevel@tonic-gate typedef struct { 540Sstevel@tonic-gate char dk_if_name[IFNAMSIZ]; 550Sstevel@tonic-gate char dk_ack[1]; 560Sstevel@tonic-gate } dhcp_kcache_t; 570Sstevel@tonic-gate 580Sstevel@tonic-gate static int get_dhcp_kcache(dhcp_kcache_t **, size_t *); 590Sstevel@tonic-gate 60*3431Scarlsonj static boolean_t get_prom_prop(const char *, const char *, uchar_t **, 61*3431Scarlsonj uint_t *); 62*3431Scarlsonj 630Sstevel@tonic-gate /* 640Sstevel@tonic-gate * dhcp_adopt(): adopts the interface managed by the kernel for diskless boot 650Sstevel@tonic-gate * 660Sstevel@tonic-gate * input: void 67*3431Scarlsonj * output: boolean_t: B_TRUE success, B_FALSE on failure 680Sstevel@tonic-gate */ 690Sstevel@tonic-gate 70*3431Scarlsonj boolean_t 710Sstevel@tonic-gate dhcp_adopt(void) 720Sstevel@tonic-gate { 730Sstevel@tonic-gate int retval; 740Sstevel@tonic-gate dhcp_kcache_t *kcache = NULL; 750Sstevel@tonic-gate size_t kcache_size; 760Sstevel@tonic-gate PKT_LIST *plp = NULL; 77*3431Scarlsonj dhcp_lif_t *lif; 78*3431Scarlsonj dhcp_smach_t *dsmp = NULL; 79*3431Scarlsonj uint_t client_id_len; 800Sstevel@tonic-gate 810Sstevel@tonic-gate retval = get_dhcp_kcache(&kcache, &kcache_size); 820Sstevel@tonic-gate if (retval == 0 || kcache_size < sizeof (dhcp_kcache_t)) { 830Sstevel@tonic-gate dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot fetch kernel cache"); 840Sstevel@tonic-gate goto failure; 850Sstevel@tonic-gate } 860Sstevel@tonic-gate 870Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "dhcp_adopt: fetched %s kcache", kcache->dk_if_name); 880Sstevel@tonic-gate 890Sstevel@tonic-gate /* 900Sstevel@tonic-gate * convert the kernel's ACK into binary 910Sstevel@tonic-gate */ 920Sstevel@tonic-gate 93*3431Scarlsonj plp = alloc_pkt_entry(strlen(kcache->dk_ack) / 2, B_FALSE); 940Sstevel@tonic-gate if (plp == NULL) 950Sstevel@tonic-gate goto failure; 960Sstevel@tonic-gate 970Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "dhcp_adopt: allocated ACK of %d bytes", plp->len); 980Sstevel@tonic-gate 99*3431Scarlsonj if (hexascii_to_octet(kcache->dk_ack, plp->len * 2, plp->pkt, 100*3431Scarlsonj &plp->len) != 0) { 1010Sstevel@tonic-gate dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot convert kernel ACK"); 1020Sstevel@tonic-gate goto failure; 1030Sstevel@tonic-gate } 1040Sstevel@tonic-gate 1050Sstevel@tonic-gate if (dhcp_options_scan(plp, B_TRUE) != 0) { 1060Sstevel@tonic-gate dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot parse kernel ACK"); 1070Sstevel@tonic-gate goto failure; 1080Sstevel@tonic-gate } 1090Sstevel@tonic-gate 1100Sstevel@tonic-gate /* 1110Sstevel@tonic-gate * make an interface to represent the "cached interface" in 1120Sstevel@tonic-gate * the kernel, hook up the ACK packet we made, and send out 1130Sstevel@tonic-gate * the extend request (to attempt to renew the lease). 1140Sstevel@tonic-gate * 1150Sstevel@tonic-gate * we do a send_extend() instead of doing a dhcp_init_reboot() 1160Sstevel@tonic-gate * because although dhcp_init_reboot() is more correct from a 1170Sstevel@tonic-gate * protocol perspective, it introduces a window where a 1180Sstevel@tonic-gate * diskless client has no IP address but may need to page in 1190Sstevel@tonic-gate * more of this program. we could mlockall(), but that's 1200Sstevel@tonic-gate * going to be a mess, especially with handling malloc() and 1210Sstevel@tonic-gate * stack growth, so it's easier to just renew(). the only 1220Sstevel@tonic-gate * catch here is that if we are not granted a renewal, we're 1230Sstevel@tonic-gate * totally hosed and can only bail out. 1240Sstevel@tonic-gate */ 1250Sstevel@tonic-gate 126*3431Scarlsonj if ((lif = attach_lif(kcache->dk_if_name, B_FALSE, &retval)) == NULL) { 127*3431Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_adopt: unable to attach %s: %d", 128*3431Scarlsonj kcache->dk_if_name, retval); 129*3431Scarlsonj goto failure; 130*3431Scarlsonj } 131*3431Scarlsonj 132*3431Scarlsonj if ((dsmp = insert_smach(lif, &retval)) == NULL) { 133*3431Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_adopt: unable to create state " 134*3431Scarlsonj "machine for %s: %d", kcache->dk_if_name, retval); 135*3431Scarlsonj goto failure; 136*3431Scarlsonj } 137*3431Scarlsonj 138*3431Scarlsonj /* 139*3431Scarlsonj * If the agent is adopting a lease, then OBP is initially 140*3431Scarlsonj * searched for a client-id. 141*3431Scarlsonj */ 142*3431Scarlsonj 143*3431Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_adopt: getting /chosen:clientid property"); 144*3431Scarlsonj 145*3431Scarlsonj client_id_len = 0; 146*3431Scarlsonj if (!get_prom_prop("chosen", "client-id", &dsmp->dsm_cid, 147*3431Scarlsonj &client_id_len)) { 148*3431Scarlsonj /* 149*3431Scarlsonj * a failure occurred trying to acquire the client-id 150*3431Scarlsonj */ 151*3431Scarlsonj 152*3431Scarlsonj dhcpmsg(MSG_DEBUG, 153*3431Scarlsonj "dhcp_adopt: cannot allocate client id for %s", 154*3431Scarlsonj dsmp->dsm_name); 155*3431Scarlsonj goto failure; 156*3431Scarlsonj } else if (dsmp->dsm_hwtype == ARPHRD_IB && dsmp->dsm_cid == NULL) { 157*3431Scarlsonj /* 158*3431Scarlsonj * when the interface is infiniband and the agent 159*3431Scarlsonj * is adopting the lease there must be an OBP 160*3431Scarlsonj * client-id. 161*3431Scarlsonj */ 162*3431Scarlsonj 163*3431Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_adopt: no /chosen:clientid id for %s", 164*3431Scarlsonj dsmp->dsm_name); 165*3431Scarlsonj goto failure; 166*3431Scarlsonj } 167*3431Scarlsonj 168*3431Scarlsonj dsmp->dsm_cidlen = client_id_len; 169*3431Scarlsonj 170*3431Scarlsonj if (set_lif_dhcp(lif, B_TRUE) != DHCP_IPC_SUCCESS) 1710Sstevel@tonic-gate goto failure; 1720Sstevel@tonic-gate 173*3431Scarlsonj if (!set_smach_state(dsmp, ADOPTING)) 174*3431Scarlsonj goto failure; 175*3431Scarlsonj dsmp->dsm_dflags = DHCP_IF_PRIMARY; 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate /* 1782612Scarlsonj * move to BOUND and use the information in our ACK packet. 1792612Scarlsonj * adoption will continue after DAD via dhcp_adopt_complete. 1800Sstevel@tonic-gate */ 1810Sstevel@tonic-gate 182*3431Scarlsonj if (!dhcp_bound(dsmp, plp)) { 1830Sstevel@tonic-gate dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot use cached packet"); 1840Sstevel@tonic-gate goto failure; 1850Sstevel@tonic-gate } 1860Sstevel@tonic-gate 1870Sstevel@tonic-gate free(kcache); 188*3431Scarlsonj return (B_TRUE); 1890Sstevel@tonic-gate 1900Sstevel@tonic-gate failure: 191*3431Scarlsonj /* Note: no need to free lif; dsmp holds reference */ 192*3431Scarlsonj if (dsmp != NULL) 193*3431Scarlsonj release_smach(dsmp); 1940Sstevel@tonic-gate free(kcache); 195*3431Scarlsonj free_pkt_entry(plp); 196*3431Scarlsonj return (B_FALSE); 1970Sstevel@tonic-gate } 1980Sstevel@tonic-gate 1990Sstevel@tonic-gate /* 2002612Scarlsonj * dhcp_adopt_complete(): completes interface adoption process after kernel 2012612Scarlsonj * duplicate address detection (DAD) is done. 2022612Scarlsonj * 203*3431Scarlsonj * input: dhcp_smach_t *: the state machine on which a lease is being adopted 2042612Scarlsonj * output: none 2052612Scarlsonj */ 2062612Scarlsonj 2072612Scarlsonj void 208*3431Scarlsonj dhcp_adopt_complete(dhcp_smach_t *dsmp) 2092612Scarlsonj { 2102612Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_adopt_complete: completing adoption"); 2112612Scarlsonj 212*3431Scarlsonj if (async_start(dsmp, DHCP_EXTEND, B_FALSE) == 0) { 2132612Scarlsonj dhcpmsg(MSG_CRIT, "dhcp_adopt_complete: async_start failed"); 2142612Scarlsonj return; 2152612Scarlsonj } 2162612Scarlsonj 217*3431Scarlsonj if (dhcp_extending(dsmp) == 0) { 2182612Scarlsonj dhcpmsg(MSG_CRIT, 2192612Scarlsonj "dhcp_adopt_complete: cannot send renew request"); 2202612Scarlsonj return; 2212612Scarlsonj } 2222612Scarlsonj 2232612Scarlsonj if (grandparent != (pid_t)0) { 224*3431Scarlsonj dhcpmsg(MSG_DEBUG, "adoption complete, signalling parent (%ld)" 2252612Scarlsonj " to exit.", grandparent); 2262612Scarlsonj (void) kill(grandparent, SIGALRM); 2272612Scarlsonj } 2282612Scarlsonj } 2292612Scarlsonj 2302612Scarlsonj /* 2310Sstevel@tonic-gate * get_dhcp_kcache(): fetches the DHCP ACK and interface name from the kernel 2320Sstevel@tonic-gate * 2330Sstevel@tonic-gate * input: dhcp_kcache_t **: a dynamically-allocated cache packet 2340Sstevel@tonic-gate * size_t *: the length of that packet (on return) 2350Sstevel@tonic-gate * output: int: nonzero on success, zero on failure 2360Sstevel@tonic-gate */ 2370Sstevel@tonic-gate 2380Sstevel@tonic-gate static int 2390Sstevel@tonic-gate get_dhcp_kcache(dhcp_kcache_t **kernel_cachep, size_t *kcache_size) 2400Sstevel@tonic-gate { 2410Sstevel@tonic-gate char dummy; 2420Sstevel@tonic-gate long size; 2430Sstevel@tonic-gate 2440Sstevel@tonic-gate size = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy)); 2450Sstevel@tonic-gate if (size == -1) 2460Sstevel@tonic-gate return (0); 2470Sstevel@tonic-gate 2480Sstevel@tonic-gate *kcache_size = size; 2490Sstevel@tonic-gate *kernel_cachep = malloc(*kcache_size); 2500Sstevel@tonic-gate if (*kernel_cachep == NULL) 2510Sstevel@tonic-gate return (0); 2520Sstevel@tonic-gate 2530Sstevel@tonic-gate (void) sysinfo(SI_DHCP_CACHE, (caddr_t)*kernel_cachep, size); 2540Sstevel@tonic-gate return (1); 2550Sstevel@tonic-gate } 256*3431Scarlsonj 257*3431Scarlsonj /* 258*3431Scarlsonj * get_prom_prop(): get the value of the named property on the named node in 259*3431Scarlsonj * devinfo root. 260*3431Scarlsonj * 261*3431Scarlsonj * input: const char *: The name of the node containing the property. 262*3431Scarlsonj * const char *: The name of the property. 263*3431Scarlsonj * uchar_t **: The property value, modified iff B_TRUE is returned. 264*3431Scarlsonj * If no value is found the value is set to NULL. 265*3431Scarlsonj * uint_t *: The length of the property value 266*3431Scarlsonj * output: boolean_t: Returns B_TRUE if successful (no problems), 267*3431Scarlsonj * otherwise B_FALSE. 268*3431Scarlsonj * note: The memory allocated by this function must be freed by 269*3431Scarlsonj * the caller. This code is derived from 270*3431Scarlsonj * usr/src/lib/libwanboot/common/bootinfo_aux.c. 271*3431Scarlsonj */ 272*3431Scarlsonj 273*3431Scarlsonj static boolean_t 274*3431Scarlsonj get_prom_prop(const char *nodename, const char *propname, uchar_t **propvaluep, 275*3431Scarlsonj uint_t *lenp) 276*3431Scarlsonj { 277*3431Scarlsonj di_node_t root_node; 278*3431Scarlsonj di_node_t node; 279*3431Scarlsonj di_prom_handle_t phdl = DI_PROM_HANDLE_NIL; 280*3431Scarlsonj di_prom_prop_t pp; 281*3431Scarlsonj uchar_t *value = NULL; 282*3431Scarlsonj unsigned int len = 0; 283*3431Scarlsonj boolean_t success = B_TRUE; 284*3431Scarlsonj 285*3431Scarlsonj /* 286*3431Scarlsonj * locate root node 287*3431Scarlsonj */ 288*3431Scarlsonj 289*3431Scarlsonj if ((root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL || 290*3431Scarlsonj (phdl = di_prom_init()) == DI_PROM_HANDLE_NIL) { 291*3431Scarlsonj dhcpmsg(MSG_DEBUG, "get_prom_prop: property root node " 292*3431Scarlsonj "not found"); 293*3431Scarlsonj goto get_prom_prop_cleanup; 294*3431Scarlsonj } 295*3431Scarlsonj 296*3431Scarlsonj /* 297*3431Scarlsonj * locate nodename within '/' 298*3431Scarlsonj */ 299*3431Scarlsonj 300*3431Scarlsonj for (node = di_child_node(root_node); 301*3431Scarlsonj node != DI_NODE_NIL; 302*3431Scarlsonj node = di_sibling_node(node)) { 303*3431Scarlsonj if (strcmp(di_node_name(node), nodename) == 0) { 304*3431Scarlsonj break; 305*3431Scarlsonj } 306*3431Scarlsonj } 307*3431Scarlsonj 308*3431Scarlsonj if (node == DI_NODE_NIL) { 309*3431Scarlsonj dhcpmsg(MSG_DEBUG, "get_prom_prop: node not found"); 310*3431Scarlsonj goto get_prom_prop_cleanup; 311*3431Scarlsonj } 312*3431Scarlsonj 313*3431Scarlsonj /* 314*3431Scarlsonj * scan all properties of /nodename for the 'propname' property 315*3431Scarlsonj */ 316*3431Scarlsonj 317*3431Scarlsonj for (pp = di_prom_prop_next(phdl, node, DI_PROM_PROP_NIL); 318*3431Scarlsonj pp != DI_PROM_PROP_NIL; 319*3431Scarlsonj pp = di_prom_prop_next(phdl, node, pp)) { 320*3431Scarlsonj 321*3431Scarlsonj dhcpmsg(MSG_DEBUG, "get_prom_prop: property = %s", 322*3431Scarlsonj di_prom_prop_name(pp)); 323*3431Scarlsonj 324*3431Scarlsonj if (strcmp(propname, di_prom_prop_name(pp)) == 0) { 325*3431Scarlsonj break; 326*3431Scarlsonj } 327*3431Scarlsonj } 328*3431Scarlsonj 329*3431Scarlsonj if (pp == DI_PROM_PROP_NIL) { 330*3431Scarlsonj dhcpmsg(MSG_DEBUG, "get_prom_prop: property not found"); 331*3431Scarlsonj goto get_prom_prop_cleanup; 332*3431Scarlsonj } 333*3431Scarlsonj 334*3431Scarlsonj /* 335*3431Scarlsonj * get the property; allocate some memory copy it out 336*3431Scarlsonj */ 337*3431Scarlsonj 338*3431Scarlsonj len = di_prom_prop_data(pp, (uchar_t **)&value); 339*3431Scarlsonj 340*3431Scarlsonj if (value == NULL) { 341*3431Scarlsonj /* 342*3431Scarlsonj * property data read problems 343*3431Scarlsonj */ 344*3431Scarlsonj 345*3431Scarlsonj success = B_FALSE; 346*3431Scarlsonj dhcpmsg(MSG_ERR, "get_prom_prop: cannot read property data"); 347*3431Scarlsonj goto get_prom_prop_cleanup; 348*3431Scarlsonj } 349*3431Scarlsonj 350*3431Scarlsonj if (propvaluep != NULL) { 351*3431Scarlsonj /* 352*3431Scarlsonj * allocate somewhere to copy the property value to 353*3431Scarlsonj */ 354*3431Scarlsonj 355*3431Scarlsonj *propvaluep = calloc(len, sizeof (uchar_t)); 356*3431Scarlsonj 357*3431Scarlsonj if (*propvaluep == NULL) { 358*3431Scarlsonj /* 359*3431Scarlsonj * allocation problems 360*3431Scarlsonj */ 361*3431Scarlsonj 362*3431Scarlsonj success = B_FALSE; 363*3431Scarlsonj dhcpmsg(MSG_ERR, "get_prom_prop: cannot allocate " 364*3431Scarlsonj "memory for property value"); 365*3431Scarlsonj goto get_prom_prop_cleanup; 366*3431Scarlsonj } 367*3431Scarlsonj 368*3431Scarlsonj /* 369*3431Scarlsonj * copy data out 370*3431Scarlsonj */ 371*3431Scarlsonj 372*3431Scarlsonj (void) memcpy(*propvaluep, value, len); 373*3431Scarlsonj 374*3431Scarlsonj /* 375*3431Scarlsonj * copy out the length if a suitable pointer has 376*3431Scarlsonj * been supplied 377*3431Scarlsonj */ 378*3431Scarlsonj 379*3431Scarlsonj if (lenp != NULL) { 380*3431Scarlsonj *lenp = len; 381*3431Scarlsonj } 382*3431Scarlsonj 383*3431Scarlsonj dhcpmsg(MSG_DEBUG, "get_prom_prop: property value " 384*3431Scarlsonj "length = %d", len); 385*3431Scarlsonj } 386*3431Scarlsonj 387*3431Scarlsonj get_prom_prop_cleanup: 388*3431Scarlsonj 389*3431Scarlsonj if (phdl != DI_PROM_HANDLE_NIL) { 390*3431Scarlsonj di_prom_fini(phdl); 391*3431Scarlsonj } 392*3431Scarlsonj 393*3431Scarlsonj if (root_node != DI_NODE_NIL) { 394*3431Scarlsonj di_fini(root_node); 395*3431Scarlsonj } 396*3431Scarlsonj 397*3431Scarlsonj return (success); 398*3431Scarlsonj } 399