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