xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/adopt.c (revision 10785:66ded0c3429b)
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*10785SPeter.Memishian@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  *
253431Scarlsonj  * ADOPTING state of the client state machine.  This is used only during
263431Scarlsonj  * diskless boot with IPv4.
270Sstevel@tonic-gate  */
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #include <sys/types.h>
300Sstevel@tonic-gate #include <string.h>
310Sstevel@tonic-gate #include <unistd.h>
320Sstevel@tonic-gate #include <stdlib.h>
332612Scarlsonj #include <signal.h>
340Sstevel@tonic-gate #include <sys/socket.h>
353431Scarlsonj #include <net/if_arp.h>
360Sstevel@tonic-gate #include <netinet/in.h>
370Sstevel@tonic-gate #include <sys/systeminfo.h>
380Sstevel@tonic-gate #include <netinet/inetutil.h>
390Sstevel@tonic-gate #include <netinet/dhcp.h>
400Sstevel@tonic-gate #include <dhcpmsg.h>
413431Scarlsonj #include <libdevinfo.h>
420Sstevel@tonic-gate 
433431Scarlsonj #include "agent.h"
440Sstevel@tonic-gate #include "async.h"
450Sstevel@tonic-gate #include "util.h"
460Sstevel@tonic-gate #include "packet.h"
470Sstevel@tonic-gate #include "interface.h"
480Sstevel@tonic-gate #include "states.h"
490Sstevel@tonic-gate 
500Sstevel@tonic-gate 
510Sstevel@tonic-gate typedef struct {
520Sstevel@tonic-gate 	char		dk_if_name[IFNAMSIZ];
530Sstevel@tonic-gate 	char		dk_ack[1];
540Sstevel@tonic-gate } dhcp_kcache_t;
550Sstevel@tonic-gate 
560Sstevel@tonic-gate static int	get_dhcp_kcache(dhcp_kcache_t **, size_t *);
570Sstevel@tonic-gate 
583431Scarlsonj static boolean_t	get_prom_prop(const char *, const char *, uchar_t **,
593431Scarlsonj 			    uint_t *);
603431Scarlsonj 
610Sstevel@tonic-gate /*
620Sstevel@tonic-gate  * dhcp_adopt(): adopts the interface managed by the kernel for diskless boot
630Sstevel@tonic-gate  *
640Sstevel@tonic-gate  *   input: void
653431Scarlsonj  *  output: boolean_t: B_TRUE success, B_FALSE on failure
660Sstevel@tonic-gate  */
670Sstevel@tonic-gate 
683431Scarlsonj boolean_t
dhcp_adopt(void)690Sstevel@tonic-gate dhcp_adopt(void)
700Sstevel@tonic-gate {
710Sstevel@tonic-gate 	int		retval;
720Sstevel@tonic-gate 	dhcp_kcache_t	*kcache = NULL;
730Sstevel@tonic-gate 	size_t		kcache_size;
740Sstevel@tonic-gate 	PKT_LIST	*plp = NULL;
753431Scarlsonj 	dhcp_lif_t	*lif;
763431Scarlsonj 	dhcp_smach_t	*dsmp = NULL;
773431Scarlsonj 	uint_t		client_id_len;
780Sstevel@tonic-gate 
790Sstevel@tonic-gate 	retval = get_dhcp_kcache(&kcache, &kcache_size);
800Sstevel@tonic-gate 	if (retval == 0 || kcache_size < sizeof (dhcp_kcache_t)) {
810Sstevel@tonic-gate 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot fetch kernel cache");
820Sstevel@tonic-gate 		goto failure;
830Sstevel@tonic-gate 	}
840Sstevel@tonic-gate 
850Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "dhcp_adopt: fetched %s kcache", kcache->dk_if_name);
860Sstevel@tonic-gate 
870Sstevel@tonic-gate 	/*
880Sstevel@tonic-gate 	 * convert the kernel's ACK into binary
890Sstevel@tonic-gate 	 */
900Sstevel@tonic-gate 
913431Scarlsonj 	plp = alloc_pkt_entry(strlen(kcache->dk_ack) / 2, B_FALSE);
920Sstevel@tonic-gate 	if (plp == NULL)
930Sstevel@tonic-gate 		goto failure;
940Sstevel@tonic-gate 
950Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "dhcp_adopt: allocated ACK of %d bytes", plp->len);
960Sstevel@tonic-gate 
973431Scarlsonj 	if (hexascii_to_octet(kcache->dk_ack, plp->len * 2, plp->pkt,
983431Scarlsonj 	    &plp->len) != 0) {
990Sstevel@tonic-gate 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot convert kernel ACK");
1000Sstevel@tonic-gate 		goto failure;
1010Sstevel@tonic-gate 	}
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate 	if (dhcp_options_scan(plp, B_TRUE) != 0) {
1040Sstevel@tonic-gate 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot parse kernel ACK");
1050Sstevel@tonic-gate 		goto failure;
1060Sstevel@tonic-gate 	}
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate 	/*
1090Sstevel@tonic-gate 	 * make an interface to represent the "cached interface" in
1100Sstevel@tonic-gate 	 * the kernel, hook up the ACK packet we made, and send out
1110Sstevel@tonic-gate 	 * the extend request (to attempt to renew the lease).
1120Sstevel@tonic-gate 	 *
1130Sstevel@tonic-gate 	 * we do a send_extend() instead of doing a dhcp_init_reboot()
1140Sstevel@tonic-gate 	 * because although dhcp_init_reboot() is more correct from a
1150Sstevel@tonic-gate 	 * protocol perspective, it introduces a window where a
1160Sstevel@tonic-gate 	 * diskless client has no IP address but may need to page in
1170Sstevel@tonic-gate 	 * more of this program.  we could mlockall(), but that's
1180Sstevel@tonic-gate 	 * going to be a mess, especially with handling malloc() and
1190Sstevel@tonic-gate 	 * stack growth, so it's easier to just renew().  the only
1200Sstevel@tonic-gate 	 * catch here is that if we are not granted a renewal, we're
1210Sstevel@tonic-gate 	 * totally hosed and can only bail out.
1220Sstevel@tonic-gate 	 */
1230Sstevel@tonic-gate 
1243431Scarlsonj 	if ((lif = attach_lif(kcache->dk_if_name, B_FALSE, &retval)) == NULL) {
1253431Scarlsonj 		dhcpmsg(MSG_ERROR, "dhcp_adopt: unable to attach %s: %d",
1263431Scarlsonj 		    kcache->dk_if_name, retval);
1273431Scarlsonj 		goto failure;
1283431Scarlsonj 	}
1293431Scarlsonj 
1303431Scarlsonj 	if ((dsmp = insert_smach(lif, &retval)) == NULL) {
1313431Scarlsonj 		dhcpmsg(MSG_ERROR, "dhcp_adopt: unable to create state "
1323431Scarlsonj 		    "machine for %s: %d", kcache->dk_if_name, retval);
1333431Scarlsonj 		goto failure;
1343431Scarlsonj 	}
1353431Scarlsonj 
1363431Scarlsonj 	/*
1373431Scarlsonj 	 * If the agent is adopting a lease, then OBP is initially
1383431Scarlsonj 	 * searched for a client-id.
1393431Scarlsonj 	 */
1403431Scarlsonj 
1413431Scarlsonj 	dhcpmsg(MSG_DEBUG, "dhcp_adopt: getting /chosen:clientid property");
1423431Scarlsonj 
1433431Scarlsonj 	client_id_len = 0;
1443431Scarlsonj 	if (!get_prom_prop("chosen", "client-id", &dsmp->dsm_cid,
1453431Scarlsonj 	    &client_id_len)) {
1463431Scarlsonj 		/*
1473431Scarlsonj 		 * a failure occurred trying to acquire the client-id
1483431Scarlsonj 		 */
1493431Scarlsonj 
1503431Scarlsonj 		dhcpmsg(MSG_DEBUG,
1513431Scarlsonj 		    "dhcp_adopt: cannot allocate client id for %s",
1523431Scarlsonj 		    dsmp->dsm_name);
1533431Scarlsonj 		goto failure;
1543431Scarlsonj 	} else if (dsmp->dsm_hwtype == ARPHRD_IB && dsmp->dsm_cid == NULL) {
1553431Scarlsonj 		/*
1563431Scarlsonj 		 * when the interface is infiniband and the agent
1573431Scarlsonj 		 * is adopting the lease there must be an OBP
1583431Scarlsonj 		 * client-id.
1593431Scarlsonj 		 */
1603431Scarlsonj 
1613431Scarlsonj 		dhcpmsg(MSG_DEBUG, "dhcp_adopt: no /chosen:clientid id for %s",
1623431Scarlsonj 		    dsmp->dsm_name);
1633431Scarlsonj 		goto failure;
1643431Scarlsonj 	}
1653431Scarlsonj 
1663431Scarlsonj 	dsmp->dsm_cidlen = client_id_len;
1673431Scarlsonj 
168*10785SPeter.Memishian@Sun.COM 	if (set_lif_dhcp(lif) != DHCP_IPC_SUCCESS)
1690Sstevel@tonic-gate 		goto failure;
1700Sstevel@tonic-gate 
1713431Scarlsonj 	if (!set_smach_state(dsmp, ADOPTING))
1723431Scarlsonj 		goto failure;
1733431Scarlsonj 	dsmp->dsm_dflags = DHCP_IF_PRIMARY;
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate 	/*
1762612Scarlsonj 	 * move to BOUND and use the information in our ACK packet.
1772612Scarlsonj 	 * adoption will continue after DAD via dhcp_adopt_complete.
1780Sstevel@tonic-gate 	 */
1790Sstevel@tonic-gate 
1803431Scarlsonj 	if (!dhcp_bound(dsmp, plp)) {
1810Sstevel@tonic-gate 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot use cached packet");
1820Sstevel@tonic-gate 		goto failure;
1830Sstevel@tonic-gate 	}
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 	free(kcache);
1863431Scarlsonj 	return (B_TRUE);
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate failure:
1893431Scarlsonj 	/* Note: no need to free lif; dsmp holds reference */
1903431Scarlsonj 	if (dsmp != NULL)
1913522Scarlsonj 		remove_smach(dsmp);
1920Sstevel@tonic-gate 	free(kcache);
1933431Scarlsonj 	free_pkt_entry(plp);
1943431Scarlsonj 	return (B_FALSE);
1950Sstevel@tonic-gate }
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate /*
1982612Scarlsonj  * dhcp_adopt_complete(): completes interface adoption process after kernel
1992612Scarlsonj  *			  duplicate address detection (DAD) is done.
2002612Scarlsonj  *
2013431Scarlsonj  *   input: dhcp_smach_t *: the state machine on which a lease is being adopted
2022612Scarlsonj  *  output: none
2032612Scarlsonj  */
2042612Scarlsonj 
2052612Scarlsonj void
dhcp_adopt_complete(dhcp_smach_t * dsmp)2063431Scarlsonj dhcp_adopt_complete(dhcp_smach_t *dsmp)
2072612Scarlsonj {
2082612Scarlsonj 	dhcpmsg(MSG_DEBUG, "dhcp_adopt_complete: completing adoption");
2092612Scarlsonj 
2103431Scarlsonj 	if (async_start(dsmp, DHCP_EXTEND, B_FALSE) == 0) {
2112612Scarlsonj 		dhcpmsg(MSG_CRIT, "dhcp_adopt_complete: async_start failed");
2122612Scarlsonj 		return;
2132612Scarlsonj 	}
2142612Scarlsonj 
2153431Scarlsonj 	if (dhcp_extending(dsmp) == 0) {
2162612Scarlsonj 		dhcpmsg(MSG_CRIT,
2172612Scarlsonj 		    "dhcp_adopt_complete: cannot send renew request");
2182612Scarlsonj 		return;
2192612Scarlsonj 	}
2202612Scarlsonj 
2212612Scarlsonj 	if (grandparent != (pid_t)0) {
2223431Scarlsonj 		dhcpmsg(MSG_DEBUG, "adoption complete, signalling parent (%ld)"
2232612Scarlsonj 		    " to exit.", grandparent);
2242612Scarlsonj 		(void) kill(grandparent, SIGALRM);
2252612Scarlsonj 	}
2262612Scarlsonj }
2272612Scarlsonj 
2282612Scarlsonj /*
2290Sstevel@tonic-gate  * get_dhcp_kcache(): fetches the DHCP ACK and interface name from the kernel
2300Sstevel@tonic-gate  *
2310Sstevel@tonic-gate  *   input: dhcp_kcache_t **: a dynamically-allocated cache packet
2320Sstevel@tonic-gate  *	    size_t *: the length of that packet (on return)
2330Sstevel@tonic-gate  *  output: int: nonzero on success, zero on failure
2340Sstevel@tonic-gate  */
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate static int
get_dhcp_kcache(dhcp_kcache_t ** kernel_cachep,size_t * kcache_size)2370Sstevel@tonic-gate get_dhcp_kcache(dhcp_kcache_t **kernel_cachep, size_t *kcache_size)
2380Sstevel@tonic-gate {
2390Sstevel@tonic-gate 	char	dummy;
2400Sstevel@tonic-gate 	long	size;
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	size = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy));
2430Sstevel@tonic-gate 	if (size == -1)
2440Sstevel@tonic-gate 		return (0);
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate 	*kcache_size   = size;
2470Sstevel@tonic-gate 	*kernel_cachep = malloc(*kcache_size);
2480Sstevel@tonic-gate 	if (*kernel_cachep == NULL)
2490Sstevel@tonic-gate 		return (0);
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 	(void) sysinfo(SI_DHCP_CACHE, (caddr_t)*kernel_cachep, size);
2520Sstevel@tonic-gate 	return (1);
2530Sstevel@tonic-gate }
2543431Scarlsonj 
2553431Scarlsonj /*
2563431Scarlsonj  * get_prom_prop(): get the value of the named property on the named node in
2573431Scarlsonj  *		    devinfo root.
2583431Scarlsonj  *
2593431Scarlsonj  *   input: const char *: The name of the node containing the property.
2603431Scarlsonj  *	    const char *: The name of the property.
2613431Scarlsonj  *	    uchar_t **: The property value, modified iff B_TRUE is returned.
2623431Scarlsonj  *                      If no value is found the value is set to NULL.
2633431Scarlsonj  *	    uint_t *: The length of the property value
2643431Scarlsonj  *  output: boolean_t: Returns B_TRUE if successful (no problems),
2653431Scarlsonj  *                     otherwise B_FALSE.
2663431Scarlsonj  *    note: The memory allocated by this function must be freed by
2673431Scarlsonj  *          the caller. This code is derived from
2683431Scarlsonj  *          usr/src/lib/libwanboot/common/bootinfo_aux.c.
2693431Scarlsonj  */
2703431Scarlsonj 
2713431Scarlsonj static boolean_t
get_prom_prop(const char * nodename,const char * propname,uchar_t ** propvaluep,uint_t * lenp)2723431Scarlsonj get_prom_prop(const char *nodename, const char *propname, uchar_t **propvaluep,
2733431Scarlsonj     uint_t *lenp)
2743431Scarlsonj {
2753431Scarlsonj 	di_node_t		root_node;
2763431Scarlsonj 	di_node_t		node;
2773431Scarlsonj 	di_prom_handle_t	phdl = DI_PROM_HANDLE_NIL;
2783431Scarlsonj 	di_prom_prop_t		pp;
2793431Scarlsonj 	uchar_t			*value = NULL;
2803431Scarlsonj 	unsigned int		len = 0;
2813431Scarlsonj 	boolean_t		success = B_TRUE;
2823431Scarlsonj 
2833431Scarlsonj 	/*
2843431Scarlsonj 	 * locate root node
2853431Scarlsonj 	 */
2863431Scarlsonj 
2873431Scarlsonj 	if ((root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL ||
2883431Scarlsonj 	    (phdl = di_prom_init()) == DI_PROM_HANDLE_NIL) {
2893431Scarlsonj 		dhcpmsg(MSG_DEBUG, "get_prom_prop: property root node "
2903431Scarlsonj 		    "not found");
2913431Scarlsonj 		goto get_prom_prop_cleanup;
2923431Scarlsonj 	}
2933431Scarlsonj 
2943431Scarlsonj 	/*
2953431Scarlsonj 	 * locate nodename within '/'
2963431Scarlsonj 	 */
2973431Scarlsonj 
2983431Scarlsonj 	for (node = di_child_node(root_node);
2993431Scarlsonj 	    node != DI_NODE_NIL;
3003431Scarlsonj 	    node = di_sibling_node(node)) {
3013431Scarlsonj 		if (strcmp(di_node_name(node), nodename) == 0) {
3023431Scarlsonj 			break;
3033431Scarlsonj 		}
3043431Scarlsonj 	}
3053431Scarlsonj 
3063431Scarlsonj 	if (node == DI_NODE_NIL) {
3073431Scarlsonj 		dhcpmsg(MSG_DEBUG, "get_prom_prop: node not found");
3083431Scarlsonj 		goto get_prom_prop_cleanup;
3093431Scarlsonj 	}
3103431Scarlsonj 
3113431Scarlsonj 	/*
3123431Scarlsonj 	 * scan all properties of /nodename for the 'propname' property
3133431Scarlsonj 	 */
3143431Scarlsonj 
3153431Scarlsonj 	for (pp = di_prom_prop_next(phdl, node, DI_PROM_PROP_NIL);
3163431Scarlsonj 	    pp != DI_PROM_PROP_NIL;
3173431Scarlsonj 	    pp = di_prom_prop_next(phdl, node, pp)) {
3183431Scarlsonj 
3193431Scarlsonj 		dhcpmsg(MSG_DEBUG, "get_prom_prop: property = %s",
3203431Scarlsonj 		    di_prom_prop_name(pp));
3213431Scarlsonj 
3223431Scarlsonj 		if (strcmp(propname, di_prom_prop_name(pp)) == 0) {
3233431Scarlsonj 			break;
3243431Scarlsonj 		}
3253431Scarlsonj 	}
3263431Scarlsonj 
3273431Scarlsonj 	if (pp == DI_PROM_PROP_NIL) {
3283431Scarlsonj 		dhcpmsg(MSG_DEBUG, "get_prom_prop: property not found");
3293431Scarlsonj 		goto get_prom_prop_cleanup;
3303431Scarlsonj 	}
3313431Scarlsonj 
3323431Scarlsonj 	/*
3333431Scarlsonj 	 * get the property; allocate some memory copy it out
3343431Scarlsonj 	 */
3353431Scarlsonj 
3363431Scarlsonj 	len = di_prom_prop_data(pp, (uchar_t **)&value);
3373431Scarlsonj 
3383431Scarlsonj 	if (value == NULL) {
3393431Scarlsonj 		/*
3403431Scarlsonj 		 * property data read problems
3413431Scarlsonj 		 */
3423431Scarlsonj 
3433431Scarlsonj 		success = B_FALSE;
3443431Scarlsonj 		dhcpmsg(MSG_ERR, "get_prom_prop: cannot read property data");
3453431Scarlsonj 		goto get_prom_prop_cleanup;
3463431Scarlsonj 	}
3473431Scarlsonj 
3483431Scarlsonj 	if (propvaluep != NULL) {
3493431Scarlsonj 		/*
3503431Scarlsonj 		 * allocate somewhere to copy the property value to
3513431Scarlsonj 		 */
3523431Scarlsonj 
3533431Scarlsonj 		*propvaluep = calloc(len, sizeof (uchar_t));
3543431Scarlsonj 
3553431Scarlsonj 		if (*propvaluep == NULL) {
3563431Scarlsonj 			/*
3573431Scarlsonj 			 * allocation problems
3583431Scarlsonj 			 */
3593431Scarlsonj 
3603431Scarlsonj 			success = B_FALSE;
3613431Scarlsonj 			dhcpmsg(MSG_ERR, "get_prom_prop: cannot allocate "
3623431Scarlsonj 			    "memory for property value");
3633431Scarlsonj 			goto get_prom_prop_cleanup;
3643431Scarlsonj 		}
3653431Scarlsonj 
3663431Scarlsonj 		/*
3673431Scarlsonj 		 * copy data out
3683431Scarlsonj 		 */
3693431Scarlsonj 
3703431Scarlsonj 		(void) memcpy(*propvaluep, value, len);
3713431Scarlsonj 
3723431Scarlsonj 		/*
3733431Scarlsonj 		 * copy out the length if a suitable pointer has
3743431Scarlsonj 		 * been supplied
3753431Scarlsonj 		 */
3763431Scarlsonj 
3773431Scarlsonj 		if (lenp != NULL) {
3783431Scarlsonj 			*lenp = len;
3793431Scarlsonj 		}
3803431Scarlsonj 
3813431Scarlsonj 		dhcpmsg(MSG_DEBUG, "get_prom_prop: property value "
3823431Scarlsonj 		    "length = %d", len);
3833431Scarlsonj 	}
3843431Scarlsonj 
3853431Scarlsonj get_prom_prop_cleanup:
3863431Scarlsonj 
3873431Scarlsonj 	if (phdl != DI_PROM_HANDLE_NIL) {
3883431Scarlsonj 		di_prom_fini(phdl);
3893431Scarlsonj 	}
3903431Scarlsonj 
3913431Scarlsonj 	if (root_node != DI_NODE_NIL) {
3923431Scarlsonj 		di_fini(root_node);
3933431Scarlsonj 	}
3943431Scarlsonj 
3953431Scarlsonj 	return (success);
3963431Scarlsonj }
397