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]
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>
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>
41 #include <libdevinfo.h>
47 #include "interface.h"
52 char dk_if_name
[IFNAMSIZ
];
56 static int get_dhcp_kcache(dhcp_kcache_t
**, size_t *);
58 static boolean_t
get_prom_prop(const char *, const char *, uchar_t
**,
62 * dhcp_adopt(): adopts the interface managed by the kernel for diskless boot
65 * output: boolean_t: B_TRUE success, B_FALSE on failure
72 dhcp_kcache_t
*kcache
= NULL
;
76 dhcp_smach_t
*dsmp
= NULL
;
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");
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
);
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
,
99 dhcpmsg(MSG_CRIT
, "dhcp_adopt: cannot convert kernel ACK");
103 if (dhcp_options_scan(plp
, B_TRUE
) != 0) {
104 dhcpmsg(MSG_CRIT
, "dhcp_adopt: cannot parse kernel ACK");
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
);
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
);
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");
144 if (!get_prom_prop("chosen", "client-id", &dsmp
->dsm_cid
,
147 * a failure occurred trying to acquire the client-id
151 "dhcp_adopt: cannot allocate client id for %s",
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
161 dhcpmsg(MSG_DEBUG
, "dhcp_adopt: no /chosen:clientid id for %s",
166 dsmp
->dsm_cidlen
= client_id_len
;
168 if (set_lif_dhcp(lif
) != DHCP_IPC_SUCCESS
)
171 if (!set_smach_state(dsmp
, ADOPTING
))
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");
189 /* Note: no need to free lif; dsmp holds reference */
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
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");
215 if (dhcp_extending(dsmp
) == 0) {
217 "dhcp_adopt_complete: cannot send renew request");
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
237 get_dhcp_kcache(dhcp_kcache_t
**kernel_cachep
, size_t *kcache_size
)
242 size
= sysinfo(SI_DHCP_CACHE
, &dummy
, sizeof (dummy
));
247 *kernel_cachep
= malloc(*kcache_size
);
248 if (*kernel_cachep
== NULL
)
251 (void) sysinfo(SI_DHCP_CACHE
, (caddr_t
)*kernel_cachep
, size
);
256 * get_prom_prop(): get the value of the named property on the named node in
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),
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.
272 get_prom_prop(const char *nodename
, const char *propname
, uchar_t
**propvaluep
,
277 di_prom_handle_t phdl
= DI_PROM_HANDLE_NIL
;
279 uchar_t
*value
= NULL
;
280 unsigned int len
= 0;
281 boolean_t success
= B_TRUE
;
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 "
291 goto get_prom_prop_cleanup
;
295 * locate nodename within '/'
298 for (node
= di_child_node(root_node
);
300 node
= di_sibling_node(node
)) {
301 if (strcmp(di_node_name(node
), nodename
) == 0) {
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) {
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
);
340 * property data read problems
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
361 dhcpmsg(MSG_ERR
, "get_prom_prop: cannot allocate "
362 "memory for property value");
363 goto get_prom_prop_cleanup
;
370 (void) memcpy(*propvaluep
, value
, len
);
373 * copy out the length if a suitable pointer has
381 dhcpmsg(MSG_DEBUG
, "get_prom_prop: property value "
385 get_prom_prop_cleanup
:
387 if (phdl
!= DI_PROM_HANDLE_NIL
) {
391 if (root_node
!= DI_NODE_NIL
) {