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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * ADOPTING state of the client state machine.
27  */
28 
29 #pragma ident	"%Z%%M%	%I%	%E% SMI"
30 
31 #include <sys/types.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <sys/sockio.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <sys/systeminfo.h>
39 #include <netinet/inetutil.h>
40 #include <netinet/dhcp.h>
41 #include <dhcpmsg.h>
42 
43 #include "async.h"
44 #include "util.h"
45 #include "packet.h"
46 #include "interface.h"
47 #include "states.h"
48 
49 
50 typedef struct {
51 	char		dk_if_name[IFNAMSIZ];
52 	char		dk_ack[1];
53 } dhcp_kcache_t;
54 
55 static int	get_dhcp_kcache(dhcp_kcache_t **, size_t *);
56 
57 /*
58  * dhcp_adopt(): adopts the interface managed by the kernel for diskless boot
59  *
60  *   input: void
61  *  output: int: nonzero on success, zero on failure
62  */
63 
64 int
65 dhcp_adopt(void)
66 {
67 	int		retval;
68 	dhcp_kcache_t	*kcache = NULL;
69 	size_t		kcache_size;
70 	PKT_LIST	*plp = NULL;
71 	struct ifslist	*ifsp;
72 
73 	retval = get_dhcp_kcache(&kcache, &kcache_size);
74 	if (retval == 0 || kcache_size < sizeof (dhcp_kcache_t)) {
75 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot fetch kernel cache");
76 		goto failure;
77 	}
78 
79 	dhcpmsg(MSG_DEBUG, "dhcp_adopt: fetched %s kcache", kcache->dk_if_name);
80 
81 	/*
82 	 * convert the kernel's ACK into binary
83 	 */
84 
85 	plp = calloc(1, sizeof (PKT_LIST));
86 	if (plp == NULL)
87 		goto failure;
88 
89 	plp->len = strlen(kcache->dk_ack) / 2;
90 	plp->pkt = malloc(plp->len);
91 	if (plp->pkt == NULL)
92 		goto failure;
93 
94 	dhcpmsg(MSG_DEBUG, "dhcp_adopt: allocated ACK of %d bytes", plp->len);
95 
96 	if (hexascii_to_octet(kcache->dk_ack, plp->len * 2, plp->pkt, &plp->len)
97 	    != 0) {
98 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot convert kernel ACK");
99 		goto failure;
100 	}
101 
102 	if (dhcp_options_scan(plp, B_TRUE) != 0) {
103 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot parse kernel ACK");
104 		goto failure;
105 	}
106 
107 	/*
108 	 * make an interface to represent the "cached interface" in
109 	 * the kernel, hook up the ACK packet we made, and send out
110 	 * the extend request (to attempt to renew the lease).
111 	 *
112 	 * we do a send_extend() instead of doing a dhcp_init_reboot()
113 	 * because although dhcp_init_reboot() is more correct from a
114 	 * protocol perspective, it introduces a window where a
115 	 * diskless client has no IP address but may need to page in
116 	 * more of this program.  we could mlockall(), but that's
117 	 * going to be a mess, especially with handling malloc() and
118 	 * stack growth, so it's easier to just renew().  the only
119 	 * catch here is that if we are not granted a renewal, we're
120 	 * totally hosed and can only bail out.
121 	 */
122 
123 	ifsp = insert_ifs(kcache->dk_if_name, B_TRUE, &retval);
124 	if (ifsp == NULL)
125 		goto failure;
126 
127 	ifsp->if_state   = ADOPTING;
128 	ifsp->if_dflags |= DHCP_IF_PRIMARY;
129 
130 	/*
131 	 * move to BOUND and use the information in our ACK packet
132 	 */
133 
134 	if (dhcp_bound(ifsp, plp) == 0) {
135 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot use cached packet");
136 		goto failure;
137 	}
138 
139 	if (async_start(ifsp, DHCP_EXTEND, B_FALSE) == 0) {
140 		dhcpmsg(MSG_CRIT, "dhcp_adopt: async_start failed");
141 		goto failure;
142 	}
143 
144 	if (dhcp_extending(ifsp) == 0) {
145 		dhcpmsg(MSG_CRIT, "dhcp_adopt: cannot send renew request");
146 		goto failure;
147 	}
148 
149 	free(kcache);
150 	return (1);
151 
152 failure:
153 	free(kcache);
154 	if (plp != NULL)
155 		free(plp->pkt);
156 	free(plp);
157 	return (0);
158 }
159 
160 /*
161  * get_dhcp_kcache(): fetches the DHCP ACK and interface name from the kernel
162  *
163  *   input: dhcp_kcache_t **: a dynamically-allocated cache packet
164  *	    size_t *: the length of that packet (on return)
165  *  output: int: nonzero on success, zero on failure
166  */
167 
168 static int
169 get_dhcp_kcache(dhcp_kcache_t **kernel_cachep, size_t *kcache_size)
170 {
171 	char	dummy;
172 	long	size;
173 
174 	size = sysinfo(SI_DHCP_CACHE, &dummy, sizeof (dummy));
175 	if (size == -1)
176 		return (0);
177 
178 	*kcache_size   = size;
179 	*kernel_cachep = malloc(*kcache_size);
180 	if (*kernel_cachep == NULL)
181 		return (0);
182 
183 	(void) sysinfo(SI_DHCP_CACHE, (caddr_t)*kernel_cachep, size);
184 	return (1);
185 }
186