1 /*	$NetBSD: main.c,v 1.3 2011/09/16 15:39:28 joerg Exp $	*/
2 
3 /*-
4  * Copyright (c) 2011, 2013 Antti Kantee.  All Rights Reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/cprng.h>
30 #include <sys/ioctl.h>
31 #include <sys/kernel.h>
32 #include <sys/kmem.h>
33 #include <sys/poll.h>
34 #include <sys/socket.h>
35 #include <sys/socketvar.h>
36 #include <sys/sysctl.h>
37 
38 #include <net/if.h>
39 #include <net/if_dl.h>
40 
41 #include <rump/rump.h>
42 
43 #include "dhcp_configure.h"
44 #include "dhcp_dhcp.h"
45 #include "dhcp_net.h"
46 #include "rump_private.h"
47 #include "rumpkern_if_priv.h"
48 #include "netconfig_if_priv.h"
49 
50 struct interface *ifaces;
51 
52 int
get_hwaddr(struct interface * iface)53 get_hwaddr(struct interface *iface)
54 {
55 	struct if_laddrreq iflr;
56 	struct sockaddr_dl *sdl;
57 	struct socket *slink;
58 	int error;
59 
60 	memset(&iflr, 0, sizeof(iflr));
61 	strlcpy(iflr.iflr_name, iface->name, sizeof(iflr.iflr_name));
62 	iflr.addr.ss_family = AF_LINK;
63 
64 	sdl = satosdl(&iflr.addr);
65 	sdl->sdl_alen = ETHER_ADDR_LEN;
66 
67 	if ((error = socreate(AF_LINK, &slink, SOCK_DGRAM, 0,
68 	    curlwp, NULL)) != 0)
69 		return error;
70 
71 	if ((error = ifioctl(slink, SIOCGLIFADDR, &iflr, curlwp)) != 0) {
72 		soclose(slink);
73 		return error;
74 	}
75 
76 	/* XXX: is that the right way to copy the link address? */
77 	memcpy(iface->hwaddr, sdl->sdl_data+strlen(iface->name), ETHER_ADDR_LEN);
78 	iface->hwlen = ETHER_ADDR_LEN;
79 	iface->family = ARPHRD_ETHER;
80 
81 	soclose(slink);
82 	return 0;
83 }
84 
85 static int
send_discover(struct interface * iface)86 send_discover(struct interface *iface)
87 {
88 	struct dhcp_message *dhcp;
89 	uint8_t *udp;
90 	ssize_t mlen, ulen;
91 	struct in_addr ia;
92 	int error;
93 
94 	memset(&ia, 0, sizeof(ia));
95 
96 	mlen = make_message(&dhcp, iface, DHCP_DISCOVER);
97 	ulen = make_udp_packet(&udp, (void *)dhcp, mlen, ia, ia);
98 	if ((error = dhcp_send_raw_packet(iface, ETHERTYPE_IP, udp, ulen)) != 0)
99 		printf("dhcp: sending discover failed\n");
100 	return error;
101 }
102 
103 static int
send_request(struct interface * iface)104 send_request(struct interface *iface)
105 {
106 	struct dhcp_message *dhcp;
107 	uint8_t *udp;
108 	ssize_t mlen, ulen;
109 	struct in_addr ia;
110 	int error;
111 
112 	memset(&ia, 0, sizeof(ia));
113 
114 	mlen = make_message(&dhcp, iface, DHCP_REQUEST);
115 	ulen = make_udp_packet(&udp, (void *)dhcp, mlen, ia, ia);
116 	if ((error = dhcp_send_raw_packet(iface, ETHERTYPE_IP, udp, ulen)) != 0)
117 		printf("dhcp: sending discover failed\n");
118 	return error;
119 }
120 
121 /* wait for 5s by default */
122 #define RESPWAIT 5
123 static bool
get_network(struct interface * iface,uint8_t * raw,const struct dhcp_message ** dhcpp)124 get_network(struct interface *iface, uint8_t *raw,
125 	const struct dhcp_message **dhcpp)
126 {
127 	struct pollfd pfd;
128 	const struct dhcp_message *dhcp;
129 	const uint8_t *data;
130 	ssize_t n;
131 
132 	pfd.fd = iface->raw_fd;
133 	pfd.events = POLLIN;
134 
135 	for (;;) {
136 		register_t rv[2];
137 		struct timespec ts;
138 
139 		ts.tv_sec = RESPWAIT;
140 		ts.tv_nsec = 0;
141 
142 		if (pollcommon(rv, &pfd, 1, &ts, NULL) != 0 || rv[0] != 1) {
143 			printf("dhcp get: timed out waiting for response.\n");
144 			return false;
145 		}
146 
147 		if ((n = dhcp_get_raw_packet(iface, ETHERTYPE_IP,
148 		    raw, udp_dhcp_len)) < 1)
149 			continue;
150 
151 		if (valid_udp_packet(raw, n, NULL) != 0) {
152 			printf("dhcp get: invalid packet received. retrying\n");
153 			continue;
154 		}
155 
156 		n = get_udp_data(&data, raw);
157 		if ((size_t)n > sizeof(*dhcp)) {
158 			printf("dhcp get: invalid packet size. retrying\n");
159 			continue;
160 		}
161 		dhcp = (const void *)data;
162 
163 		/* XXX: what if packet is too small? */
164 
165 		/* some sanity checks */
166 		if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
167 			/* ignore */
168 			continue;
169 		}
170 
171 		if (iface->state->xid != dhcp->xid) {
172 			printf("dhcp get: invalid transaction. retrying\n");
173 			continue;
174 		}
175 
176 		break;
177 	}
178 
179 	*dhcpp = dhcp;
180 	return true;
181 }
182 
183 static bool
get_offer(struct interface * iface)184 get_offer(struct interface *iface)
185 {
186 	const struct dhcp_message *dhcp;
187 	uint8_t *raw;
188 	uint8_t type;
189 
190 	raw = kmem_alloc(udp_dhcp_len, KM_SLEEP);
191 	if (!get_network(iface, raw, &dhcp))
192 		return false;
193 
194 	get_option_uint8(&type, dhcp, DHO_MESSAGETYPE);
195 	switch (type) {
196 	case DHCP_OFFER:
197 		if (get_option_addr(&iface->state->lease.server,
198 		    dhcp, DHO_SERVERID) != 0)
199 			iface->state->lease.server.s_addr = INADDR_ANY;
200 		break;
201 	case DHCP_NAK:
202 		printf("dhcp: got NAK from dhcp server\n");
203 		return false;
204 	default:
205 		printf("dhcp: didn't receive offer\n");
206 		return false;
207 	}
208 
209 	iface->state->offer = kmem_alloc(sizeof(*iface->state->offer),
210 	    KM_SLEEP);
211 	memcpy(iface->state->offer, dhcp, sizeof(*iface->state->offer));
212 	iface->state->lease.addr.s_addr = dhcp->yiaddr;
213 	iface->state->lease.cookie = dhcp->cookie;
214 	kmem_free(raw, udp_dhcp_len);
215 
216 	return true;
217 }
218 
219 static bool
get_ack(struct interface * iface)220 get_ack(struct interface *iface)
221 {
222 	const struct dhcp_message *dhcp;
223 	uint8_t *raw;
224 	uint8_t type;
225 
226 	raw = kmem_alloc(udp_dhcp_len, KM_SLEEP);
227 	get_network(iface, raw, &dhcp);
228 	get_option_uint8(&type, dhcp, DHO_MESSAGETYPE);
229 	if (type != DHCP_ACK) {
230 		printf("dhcp: didn't receive ack\n");
231 		return false;
232 	}
233 
234 	iface->state->new = iface->state->offer;
235 	get_lease(&iface->state->lease, iface->state->new);
236 	kmem_free(raw, udp_dhcp_len);
237 
238 	return true;
239 }
240 
241 /* since we do essentially mi_switch() here, cannot use curlwp directly */
242 #define mycurlwp curcpu()->ci_curlwp
243 
244 /*
245  * release our called.  called at reboot-time.  we use the original
246  * proc/lwp context here to avoid having to open new file descriptors.
247  */
248 static void
send_release(void * arg)249 send_release(void *arg)
250 {
251 	struct interface *iface = ifaces;
252 	struct dhcp_message *dhcp;
253 	uint8_t *udp;
254 	ssize_t mlen, ulen;
255 	struct in_addr ia;
256 	struct lwp *origlwp = mycurlwp;
257 
258 	rump__lwproc_lwphold();
259 	rump_lwproc_switch(arg);
260 
261 	memset(&ia, 0, sizeof(ia));
262 
263 	mlen = make_message(&dhcp, iface, DHCP_RELEASE);
264 	ulen = make_udp_packet(&udp, (void *)dhcp, mlen, ia, ia);
265 	dhcp_send_raw_packet(iface, ETHERTYPE_IP, udp, ulen);
266 
267 	/* give it a chance to fly (rump kernel will exit) */
268 	kpause("dhcprel", false, 1, NULL);
269 
270 	/* release the DHCP process */
271 	rump_lwproc_releaselwp();
272 
273 	rump_lwproc_switch(origlwp);
274 	rump__lwproc_lwprele();
275 }
276 
277 /*
278  * Configure an address for one interface.  Not very robust and
279  * does not clean up after itself.
280  */
281 #define MAXTRIES 10
282 int
rump_netconfig_dhcp_ipv4_oneshot(const char * ifname)283 rump_netconfig_dhcp_ipv4_oneshot(const char *ifname)
284 {
285 	struct interface *iface;
286 	struct if_options *ifo;
287 	struct lwp *origlwp = mycurlwp;
288 	int error, tries = 0;
289 	bool rv;
290 
291 	/*
292 	 * first, create ourselves a new process context, since we're
293 	 * going to be opening file descriptors
294 	 */
295 	rump__lwproc_lwphold();
296 	rump_lwproc_rfork(RUMP_RFCFDG);
297 
298 	if ((error = init_sockets()) != 0) {
299 		printf("failed to init sockets\n");
300 		goto out;
301 	}
302 
303 	if ((error = init_interface(ifname, &iface)) != 0) {
304 		printf("cannot init %s (%d)\n", ifname, error);
305 		goto out;
306 	}
307 	rump_netconfig_ifup(ifname);
308 	ifaces = iface;
309 	if ((error = dhcp_open_socket(iface, ETHERTYPE_IP)) != 0)
310 		panic("failed to open socket: %d", error);
311 
312 	up_interface(iface);
313 
314 	iface->state = kmem_zalloc(sizeof(*iface->state), KM_SLEEP);
315 	iface->state->options = ifo
316 	    = kmem_zalloc(sizeof(*iface->state->options), KM_SLEEP);
317 	iface->state->xid = cprng_fast32();
318 
319 	strlcpy(ifo->hostname, hostname, sizeof(ifo->hostname));
320 	ifo->options = DHCPCD_GATEWAY | DHCPCD_HOSTNAME;
321 
322 	if ((error = get_hwaddr(iface)) != 0) {
323 		printf("failed to get hwaddr for %s\n", iface->name);
324 		goto out;
325 	}
326 
327 	for (rv = false; !rv && tries < MAXTRIES; tries++) {
328 		if (send_discover(iface) != 0) {
329 			kpause("dhcpdis", false, hz, NULL);
330 			continue;
331 		}
332 		rv = get_offer(iface);
333 	}
334 	if (!rv) {
335 		error = EADDRNOTAVAIL; /* heh heh heh */
336 		goto out;
337 	}
338 
339 	for (rv = false, tries = 0; !rv && tries < MAXTRIES; tries++) {
340 		if (send_request(iface) != 0) {
341 			kpause("dhcpreq", false, hz, NULL);
342 			continue;
343 		}
344 
345 		rv = get_ack(iface);
346 	}
347 	if (!rv) {
348 		error = EADDRNOTAVAIL; /* hoh hoh hoh */
349 		goto out;
350 	}
351 
352 	error = configure(iface);
353 	if (!error)
354 		shutdownhook_establish(send_release, mycurlwp);
355  out:
356 	rump_lwproc_switch(origlwp);
357 	rump__lwproc_lwprele();
358 	return error;
359 }
360