8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / cmd-inet / sbin / dhcpagent / adopt.c
blobd81cb9264f8acbd7e6047da694d39142ec113c80
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 * ADOPTING state of the client state machine. This is used only during
26 * diskless boot with IPv4.
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>
43 #include "agent.h"
44 #include "async.h"
45 #include "util.h"
46 #include "packet.h"
47 #include "interface.h"
48 #include "states.h"
51 typedef struct {
52 char dk_if_name[IFNAMSIZ];
53 char dk_ack[1];
54 } dhcp_kcache_t;
56 static int get_dhcp_kcache(dhcp_kcache_t **, size_t *);
58 static boolean_t get_prom_prop(const char *, const char *, uchar_t **,
59 uint_t *);
62 * dhcp_adopt(): adopts the interface managed by the kernel for diskless boot
64 * input: void
65 * output: boolean_t: B_TRUE success, B_FALSE on failure
68 boolean_t
69 dhcp_adopt(void)
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;
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;
85 dhcpmsg(MSG_DEBUG, "dhcp_adopt: fetched %s kcache", kcache->dk_if_name);
88 * convert the kernel's ACK into binary
91 plp = alloc_pkt_entry(strlen(kcache->dk_ack) / 2, B_FALSE);
92 if (plp == NULL)
93 goto failure;
95 dhcpmsg(MSG_DEBUG, "dhcp_adopt: allocated ACK of %d bytes", plp->len);
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;
103 if (dhcp_options_scan(plp, B_TRUE) != 0) {
104 dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot parse kernel ACK");
105 goto failure;
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).
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.
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;
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;
137 * If the agent is adopting a lease, then OBP is initially
138 * searched for a client-id.
141 dhcpmsg(MSG_DEBUG, "dhcp_adopt: getting /chosen:clientid property");
143 client_id_len = 0;
144 if (!get_prom_prop("chosen", "client-id", &dsmp->dsm_cid,
145 &client_id_len)) {
147 * a failure occurred trying to acquire the client-id
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) {
156 * when the interface is infiniband and the agent
157 * is adopting the lease there must be an OBP
158 * client-id.
161 dhcpmsg(MSG_DEBUG, "dhcp_adopt: no /chosen:clientid id for %s",
162 dsmp->dsm_name);
163 goto failure;
166 dsmp->dsm_cidlen = client_id_len;
168 if (set_lif_dhcp(lif) != DHCP_IPC_SUCCESS)
169 goto failure;
171 if (!set_smach_state(dsmp, ADOPTING))
172 goto failure;
173 dsmp->dsm_dflags = DHCP_IF_PRIMARY;
176 * move to BOUND and use the information in our ACK packet.
177 * adoption will continue after DAD via dhcp_adopt_complete.
180 if (!dhcp_bound(dsmp, plp)) {
181 dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot use cached packet");
182 goto failure;
185 free(kcache);
186 return (B_TRUE);
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);
198 * dhcp_adopt_complete(): completes interface adoption process after kernel
199 * duplicate address detection (DAD) is done.
201 * input: dhcp_smach_t *: the state machine on which a lease is being adopted
202 * output: none
205 void
206 dhcp_adopt_complete(dhcp_smach_t *dsmp)
208 dhcpmsg(MSG_DEBUG, "dhcp_adopt_complete: completing adoption");
210 if (async_start(dsmp, DHCP_EXTEND, B_FALSE) == 0) {
211 dhcpmsg(MSG_CRIT, "dhcp_adopt_complete: async_start failed");
212 return;
215 if (dhcp_extending(dsmp) == 0) {
216 dhcpmsg(MSG_CRIT,
217 "dhcp_adopt_complete: cannot send renew request");
218 return;
221 if (grandparent != (pid_t)0) {
222 dhcpmsg(MSG_DEBUG, "adoption complete, signalling parent (%ld)"
223 " to exit.", grandparent);
224 (void) kill(grandparent, SIGALRM);
229 * get_dhcp_kcache(): fetches the DHCP ACK and interface name from the kernel
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
236 static int
237 get_dhcp_kcache(dhcp_kcache_t **kernel_cachep, size_t *kcache_size)
239 char dummy;
240 long size;
242 size = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy));
243 if (size == -1)
244 return (0);
246 *kcache_size = size;
247 *kernel_cachep = malloc(*kcache_size);
248 if (*kernel_cachep == NULL)
249 return (0);
251 (void) sysinfo(SI_DHCP_CACHE, (caddr_t)*kernel_cachep, size);
252 return (1);
256 * get_prom_prop(): get the value of the named property on the named node in
257 * devinfo root.
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.
271 static boolean_t
272 get_prom_prop(const char *nodename, const char *propname, uchar_t **propvaluep,
273 uint_t *lenp)
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;
284 * locate root node
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;
295 * locate nodename within '/'
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;
306 if (node == DI_NODE_NIL) {
307 dhcpmsg(MSG_DEBUG, "get_prom_prop: node not found");
308 goto get_prom_prop_cleanup;
312 * scan all properties of /nodename for the 'propname' property
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)) {
319 dhcpmsg(MSG_DEBUG, "get_prom_prop: property = %s",
320 di_prom_prop_name(pp));
322 if (strcmp(propname, di_prom_prop_name(pp)) == 0) {
323 break;
327 if (pp == DI_PROM_PROP_NIL) {
328 dhcpmsg(MSG_DEBUG, "get_prom_prop: property not found");
329 goto get_prom_prop_cleanup;
333 * get the property; allocate some memory copy it out
336 len = di_prom_prop_data(pp, (uchar_t **)&value);
338 if (value == NULL) {
340 * property data read problems
343 success = B_FALSE;
344 dhcpmsg(MSG_ERR, "get_prom_prop: cannot read property data");
345 goto get_prom_prop_cleanup;
348 if (propvaluep != NULL) {
350 * allocate somewhere to copy the property value to
353 *propvaluep = calloc(len, sizeof (uchar_t));
355 if (*propvaluep == NULL) {
357 * allocation problems
360 success = B_FALSE;
361 dhcpmsg(MSG_ERR, "get_prom_prop: cannot allocate "
362 "memory for property value");
363 goto get_prom_prop_cleanup;
367 * copy data out
370 (void) memcpy(*propvaluep, value, len);
373 * copy out the length if a suitable pointer has
374 * been supplied
377 if (lenp != NULL) {
378 *lenp = len;
381 dhcpmsg(MSG_DEBUG, "get_prom_prop: property value "
382 "length = %d", len);
385 get_prom_prop_cleanup:
387 if (phdl != DI_PROM_HANDLE_NIL) {
388 di_prom_fini(phdl);
391 if (root_node != DI_NODE_NIL) {
392 di_fini(root_node);
395 return (success);