xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/adopt.c (revision 2612:c33643c09b32)
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
5*2612Scarlsonj  * Common Development and Distribution License (the "License").
6*2612Scarlsonj  * 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*2612Scarlsonj  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  *
250Sstevel@tonic-gate  * ADOPTING state of the client state machine.
260Sstevel@tonic-gate  */
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
290Sstevel@tonic-gate 
300Sstevel@tonic-gate #include <sys/types.h>
310Sstevel@tonic-gate #include <string.h>
320Sstevel@tonic-gate #include <unistd.h>
330Sstevel@tonic-gate #include <stdlib.h>
34*2612Scarlsonj #include <signal.h>
350Sstevel@tonic-gate #include <sys/sockio.h>
360Sstevel@tonic-gate #include <sys/socket.h>
370Sstevel@tonic-gate #include <netinet/in.h>
380Sstevel@tonic-gate #include <sys/systeminfo.h>
390Sstevel@tonic-gate #include <netinet/inetutil.h>
400Sstevel@tonic-gate #include <netinet/dhcp.h>
410Sstevel@tonic-gate #include <dhcpmsg.h>
420Sstevel@tonic-gate 
430Sstevel@tonic-gate #include "async.h"
440Sstevel@tonic-gate #include "util.h"
450Sstevel@tonic-gate #include "packet.h"
460Sstevel@tonic-gate #include "interface.h"
470Sstevel@tonic-gate #include "states.h"
480Sstevel@tonic-gate 
490Sstevel@tonic-gate 
500Sstevel@tonic-gate typedef struct {
510Sstevel@tonic-gate 	char		dk_if_name[IFNAMSIZ];
520Sstevel@tonic-gate 	char		dk_ack[1];
530Sstevel@tonic-gate } dhcp_kcache_t;
540Sstevel@tonic-gate 
550Sstevel@tonic-gate static int	get_dhcp_kcache(dhcp_kcache_t **, size_t *);
560Sstevel@tonic-gate 
570Sstevel@tonic-gate /*
580Sstevel@tonic-gate  * dhcp_adopt(): adopts the interface managed by the kernel for diskless boot
590Sstevel@tonic-gate  *
600Sstevel@tonic-gate  *   input: void
610Sstevel@tonic-gate  *  output: int: nonzero on success, zero on failure
620Sstevel@tonic-gate  */
630Sstevel@tonic-gate 
640Sstevel@tonic-gate int
650Sstevel@tonic-gate dhcp_adopt(void)
660Sstevel@tonic-gate {
670Sstevel@tonic-gate 	int		retval;
680Sstevel@tonic-gate 	dhcp_kcache_t	*kcache = NULL;
690Sstevel@tonic-gate 	size_t		kcache_size;
700Sstevel@tonic-gate 	PKT_LIST	*plp = NULL;
710Sstevel@tonic-gate 	struct ifslist	*ifsp;
720Sstevel@tonic-gate 
730Sstevel@tonic-gate 	retval = get_dhcp_kcache(&kcache, &kcache_size);
740Sstevel@tonic-gate 	if (retval == 0 || kcache_size < sizeof (dhcp_kcache_t)) {
750Sstevel@tonic-gate 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot fetch kernel cache");
760Sstevel@tonic-gate 		goto failure;
770Sstevel@tonic-gate 	}
780Sstevel@tonic-gate 
790Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "dhcp_adopt: fetched %s kcache", kcache->dk_if_name);
800Sstevel@tonic-gate 
810Sstevel@tonic-gate 	/*
820Sstevel@tonic-gate 	 * convert the kernel's ACK into binary
830Sstevel@tonic-gate 	 */
840Sstevel@tonic-gate 
850Sstevel@tonic-gate 	plp = calloc(1, sizeof (PKT_LIST));
860Sstevel@tonic-gate 	if (plp == NULL)
870Sstevel@tonic-gate 		goto failure;
880Sstevel@tonic-gate 
890Sstevel@tonic-gate 	plp->len = strlen(kcache->dk_ack) / 2;
900Sstevel@tonic-gate 	plp->pkt = malloc(plp->len);
910Sstevel@tonic-gate 	if (plp->pkt == NULL)
920Sstevel@tonic-gate 		goto failure;
930Sstevel@tonic-gate 
940Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "dhcp_adopt: allocated ACK of %d bytes", plp->len);
950Sstevel@tonic-gate 
960Sstevel@tonic-gate 	if (hexascii_to_octet(kcache->dk_ack, plp->len * 2, plp->pkt, &plp->len)
970Sstevel@tonic-gate 	    != 0) {
980Sstevel@tonic-gate 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot convert kernel ACK");
990Sstevel@tonic-gate 		goto failure;
1000Sstevel@tonic-gate 	}
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate 	if (dhcp_options_scan(plp, B_TRUE) != 0) {
1030Sstevel@tonic-gate 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot parse kernel ACK");
1040Sstevel@tonic-gate 		goto failure;
1050Sstevel@tonic-gate 	}
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate 	/*
1080Sstevel@tonic-gate 	 * make an interface to represent the "cached interface" in
1090Sstevel@tonic-gate 	 * the kernel, hook up the ACK packet we made, and send out
1100Sstevel@tonic-gate 	 * the extend request (to attempt to renew the lease).
1110Sstevel@tonic-gate 	 *
1120Sstevel@tonic-gate 	 * we do a send_extend() instead of doing a dhcp_init_reboot()
1130Sstevel@tonic-gate 	 * because although dhcp_init_reboot() is more correct from a
1140Sstevel@tonic-gate 	 * protocol perspective, it introduces a window where a
1150Sstevel@tonic-gate 	 * diskless client has no IP address but may need to page in
1160Sstevel@tonic-gate 	 * more of this program.  we could mlockall(), but that's
1170Sstevel@tonic-gate 	 * going to be a mess, especially with handling malloc() and
1180Sstevel@tonic-gate 	 * stack growth, so it's easier to just renew().  the only
1190Sstevel@tonic-gate 	 * catch here is that if we are not granted a renewal, we're
1200Sstevel@tonic-gate 	 * totally hosed and can only bail out.
1210Sstevel@tonic-gate 	 */
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate 	ifsp = insert_ifs(kcache->dk_if_name, B_TRUE, &retval);
1240Sstevel@tonic-gate 	if (ifsp == NULL)
1250Sstevel@tonic-gate 		goto failure;
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate 	ifsp->if_state   = ADOPTING;
1280Sstevel@tonic-gate 	ifsp->if_dflags |= DHCP_IF_PRIMARY;
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate 	/*
131*2612Scarlsonj 	 * move to BOUND and use the information in our ACK packet.
132*2612Scarlsonj 	 * adoption will continue after DAD via dhcp_adopt_complete.
1330Sstevel@tonic-gate 	 */
1340Sstevel@tonic-gate 
1350Sstevel@tonic-gate 	if (dhcp_bound(ifsp, plp) == 0) {
1360Sstevel@tonic-gate 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot use cached packet");
1370Sstevel@tonic-gate 		goto failure;
1380Sstevel@tonic-gate 	}
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate 	free(kcache);
1410Sstevel@tonic-gate 	return (1);
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate failure:
1440Sstevel@tonic-gate 	free(kcache);
1450Sstevel@tonic-gate 	if (plp != NULL)
1460Sstevel@tonic-gate 		free(plp->pkt);
1470Sstevel@tonic-gate 	free(plp);
1480Sstevel@tonic-gate 	return (0);
1490Sstevel@tonic-gate }
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate /*
152*2612Scarlsonj  * dhcp_adopt_complete(): completes interface adoption process after kernel
153*2612Scarlsonj  *			  duplicate address detection (DAD) is done.
154*2612Scarlsonj  *
155*2612Scarlsonj  *   input: struct ifslist *: the interface on which a lease is being adopted
156*2612Scarlsonj  *  output: none
157*2612Scarlsonj  */
158*2612Scarlsonj 
159*2612Scarlsonj void
160*2612Scarlsonj dhcp_adopt_complete(struct ifslist *ifsp)
161*2612Scarlsonj {
162*2612Scarlsonj 	dhcpmsg(MSG_DEBUG, "dhcp_adopt_complete: completing adoption");
163*2612Scarlsonj 
164*2612Scarlsonj 	if (async_start(ifsp, DHCP_EXTEND, B_FALSE) == 0) {
165*2612Scarlsonj 		dhcpmsg(MSG_CRIT, "dhcp_adopt_complete: async_start failed");
166*2612Scarlsonj 		return;
167*2612Scarlsonj 	}
168*2612Scarlsonj 
169*2612Scarlsonj 	if (dhcp_extending(ifsp) == 0) {
170*2612Scarlsonj 		dhcpmsg(MSG_CRIT,
171*2612Scarlsonj 		    "dhcp_adopt_complete: cannot send renew request");
172*2612Scarlsonj 		return;
173*2612Scarlsonj 	}
174*2612Scarlsonj 
175*2612Scarlsonj 	if (grandparent != (pid_t)0) {
176*2612Scarlsonj 		dhcpmsg(MSG_DEBUG, "adoption complete, signalling parent (%i)"
177*2612Scarlsonj 		    " to exit.", grandparent);
178*2612Scarlsonj 		(void) kill(grandparent, SIGALRM);
179*2612Scarlsonj 	}
180*2612Scarlsonj }
181*2612Scarlsonj 
182*2612Scarlsonj /*
1830Sstevel@tonic-gate  * get_dhcp_kcache(): fetches the DHCP ACK and interface name from the kernel
1840Sstevel@tonic-gate  *
1850Sstevel@tonic-gate  *   input: dhcp_kcache_t **: a dynamically-allocated cache packet
1860Sstevel@tonic-gate  *	    size_t *: the length of that packet (on return)
1870Sstevel@tonic-gate  *  output: int: nonzero on success, zero on failure
1880Sstevel@tonic-gate  */
1890Sstevel@tonic-gate 
1900Sstevel@tonic-gate static int
1910Sstevel@tonic-gate get_dhcp_kcache(dhcp_kcache_t **kernel_cachep, size_t *kcache_size)
1920Sstevel@tonic-gate {
1930Sstevel@tonic-gate 	char	dummy;
1940Sstevel@tonic-gate 	long	size;
1950Sstevel@tonic-gate 
1960Sstevel@tonic-gate 	size = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy));
1970Sstevel@tonic-gate 	if (size == -1)
1980Sstevel@tonic-gate 		return (0);
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 	*kcache_size   = size;
2010Sstevel@tonic-gate 	*kernel_cachep = malloc(*kcache_size);
2020Sstevel@tonic-gate 	if (*kernel_cachep == NULL)
2030Sstevel@tonic-gate 		return (0);
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate 	(void) sysinfo(SI_DHCP_CACHE, (caddr_t)*kernel_cachep, size);
2060Sstevel@tonic-gate 	return (1);
2070Sstevel@tonic-gate }
208