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.
268 */
269
270 static boolean_t
get_prom_prop(const char * nodename,const char * propname,uchar_t ** propvaluep,uint_t * lenp)271 get_prom_prop(const char *nodename, const char *propname, uchar_t **propvaluep,
272 uint_t *lenp)
273 {
274 di_node_t root_node;
275 di_node_t node;
276 di_prom_handle_t phdl = DI_PROM_HANDLE_NIL;
277 di_prom_prop_t pp;
278 uchar_t *value = NULL;
279 unsigned int len = 0;
280 boolean_t success = B_TRUE;
281
282 /*
283 * locate root node
284 */
285
286 if ((root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL ||
287 (phdl = di_prom_init()) == DI_PROM_HANDLE_NIL) {
288 dhcpmsg(MSG_DEBUG, "get_prom_prop: property root node "
289 "not found");
290 goto get_prom_prop_cleanup;
291 }
292
293 /*
294 * locate nodename within '/'
295 */
296
297 for (node = di_child_node(root_node);
298 node != DI_NODE_NIL;
299 node = di_sibling_node(node)) {
300 if (strcmp(di_node_name(node), nodename) == 0) {
301 break;
302 }
303 }
304
305 if (node == DI_NODE_NIL) {
306 dhcpmsg(MSG_DEBUG, "get_prom_prop: node not found");
307 goto get_prom_prop_cleanup;
308 }
309
310 /*
311 * scan all properties of /nodename for the 'propname' property
312 */
313
314 for (pp = di_prom_prop_next(phdl, node, DI_PROM_PROP_NIL);
315 pp != DI_PROM_PROP_NIL;
316 pp = di_prom_prop_next(phdl, node, pp)) {
317
318 dhcpmsg(MSG_DEBUG, "get_prom_prop: property = %s",
319 di_prom_prop_name(pp));
320
321 if (strcmp(propname, di_prom_prop_name(pp)) == 0) {
322 break;
323 }
324 }
325
326 if (pp == DI_PROM_PROP_NIL) {
327 dhcpmsg(MSG_DEBUG, "get_prom_prop: property not found");
328 goto get_prom_prop_cleanup;
329 }
330
331 /*
332 * get the property; allocate some memory copy it out
333 */
334
335 len = di_prom_prop_data(pp, (uchar_t **)&value);
336
337 if (value == NULL) {
338 /*
339 * property data read problems
340 */
341
342 success = B_FALSE;
343 dhcpmsg(MSG_ERR, "get_prom_prop: cannot read property data");
344 goto get_prom_prop_cleanup;
345 }
346
347 if (propvaluep != NULL) {
348 /*
349 * allocate somewhere to copy the property value to
350 */
351
352 *propvaluep = calloc(len, sizeof (uchar_t));
353
354 if (*propvaluep == NULL) {
355 /*
356 * allocation problems
357 */
358
359 success = B_FALSE;
360 dhcpmsg(MSG_ERR, "get_prom_prop: cannot allocate "
361 "memory for property value");
362 goto get_prom_prop_cleanup;
363 }
364
365 /*
366 * copy data out
367 */
368
369 (void) memcpy(*propvaluep, value, len);
370
371 /*
372 * copy out the length if a suitable pointer has
373 * been supplied
374 */
375
376 if (lenp != NULL) {
377 *lenp = len;
378 }
379
380 dhcpmsg(MSG_DEBUG, "get_prom_prop: property value "
381 "length = %d", len);
382 }
383
384 get_prom_prop_cleanup:
385
386 if (phdl != DI_PROM_HANDLE_NIL) {
387 di_prom_fini(phdl);
388 }
389
390 if (root_node != DI_NODE_NIL) {
391 di_fini(root_node);
392 }
393
394 return (success);
395 }
396