xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/adopt.c (revision 3431:9f2d277dcffa)
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