Home | History | Annotate | Download | only in dhcpagent
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  *
     25  * ADOPTING state of the client state machine.  This is used only during
     26  * diskless boot with IPv4.
     27  */
     28 
     29 #include <sys/types.h>
     30 #include <string.h>
     31 #include <unistd.h>
     32 #include <stdlib.h>
     33 #include <signal.h>
     34 #include <sys/socket.h>
     35 #include <net/if_arp.h>
     36 #include <netinet/in.h>
     37 #include <sys/systeminfo.h>
     38 #include <netinet/inetutil.h>
     39 #include <netinet/dhcp.h>
     40 #include <dhcpmsg.h>
     41 #include <libdevinfo.h>
     42 
     43 #include "agent.h"
     44 #include "async.h"
     45 #include "util.h"
     46 #include "packet.h"
     47 #include "interface.h"
     48 #include "states.h"
     49 
     50 
     51 typedef struct {
     52 	char		dk_if_name[IFNAMSIZ];
     53 	char		dk_ack[1];
     54 } dhcp_kcache_t;
     55 
     56 static int	get_dhcp_kcache(dhcp_kcache_t **, size_t *);
     57 
     58 static boolean_t	get_prom_prop(const char *, const char *, uchar_t **,
     59 			    uint_t *);
     60 
     61 /*
     62  * dhcp_adopt(): adopts the interface managed by the kernel for diskless boot
     63  *
     64  *   input: void
     65  *  output: boolean_t: B_TRUE success, B_FALSE on failure
     66  */
     67 
     68 boolean_t
     69 dhcp_adopt(void)
     70 {
     71 	int		retval;
     72 	dhcp_kcache_t	*kcache = NULL;
     73 	size_t		kcache_size;
     74 	PKT_LIST	*plp = NULL;
     75 	dhcp_lif_t	*lif;
     76 	dhcp_smach_t	*dsmp = NULL;
     77 	uint_t		client_id_len;
     78 
     79 	retval = get_dhcp_kcache(&kcache, &kcache_size);
     80 	if (retval == 0 || kcache_size < sizeof (dhcp_kcache_t)) {
     81 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot fetch kernel cache");
     82 		goto failure;
     83 	}
     84 
     85 	dhcpmsg(MSG_DEBUG, "dhcp_adopt: fetched %s kcache", kcache->dk_if_name);
     86 
     87 	/*
     88 	 * convert the kernel's ACK into binary
     89 	 */
     90 
     91 	plp = alloc_pkt_entry(strlen(kcache->dk_ack) / 2, B_FALSE);
     92 	if (plp == NULL)
     93 		goto failure;
     94 
     95 	dhcpmsg(MSG_DEBUG, "dhcp_adopt: allocated ACK of %d bytes", plp->len);
     96 
     97 	if (hexascii_to_octet(kcache->dk_ack, plp->len * 2, plp->pkt,
     98 	    &plp->len) != 0) {
     99 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot convert kernel ACK");
    100 		goto failure;
    101 	}
    102 
    103 	if (dhcp_options_scan(plp, B_TRUE) != 0) {
    104 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot parse kernel ACK");
    105 		goto failure;
    106 	}
    107 
    108 	/*
    109 	 * make an interface to represent the "cached interface" in
    110 	 * the kernel, hook up the ACK packet we made, and send out
    111 	 * the extend request (to attempt to renew the lease).
    112 	 *
    113 	 * we do a send_extend() instead of doing a dhcp_init_reboot()
    114 	 * because although dhcp_init_reboot() is more correct from a
    115 	 * protocol perspective, it introduces a window where a
    116 	 * diskless client has no IP address but may need to page in
    117 	 * more of this program.  we could mlockall(), but that's
    118 	 * going to be a mess, especially with handling malloc() and
    119 	 * stack growth, so it's easier to just renew().  the only
    120 	 * catch here is that if we are not granted a renewal, we're
    121 	 * totally hosed and can only bail out.
    122 	 */
    123 
    124 	if ((lif = attach_lif(kcache->dk_if_name, B_FALSE, &retval)) == NULL) {
    125 		dhcpmsg(MSG_ERROR, "dhcp_adopt: unable to attach %s: %d",
    126 		    kcache->dk_if_name, retval);
    127 		goto failure;
    128 	}
    129 
    130 	if ((dsmp = insert_smach(lif, &retval)) == NULL) {
    131 		dhcpmsg(MSG_ERROR, "dhcp_adopt: unable to create state "
    132 		    "machine for %s: %d", kcache->dk_if_name, retval);
    133 		goto failure;
    134 	}
    135 
    136 	/*
    137 	 * If the agent is adopting a lease, then OBP is initially
    138 	 * searched for a client-id.
    139 	 */
    140 
    141 	dhcpmsg(MSG_DEBUG, "dhcp_adopt: getting /chosen:clientid property");
    142 
    143 	client_id_len = 0;
    144 	if (!get_prom_prop("chosen", "client-id", &dsmp->dsm_cid,
    145 	    &client_id_len)) {
    146 		/*
    147 		 * a failure occurred trying to acquire the client-id
    148 		 */
    149 
    150 		dhcpmsg(MSG_DEBUG,
    151 		    "dhcp_adopt: cannot allocate client id for %s",
    152 		    dsmp->dsm_name);
    153 		goto failure;
    154 	} else if (dsmp->dsm_hwtype == ARPHRD_IB && dsmp->dsm_cid == NULL) {
    155 		/*
    156 		 * when the interface is infiniband and the agent
    157 		 * is adopting the lease there must be an OBP
    158 		 * client-id.
    159 		 */
    160 
    161 		dhcpmsg(MSG_DEBUG, "dhcp_adopt: no /chosen:clientid id for %s",
    162 		    dsmp->dsm_name);
    163 		goto failure;
    164 	}
    165 
    166 	dsmp->dsm_cidlen = client_id_len;
    167 
    168 	if (set_lif_dhcp(lif) != DHCP_IPC_SUCCESS)
    169 		goto failure;
    170 
    171 	if (!set_smach_state(dsmp, ADOPTING))
    172 		goto failure;
    173 	dsmp->dsm_dflags = DHCP_IF_PRIMARY;
    174 
    175 	/*
    176 	 * move to BOUND and use the information in our ACK packet.
    177 	 * adoption will continue after DAD via dhcp_adopt_complete.
    178 	 */
    179 
    180 	if (!dhcp_bound(dsmp, plp)) {
    181 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot use cached packet");
    182 		goto failure;
    183 	}
    184 
    185 	free(kcache);
    186 	return (B_TRUE);
    187 
    188 failure:
    189 	/* Note: no need to free lif; dsmp holds reference */
    190 	if (dsmp != NULL)
    191 		remove_smach(dsmp);
    192 	free(kcache);
    193 	free_pkt_entry(plp);
    194 	return (B_FALSE);
    195 }
    196 
    197 /*
    198  * dhcp_adopt_complete(): completes interface adoption process after kernel
    199  *			  duplicate address detection (DAD) is done.
    200  *
    201  *   input: dhcp_smach_t *: the state machine on which a lease is being adopted
    202  *  output: none
    203  */
    204 
    205 void
    206 dhcp_adopt_complete(dhcp_smach_t *dsmp)
    207 {
    208 	dhcpmsg(MSG_DEBUG, "dhcp_adopt_complete: completing adoption");
    209 
    210 	if (async_start(dsmp, DHCP_EXTEND, B_FALSE) == 0) {
    211 		dhcpmsg(MSG_CRIT, "dhcp_adopt_complete: async_start failed");
    212 		return;
    213 	}
    214 
    215 	if (dhcp_extending(dsmp) == 0) {
    216 		dhcpmsg(MSG_CRIT,
    217 		    "dhcp_adopt_complete: cannot send renew request");
    218 		return;
    219 	}
    220 
    221 	if (grandparent != (pid_t)0) {
    222 		dhcpmsg(MSG_DEBUG, "adoption complete, signalling parent (%ld)"
    223 		    " to exit.", grandparent);
    224 		(void) kill(grandparent, SIGALRM);
    225 	}
    226 }
    227 
    228 /*
    229  * get_dhcp_kcache(): fetches the DHCP ACK and interface name from the kernel
    230  *
    231  *   input: dhcp_kcache_t **: a dynamically-allocated cache packet
    232  *	    size_t *: the length of that packet (on return)
    233  *  output: int: nonzero on success, zero on failure
    234  */
    235 
    236 static int
    237 get_dhcp_kcache(dhcp_kcache_t **kernel_cachep, size_t *kcache_size)
    238 {
    239 	char	dummy;
    240 	long	size;
    241 
    242 	size = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy));
    243 	if (size == -1)
    244 		return (0);
    245 
    246 	*kcache_size   = size;
    247 	*kernel_cachep = malloc(*kcache_size);
    248 	if (*kernel_cachep == NULL)
    249 		return (0);
    250 
    251 	(void) sysinfo(SI_DHCP_CACHE, (caddr_t)*kernel_cachep, size);
    252 	return (1);
    253 }
    254 
    255 /*
    256  * get_prom_prop(): get the value of the named property on the named node in
    257  *		    devinfo root.
    258  *
    259  *   input: const char *: The name of the node containing the property.
    260  *	    const char *: The name of the property.
    261  *	    uchar_t **: The property value, modified iff B_TRUE is returned.
    262  *                      If no value is found the value is set to NULL.
    263  *	    uint_t *: The length of the property value
    264  *  output: boolean_t: Returns B_TRUE if successful (no problems),
    265  *                     otherwise B_FALSE.
    266  *    note: The memory allocated by this function must be freed by
    267  *          the caller. This code is derived from
    268  *          usr/src/lib/libwanboot/common/bootinfo_aux.c.
    269  */
    270 
    271 static boolean_t
    272 get_prom_prop(const char *nodename, const char *propname, uchar_t **propvaluep,
    273     uint_t *lenp)
    274 {
    275 	di_node_t		root_node;
    276 	di_node_t		node;
    277 	di_prom_handle_t	phdl = DI_PROM_HANDLE_NIL;
    278 	di_prom_prop_t		pp;
    279 	uchar_t			*value = NULL;
    280 	unsigned int		len = 0;
    281 	boolean_t		success = B_TRUE;
    282 
    283 	/*
    284 	 * locate root node
    285 	 */
    286 
    287 	if ((root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL ||
    288 	    (phdl = di_prom_init()) == DI_PROM_HANDLE_NIL) {
    289 		dhcpmsg(MSG_DEBUG, "get_prom_prop: property root node "
    290 		    "not found");
    291 		goto get_prom_prop_cleanup;
    292 	}
    293 
    294 	/*
    295 	 * locate nodename within '/'
    296 	 */
    297 
    298 	for (node = di_child_node(root_node);
    299 	    node != DI_NODE_NIL;
    300 	    node = di_sibling_node(node)) {
    301 		if (strcmp(di_node_name(node), nodename) == 0) {
    302 			break;
    303 		}
    304 	}
    305 
    306 	if (node == DI_NODE_NIL) {
    307 		dhcpmsg(MSG_DEBUG, "get_prom_prop: node not found");
    308 		goto get_prom_prop_cleanup;
    309 	}
    310 
    311 	/*
    312 	 * scan all properties of /nodename for the 'propname' property
    313 	 */
    314 
    315 	for (pp = di_prom_prop_next(phdl, node, DI_PROM_PROP_NIL);
    316 	    pp != DI_PROM_PROP_NIL;
    317 	    pp = di_prom_prop_next(phdl, node, pp)) {
    318 
    319 		dhcpmsg(MSG_DEBUG, "get_prom_prop: property = %s",
    320 		    di_prom_prop_name(pp));
    321 
    322 		if (strcmp(propname, di_prom_prop_name(pp)) == 0) {
    323 			break;
    324 		}
    325 	}
    326 
    327 	if (pp == DI_PROM_PROP_NIL) {
    328 		dhcpmsg(MSG_DEBUG, "get_prom_prop: property not found");
    329 		goto get_prom_prop_cleanup;
    330 	}
    331 
    332 	/*
    333 	 * get the property; allocate some memory copy it out
    334 	 */
    335 
    336 	len = di_prom_prop_data(pp, (uchar_t **)&value);
    337 
    338 	if (value == NULL) {
    339 		/*
    340 		 * property data read problems
    341 		 */
    342 
    343 		success = B_FALSE;
    344 		dhcpmsg(MSG_ERR, "get_prom_prop: cannot read property data");
    345 		goto get_prom_prop_cleanup;
    346 	}
    347 
    348 	if (propvaluep != NULL) {
    349 		/*
    350 		 * allocate somewhere to copy the property value to
    351 		 */
    352 
    353 		*propvaluep = calloc(len, sizeof (uchar_t));
    354 
    355 		if (*propvaluep == NULL) {
    356 			/*
    357 			 * allocation problems
    358 			 */
    359 
    360 			success = B_FALSE;
    361 			dhcpmsg(MSG_ERR, "get_prom_prop: cannot allocate "
    362 			    "memory for property value");
    363 			goto get_prom_prop_cleanup;
    364 		}
    365 
    366 		/*
    367 		 * copy data out
    368 		 */
    369 
    370 		(void) memcpy(*propvaluep, value, len);
    371 
    372 		/*
    373 		 * copy out the length if a suitable pointer has
    374 		 * been supplied
    375 		 */
    376 
    377 		if (lenp != NULL) {
    378 			*lenp = len;
    379 		}
    380 
    381 		dhcpmsg(MSG_DEBUG, "get_prom_prop: property value "
    382 		    "length = %d", len);
    383 	}
    384 
    385 get_prom_prop_cleanup:
    386 
    387 	if (phdl != DI_PROM_HANDLE_NIL) {
    388 		di_prom_fini(phdl);
    389 	}
    390 
    391 	if (root_node != DI_NODE_NIL) {
    392 		di_fini(root_node);
    393 	}
    394 
    395 	return (success);
    396 }
    397