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 5dc041e83Scarlsonj * Common Development and Distribution License (the "License"). 6dc041e83Scarlsonj * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*dc918d99Smeem * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate * 25d04ccbb3Scarlsonj * ADOPTING state of the client state machine. This is used only during 26d04ccbb3Scarlsonj * diskless boot with IPv4. 277c478bd9Sstevel@tonic-gate */ 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate #include <sys/types.h> 307c478bd9Sstevel@tonic-gate #include <string.h> 317c478bd9Sstevel@tonic-gate #include <unistd.h> 327c478bd9Sstevel@tonic-gate #include <stdlib.h> 33dc041e83Scarlsonj #include <signal.h> 347c478bd9Sstevel@tonic-gate #include <sys/socket.h> 35d04ccbb3Scarlsonj #include <net/if_arp.h> 367c478bd9Sstevel@tonic-gate #include <netinet/in.h> 377c478bd9Sstevel@tonic-gate #include <sys/systeminfo.h> 387c478bd9Sstevel@tonic-gate #include <netinet/inetutil.h> 397c478bd9Sstevel@tonic-gate #include <netinet/dhcp.h> 407c478bd9Sstevel@tonic-gate #include <dhcpmsg.h> 41d04ccbb3Scarlsonj #include <libdevinfo.h> 427c478bd9Sstevel@tonic-gate 43d04ccbb3Scarlsonj #include "agent.h" 447c478bd9Sstevel@tonic-gate #include "async.h" 457c478bd9Sstevel@tonic-gate #include "util.h" 467c478bd9Sstevel@tonic-gate #include "packet.h" 477c478bd9Sstevel@tonic-gate #include "interface.h" 487c478bd9Sstevel@tonic-gate #include "states.h" 497c478bd9Sstevel@tonic-gate 507c478bd9Sstevel@tonic-gate 517c478bd9Sstevel@tonic-gate typedef struct { 527c478bd9Sstevel@tonic-gate char dk_if_name[IFNAMSIZ]; 537c478bd9Sstevel@tonic-gate char dk_ack[1]; 547c478bd9Sstevel@tonic-gate } dhcp_kcache_t; 557c478bd9Sstevel@tonic-gate 567c478bd9Sstevel@tonic-gate static int get_dhcp_kcache(dhcp_kcache_t **, size_t *); 577c478bd9Sstevel@tonic-gate 58d04ccbb3Scarlsonj static boolean_t get_prom_prop(const char *, const char *, uchar_t **, 59d04ccbb3Scarlsonj uint_t *); 60d04ccbb3Scarlsonj 617c478bd9Sstevel@tonic-gate /* 627c478bd9Sstevel@tonic-gate * dhcp_adopt(): adopts the interface managed by the kernel for diskless boot 637c478bd9Sstevel@tonic-gate * 647c478bd9Sstevel@tonic-gate * input: void 65d04ccbb3Scarlsonj * output: boolean_t: B_TRUE success, B_FALSE on failure 667c478bd9Sstevel@tonic-gate */ 677c478bd9Sstevel@tonic-gate 68d04ccbb3Scarlsonj boolean_t 697c478bd9Sstevel@tonic-gate dhcp_adopt(void) 707c478bd9Sstevel@tonic-gate { 717c478bd9Sstevel@tonic-gate int retval; 727c478bd9Sstevel@tonic-gate dhcp_kcache_t *kcache = NULL; 737c478bd9Sstevel@tonic-gate size_t kcache_size; 747c478bd9Sstevel@tonic-gate PKT_LIST *plp = NULL; 75d04ccbb3Scarlsonj dhcp_lif_t *lif; 76d04ccbb3Scarlsonj dhcp_smach_t *dsmp = NULL; 77d04ccbb3Scarlsonj uint_t client_id_len; 787c478bd9Sstevel@tonic-gate 797c478bd9Sstevel@tonic-gate retval = get_dhcp_kcache(&kcache, &kcache_size); 807c478bd9Sstevel@tonic-gate if (retval == 0 || kcache_size < sizeof (dhcp_kcache_t)) { 817c478bd9Sstevel@tonic-gate dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot fetch kernel cache"); 827c478bd9Sstevel@tonic-gate goto failure; 837c478bd9Sstevel@tonic-gate } 847c478bd9Sstevel@tonic-gate 857c478bd9Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "dhcp_adopt: fetched %s kcache", kcache->dk_if_name); 867c478bd9Sstevel@tonic-gate 877c478bd9Sstevel@tonic-gate /* 887c478bd9Sstevel@tonic-gate * convert the kernel's ACK into binary 897c478bd9Sstevel@tonic-gate */ 907c478bd9Sstevel@tonic-gate 91d04ccbb3Scarlsonj plp = alloc_pkt_entry(strlen(kcache->dk_ack) / 2, B_FALSE); 927c478bd9Sstevel@tonic-gate if (plp == NULL) 937c478bd9Sstevel@tonic-gate goto failure; 947c478bd9Sstevel@tonic-gate 957c478bd9Sstevel@tonic-gate dhcpmsg(MSG_DEBUG, "dhcp_adopt: allocated ACK of %d bytes", plp->len); 967c478bd9Sstevel@tonic-gate 97d04ccbb3Scarlsonj if (hexascii_to_octet(kcache->dk_ack, plp->len * 2, plp->pkt, 98d04ccbb3Scarlsonj &plp->len) != 0) { 997c478bd9Sstevel@tonic-gate dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot convert kernel ACK"); 1007c478bd9Sstevel@tonic-gate goto failure; 1017c478bd9Sstevel@tonic-gate } 1027c478bd9Sstevel@tonic-gate 1037c478bd9Sstevel@tonic-gate if (dhcp_options_scan(plp, B_TRUE) != 0) { 1047c478bd9Sstevel@tonic-gate dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot parse kernel ACK"); 1057c478bd9Sstevel@tonic-gate goto failure; 1067c478bd9Sstevel@tonic-gate } 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate /* 1097c478bd9Sstevel@tonic-gate * make an interface to represent the "cached interface" in 1107c478bd9Sstevel@tonic-gate * the kernel, hook up the ACK packet we made, and send out 1117c478bd9Sstevel@tonic-gate * the extend request (to attempt to renew the lease). 1127c478bd9Sstevel@tonic-gate * 1137c478bd9Sstevel@tonic-gate * we do a send_extend() instead of doing a dhcp_init_reboot() 1147c478bd9Sstevel@tonic-gate * because although dhcp_init_reboot() is more correct from a 1157c478bd9Sstevel@tonic-gate * protocol perspective, it introduces a window where a 1167c478bd9Sstevel@tonic-gate * diskless client has no IP address but may need to page in 1177c478bd9Sstevel@tonic-gate * more of this program. we could mlockall(), but that's 1187c478bd9Sstevel@tonic-gate * going to be a mess, especially with handling malloc() and 1197c478bd9Sstevel@tonic-gate * stack growth, so it's easier to just renew(). the only 1207c478bd9Sstevel@tonic-gate * catch here is that if we are not granted a renewal, we're 1217c478bd9Sstevel@tonic-gate * totally hosed and can only bail out. 1227c478bd9Sstevel@tonic-gate */ 1237c478bd9Sstevel@tonic-gate 124d04ccbb3Scarlsonj if ((lif = attach_lif(kcache->dk_if_name, B_FALSE, &retval)) == NULL) { 125d04ccbb3Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_adopt: unable to attach %s: %d", 126d04ccbb3Scarlsonj kcache->dk_if_name, retval); 127d04ccbb3Scarlsonj goto failure; 128d04ccbb3Scarlsonj } 129d04ccbb3Scarlsonj 130d04ccbb3Scarlsonj if ((dsmp = insert_smach(lif, &retval)) == NULL) { 131d04ccbb3Scarlsonj dhcpmsg(MSG_ERROR, "dhcp_adopt: unable to create state " 132d04ccbb3Scarlsonj "machine for %s: %d", kcache->dk_if_name, retval); 133d04ccbb3Scarlsonj goto failure; 134d04ccbb3Scarlsonj } 135d04ccbb3Scarlsonj 136d04ccbb3Scarlsonj /* 137d04ccbb3Scarlsonj * If the agent is adopting a lease, then OBP is initially 138d04ccbb3Scarlsonj * searched for a client-id. 139d04ccbb3Scarlsonj */ 140d04ccbb3Scarlsonj 141d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_adopt: getting /chosen:clientid property"); 142d04ccbb3Scarlsonj 143d04ccbb3Scarlsonj client_id_len = 0; 144d04ccbb3Scarlsonj if (!get_prom_prop("chosen", "client-id", &dsmp->dsm_cid, 145d04ccbb3Scarlsonj &client_id_len)) { 146d04ccbb3Scarlsonj /* 147d04ccbb3Scarlsonj * a failure occurred trying to acquire the client-id 148d04ccbb3Scarlsonj */ 149d04ccbb3Scarlsonj 150d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, 151d04ccbb3Scarlsonj "dhcp_adopt: cannot allocate client id for %s", 152d04ccbb3Scarlsonj dsmp->dsm_name); 153d04ccbb3Scarlsonj goto failure; 154d04ccbb3Scarlsonj } else if (dsmp->dsm_hwtype == ARPHRD_IB && dsmp->dsm_cid == NULL) { 155d04ccbb3Scarlsonj /* 156d04ccbb3Scarlsonj * when the interface is infiniband and the agent 157d04ccbb3Scarlsonj * is adopting the lease there must be an OBP 158d04ccbb3Scarlsonj * client-id. 159d04ccbb3Scarlsonj */ 160d04ccbb3Scarlsonj 161d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_adopt: no /chosen:clientid id for %s", 162d04ccbb3Scarlsonj dsmp->dsm_name); 163d04ccbb3Scarlsonj goto failure; 164d04ccbb3Scarlsonj } 165d04ccbb3Scarlsonj 166d04ccbb3Scarlsonj dsmp->dsm_cidlen = client_id_len; 167d04ccbb3Scarlsonj 168*dc918d99Smeem if (set_lif_dhcp(lif) != DHCP_IPC_SUCCESS) 1697c478bd9Sstevel@tonic-gate goto failure; 1707c478bd9Sstevel@tonic-gate 171d04ccbb3Scarlsonj if (!set_smach_state(dsmp, ADOPTING)) 172d04ccbb3Scarlsonj goto failure; 173d04ccbb3Scarlsonj dsmp->dsm_dflags = DHCP_IF_PRIMARY; 1747c478bd9Sstevel@tonic-gate 1757c478bd9Sstevel@tonic-gate /* 176dc041e83Scarlsonj * move to BOUND and use the information in our ACK packet. 177dc041e83Scarlsonj * adoption will continue after DAD via dhcp_adopt_complete. 1787c478bd9Sstevel@tonic-gate */ 1797c478bd9Sstevel@tonic-gate 180d04ccbb3Scarlsonj if (!dhcp_bound(dsmp, plp)) { 1817c478bd9Sstevel@tonic-gate dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot use cached packet"); 1827c478bd9Sstevel@tonic-gate goto failure; 1837c478bd9Sstevel@tonic-gate } 1847c478bd9Sstevel@tonic-gate 1857c478bd9Sstevel@tonic-gate free(kcache); 186d04ccbb3Scarlsonj return (B_TRUE); 1877c478bd9Sstevel@tonic-gate 1887c478bd9Sstevel@tonic-gate failure: 189d04ccbb3Scarlsonj /* Note: no need to free lif; dsmp holds reference */ 190d04ccbb3Scarlsonj if (dsmp != NULL) 1913589885cScarlsonj remove_smach(dsmp); 1927c478bd9Sstevel@tonic-gate free(kcache); 193d04ccbb3Scarlsonj free_pkt_entry(plp); 194d04ccbb3Scarlsonj return (B_FALSE); 1957c478bd9Sstevel@tonic-gate } 1967c478bd9Sstevel@tonic-gate 1977c478bd9Sstevel@tonic-gate /* 198dc041e83Scarlsonj * dhcp_adopt_complete(): completes interface adoption process after kernel 199dc041e83Scarlsonj * duplicate address detection (DAD) is done. 200dc041e83Scarlsonj * 201d04ccbb3Scarlsonj * input: dhcp_smach_t *: the state machine on which a lease is being adopted 202dc041e83Scarlsonj * output: none 203dc041e83Scarlsonj */ 204dc041e83Scarlsonj 205dc041e83Scarlsonj void 206d04ccbb3Scarlsonj dhcp_adopt_complete(dhcp_smach_t *dsmp) 207dc041e83Scarlsonj { 208dc041e83Scarlsonj dhcpmsg(MSG_DEBUG, "dhcp_adopt_complete: completing adoption"); 209dc041e83Scarlsonj 210d04ccbb3Scarlsonj if (async_start(dsmp, DHCP_EXTEND, B_FALSE) == 0) { 211dc041e83Scarlsonj dhcpmsg(MSG_CRIT, "dhcp_adopt_complete: async_start failed"); 212dc041e83Scarlsonj return; 213dc041e83Scarlsonj } 214dc041e83Scarlsonj 215d04ccbb3Scarlsonj if (dhcp_extending(dsmp) == 0) { 216dc041e83Scarlsonj dhcpmsg(MSG_CRIT, 217dc041e83Scarlsonj "dhcp_adopt_complete: cannot send renew request"); 218dc041e83Scarlsonj return; 219dc041e83Scarlsonj } 220dc041e83Scarlsonj 221dc041e83Scarlsonj if (grandparent != (pid_t)0) { 222d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "adoption complete, signalling parent (%ld)" 223dc041e83Scarlsonj " to exit.", grandparent); 224dc041e83Scarlsonj (void) kill(grandparent, SIGALRM); 225dc041e83Scarlsonj } 226dc041e83Scarlsonj } 227dc041e83Scarlsonj 228dc041e83Scarlsonj /* 2297c478bd9Sstevel@tonic-gate * get_dhcp_kcache(): fetches the DHCP ACK and interface name from the kernel 2307c478bd9Sstevel@tonic-gate * 2317c478bd9Sstevel@tonic-gate * input: dhcp_kcache_t **: a dynamically-allocated cache packet 2327c478bd9Sstevel@tonic-gate * size_t *: the length of that packet (on return) 2337c478bd9Sstevel@tonic-gate * output: int: nonzero on success, zero on failure 2347c478bd9Sstevel@tonic-gate */ 2357c478bd9Sstevel@tonic-gate 2367c478bd9Sstevel@tonic-gate static int 2377c478bd9Sstevel@tonic-gate get_dhcp_kcache(dhcp_kcache_t **kernel_cachep, size_t *kcache_size) 2387c478bd9Sstevel@tonic-gate { 2397c478bd9Sstevel@tonic-gate char dummy; 2407c478bd9Sstevel@tonic-gate long size; 2417c478bd9Sstevel@tonic-gate 2427c478bd9Sstevel@tonic-gate size = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy)); 2437c478bd9Sstevel@tonic-gate if (size == -1) 2447c478bd9Sstevel@tonic-gate return (0); 2457c478bd9Sstevel@tonic-gate 2467c478bd9Sstevel@tonic-gate *kcache_size = size; 2477c478bd9Sstevel@tonic-gate *kernel_cachep = malloc(*kcache_size); 2487c478bd9Sstevel@tonic-gate if (*kernel_cachep == NULL) 2497c478bd9Sstevel@tonic-gate return (0); 2507c478bd9Sstevel@tonic-gate 2517c478bd9Sstevel@tonic-gate (void) sysinfo(SI_DHCP_CACHE, (caddr_t)*kernel_cachep, size); 2527c478bd9Sstevel@tonic-gate return (1); 2537c478bd9Sstevel@tonic-gate } 254d04ccbb3Scarlsonj 255d04ccbb3Scarlsonj /* 256d04ccbb3Scarlsonj * get_prom_prop(): get the value of the named property on the named node in 257d04ccbb3Scarlsonj * devinfo root. 258d04ccbb3Scarlsonj * 259d04ccbb3Scarlsonj * input: const char *: The name of the node containing the property. 260d04ccbb3Scarlsonj * const char *: The name of the property. 261d04ccbb3Scarlsonj * uchar_t **: The property value, modified iff B_TRUE is returned. 262d04ccbb3Scarlsonj * If no value is found the value is set to NULL. 263d04ccbb3Scarlsonj * uint_t *: The length of the property value 264d04ccbb3Scarlsonj * output: boolean_t: Returns B_TRUE if successful (no problems), 265d04ccbb3Scarlsonj * otherwise B_FALSE. 266d04ccbb3Scarlsonj * note: The memory allocated by this function must be freed by 267d04ccbb3Scarlsonj * the caller. This code is derived from 268d04ccbb3Scarlsonj * usr/src/lib/libwanboot/common/bootinfo_aux.c. 269d04ccbb3Scarlsonj */ 270d04ccbb3Scarlsonj 271d04ccbb3Scarlsonj static boolean_t 272d04ccbb3Scarlsonj get_prom_prop(const char *nodename, const char *propname, uchar_t **propvaluep, 273d04ccbb3Scarlsonj uint_t *lenp) 274d04ccbb3Scarlsonj { 275d04ccbb3Scarlsonj di_node_t root_node; 276d04ccbb3Scarlsonj di_node_t node; 277d04ccbb3Scarlsonj di_prom_handle_t phdl = DI_PROM_HANDLE_NIL; 278d04ccbb3Scarlsonj di_prom_prop_t pp; 279d04ccbb3Scarlsonj uchar_t *value = NULL; 280d04ccbb3Scarlsonj unsigned int len = 0; 281d04ccbb3Scarlsonj boolean_t success = B_TRUE; 282d04ccbb3Scarlsonj 283d04ccbb3Scarlsonj /* 284d04ccbb3Scarlsonj * locate root node 285d04ccbb3Scarlsonj */ 286d04ccbb3Scarlsonj 287d04ccbb3Scarlsonj if ((root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL || 288d04ccbb3Scarlsonj (phdl = di_prom_init()) == DI_PROM_HANDLE_NIL) { 289d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "get_prom_prop: property root node " 290d04ccbb3Scarlsonj "not found"); 291d04ccbb3Scarlsonj goto get_prom_prop_cleanup; 292d04ccbb3Scarlsonj } 293d04ccbb3Scarlsonj 294d04ccbb3Scarlsonj /* 295d04ccbb3Scarlsonj * locate nodename within '/' 296d04ccbb3Scarlsonj */ 297d04ccbb3Scarlsonj 298d04ccbb3Scarlsonj for (node = di_child_node(root_node); 299d04ccbb3Scarlsonj node != DI_NODE_NIL; 300d04ccbb3Scarlsonj node = di_sibling_node(node)) { 301d04ccbb3Scarlsonj if (strcmp(di_node_name(node), nodename) == 0) { 302d04ccbb3Scarlsonj break; 303d04ccbb3Scarlsonj } 304d04ccbb3Scarlsonj } 305d04ccbb3Scarlsonj 306d04ccbb3Scarlsonj if (node == DI_NODE_NIL) { 307d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "get_prom_prop: node not found"); 308d04ccbb3Scarlsonj goto get_prom_prop_cleanup; 309d04ccbb3Scarlsonj } 310d04ccbb3Scarlsonj 311d04ccbb3Scarlsonj /* 312d04ccbb3Scarlsonj * scan all properties of /nodename for the 'propname' property 313d04ccbb3Scarlsonj */ 314d04ccbb3Scarlsonj 315d04ccbb3Scarlsonj for (pp = di_prom_prop_next(phdl, node, DI_PROM_PROP_NIL); 316d04ccbb3Scarlsonj pp != DI_PROM_PROP_NIL; 317d04ccbb3Scarlsonj pp = di_prom_prop_next(phdl, node, pp)) { 318d04ccbb3Scarlsonj 319d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "get_prom_prop: property = %s", 320d04ccbb3Scarlsonj di_prom_prop_name(pp)); 321d04ccbb3Scarlsonj 322d04ccbb3Scarlsonj if (strcmp(propname, di_prom_prop_name(pp)) == 0) { 323d04ccbb3Scarlsonj break; 324d04ccbb3Scarlsonj } 325d04ccbb3Scarlsonj } 326d04ccbb3Scarlsonj 327d04ccbb3Scarlsonj if (pp == DI_PROM_PROP_NIL) { 328d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "get_prom_prop: property not found"); 329d04ccbb3Scarlsonj goto get_prom_prop_cleanup; 330d04ccbb3Scarlsonj } 331d04ccbb3Scarlsonj 332d04ccbb3Scarlsonj /* 333d04ccbb3Scarlsonj * get the property; allocate some memory copy it out 334d04ccbb3Scarlsonj */ 335d04ccbb3Scarlsonj 336d04ccbb3Scarlsonj len = di_prom_prop_data(pp, (uchar_t **)&value); 337d04ccbb3Scarlsonj 338d04ccbb3Scarlsonj if (value == NULL) { 339d04ccbb3Scarlsonj /* 340d04ccbb3Scarlsonj * property data read problems 341d04ccbb3Scarlsonj */ 342d04ccbb3Scarlsonj 343d04ccbb3Scarlsonj success = B_FALSE; 344d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "get_prom_prop: cannot read property data"); 345d04ccbb3Scarlsonj goto get_prom_prop_cleanup; 346d04ccbb3Scarlsonj } 347d04ccbb3Scarlsonj 348d04ccbb3Scarlsonj if (propvaluep != NULL) { 349d04ccbb3Scarlsonj /* 350d04ccbb3Scarlsonj * allocate somewhere to copy the property value to 351d04ccbb3Scarlsonj */ 352d04ccbb3Scarlsonj 353d04ccbb3Scarlsonj *propvaluep = calloc(len, sizeof (uchar_t)); 354d04ccbb3Scarlsonj 355d04ccbb3Scarlsonj if (*propvaluep == NULL) { 356d04ccbb3Scarlsonj /* 357d04ccbb3Scarlsonj * allocation problems 358d04ccbb3Scarlsonj */ 359d04ccbb3Scarlsonj 360d04ccbb3Scarlsonj success = B_FALSE; 361d04ccbb3Scarlsonj dhcpmsg(MSG_ERR, "get_prom_prop: cannot allocate " 362d04ccbb3Scarlsonj "memory for property value"); 363d04ccbb3Scarlsonj goto get_prom_prop_cleanup; 364d04ccbb3Scarlsonj } 365d04ccbb3Scarlsonj 366d04ccbb3Scarlsonj /* 367d04ccbb3Scarlsonj * copy data out 368d04ccbb3Scarlsonj */ 369d04ccbb3Scarlsonj 370d04ccbb3Scarlsonj (void) memcpy(*propvaluep, value, len); 371d04ccbb3Scarlsonj 372d04ccbb3Scarlsonj /* 373d04ccbb3Scarlsonj * copy out the length if a suitable pointer has 374d04ccbb3Scarlsonj * been supplied 375d04ccbb3Scarlsonj */ 376d04ccbb3Scarlsonj 377d04ccbb3Scarlsonj if (lenp != NULL) { 378d04ccbb3Scarlsonj *lenp = len; 379d04ccbb3Scarlsonj } 380d04ccbb3Scarlsonj 381d04ccbb3Scarlsonj dhcpmsg(MSG_DEBUG, "get_prom_prop: property value " 382d04ccbb3Scarlsonj "length = %d", len); 383d04ccbb3Scarlsonj } 384d04ccbb3Scarlsonj 385d04ccbb3Scarlsonj get_prom_prop_cleanup: 386d04ccbb3Scarlsonj 387d04ccbb3Scarlsonj if (phdl != DI_PROM_HANDLE_NIL) { 388d04ccbb3Scarlsonj di_prom_fini(phdl); 389d04ccbb3Scarlsonj } 390d04ccbb3Scarlsonj 391d04ccbb3Scarlsonj if (root_node != DI_NODE_NIL) { 392d04ccbb3Scarlsonj di_fini(root_node); 393d04ccbb3Scarlsonj } 394d04ccbb3Scarlsonj 395d04ccbb3Scarlsonj return (success); 396d04ccbb3Scarlsonj } 397