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
dhcp_adopt(void)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
dhcp_adopt_complete(dhcp_smach_t * dsmp)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
get_dhcp_kcache(dhcp_kcache_t ** kernel_cachep,size_t * kcache_size)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
get_prom_prop(const char * nodename,const char * propname,uchar_t ** propvaluep,uint_t * lenp)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