1*43e4027dSchristos /* $NetBSD: main.c,v 1.4 2015/06/16 22:54:10 christos Exp $ */
2e8a634acSpooka
3e8a634acSpooka /*-
4e8a634acSpooka * Copyright (c) 2011 Antti Kantee. All Rights Reserved.
5e8a634acSpooka *
6e8a634acSpooka * Redistribution and use in source and binary forms, with or without
7e8a634acSpooka * modification, are permitted provided that the following conditions
8e8a634acSpooka * are met:
9e8a634acSpooka * 1. Redistributions of source code must retain the above copyright
10e8a634acSpooka * notice, this list of conditions and the following disclaimer.
11e8a634acSpooka * 2. Redistributions in binary form must reproduce the above copyright
12e8a634acSpooka * notice, this list of conditions and the following disclaimer in the
13e8a634acSpooka * documentation and/or other materials provided with the distribution.
14e8a634acSpooka *
15e8a634acSpooka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16e8a634acSpooka * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17e8a634acSpooka * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18e8a634acSpooka * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19e8a634acSpooka * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20e8a634acSpooka * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21e8a634acSpooka * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22e8a634acSpooka * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23e8a634acSpooka * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24e8a634acSpooka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25e8a634acSpooka * SUCH DAMAGE.
26e8a634acSpooka */
27e8a634acSpooka
28e8a634acSpooka #include <sys/types.h>
29e8a634acSpooka #include <sys/ioctl.h>
30e8a634acSpooka #include <sys/socket.h>
31e8a634acSpooka #include <sys/sysctl.h>
32e8a634acSpooka
33e8a634acSpooka #include <net/if.h>
34e8a634acSpooka #include <net/if_dl.h>
35e8a634acSpooka
36e8a634acSpooka #include <err.h>
37e8a634acSpooka #include <errno.h>
38e8a634acSpooka #include <poll.h>
39e8a634acSpooka #include <stdlib.h>
40e8a634acSpooka #include <string.h>
41e8a634acSpooka
42e8a634acSpooka #include <rump/rump_syscalls.h>
43e8a634acSpooka #include <rump/rumpclient.h>
44e8a634acSpooka
45e8a634acSpooka #include "configure.h"
46e8a634acSpooka #include "dhcp.h"
47e8a634acSpooka #include "net.h"
48e8a634acSpooka
49e8a634acSpooka struct interface *ifaces;
50e8a634acSpooka
514624cd84Sjoerg __dead static void
usage(void)52e8a634acSpooka usage(void)
53e8a634acSpooka {
54e8a634acSpooka
55*43e4027dSchristos fprintf(stderr, "Usage: %s ifname\n", getprogname());
56e8a634acSpooka exit(1);
57e8a634acSpooka }
58e8a634acSpooka
59e8a634acSpooka int
get_hwaddr(struct interface * ifp)60e8a634acSpooka get_hwaddr(struct interface *ifp)
61e8a634acSpooka {
62e8a634acSpooka struct if_laddrreq iflr;
63e8a634acSpooka struct sockaddr_dl *sdl;
64e8a634acSpooka int s, sverrno;
65e8a634acSpooka
66e8a634acSpooka memset(&iflr, 0, sizeof(iflr));
67e8a634acSpooka strlcpy(iflr.iflr_name, ifp->name, sizeof(iflr.iflr_name));
68e8a634acSpooka iflr.addr.ss_family = AF_LINK;
69e8a634acSpooka
70e8a634acSpooka sdl = satosdl(&iflr.addr);
71e8a634acSpooka sdl->sdl_alen = ETHER_ADDR_LEN;
72e8a634acSpooka
73e8a634acSpooka if ((s = rump_sys_socket(AF_LINK, SOCK_DGRAM, 0)) == -1)
74e8a634acSpooka return -1;
75e8a634acSpooka
76e8a634acSpooka if (rump_sys_ioctl(s, SIOCGLIFADDR, &iflr) == -1) {
77e8a634acSpooka sverrno = errno;
78e8a634acSpooka rump_sys_close(s);
79e8a634acSpooka errno = sverrno;
80e8a634acSpooka return -1;
81e8a634acSpooka }
82e8a634acSpooka
83e8a634acSpooka /* XXX: is that the right way to copy the link address? */
84e8a634acSpooka memcpy(ifp->hwaddr, sdl->sdl_data+strlen(ifp->name), ETHER_ADDR_LEN);
85e8a634acSpooka ifp->hwlen = ETHER_ADDR_LEN;
86e8a634acSpooka ifp->family = ARPHRD_ETHER;
87e8a634acSpooka
88e8a634acSpooka rump_sys_close(s);
89e8a634acSpooka return 0;
90e8a634acSpooka }
91e8a634acSpooka
92e8a634acSpooka static void
send_discover(struct interface * ifp)93e8a634acSpooka send_discover(struct interface *ifp)
94e8a634acSpooka {
95e8a634acSpooka struct dhcp_message *dhcp;
96e8a634acSpooka uint8_t *udp;
97e8a634acSpooka ssize_t mlen, ulen;
98e8a634acSpooka struct in_addr ia;
99e8a634acSpooka
100e8a634acSpooka memset(&ia, 0, sizeof(ia));
101e8a634acSpooka
102e8a634acSpooka mlen = make_message(&dhcp, ifp, DHCP_DISCOVER);
103e8a634acSpooka ulen = make_udp_packet(&udp, (void *)dhcp, mlen, ia, ia);
104e8a634acSpooka if (send_raw_packet(ifp, ETHERTYPE_IP, udp, ulen) == -1)
105*43e4027dSchristos err(EXIT_FAILURE, "sending discover failed");
106e8a634acSpooka }
107e8a634acSpooka
108e8a634acSpooka static void
send_request(struct interface * ifp)109e8a634acSpooka send_request(struct interface *ifp)
110e8a634acSpooka {
111e8a634acSpooka struct dhcp_message *dhcp;
112e8a634acSpooka uint8_t *udp;
113e8a634acSpooka ssize_t mlen, ulen;
114e8a634acSpooka struct in_addr ia;
115e8a634acSpooka
116e8a634acSpooka memset(&ia, 0, sizeof(ia));
117e8a634acSpooka
118e8a634acSpooka mlen = make_message(&dhcp, ifp, DHCP_REQUEST);
119e8a634acSpooka ulen = make_udp_packet(&udp, (void *)dhcp, mlen, ia, ia);
120e8a634acSpooka if (send_raw_packet(ifp, ETHERTYPE_IP, udp, ulen) == -1)
121*43e4027dSchristos err(EXIT_FAILURE, "sending discover failed");
122e8a634acSpooka }
123e8a634acSpooka
124e8a634acSpooka /* wait for 5s by default */
125e8a634acSpooka #define RESPWAIT 5000
126e8a634acSpooka static void
get_network(struct interface * ifp,uint8_t ** rawp,const struct dhcp_message ** dhcpp)127e8a634acSpooka get_network(struct interface *ifp, uint8_t **rawp,
128e8a634acSpooka const struct dhcp_message **dhcpp)
129e8a634acSpooka {
130e8a634acSpooka struct pollfd pfd;
131e8a634acSpooka const struct dhcp_message *dhcp;
132e8a634acSpooka const uint8_t *data;
133e8a634acSpooka uint8_t *raw;
134e8a634acSpooka ssize_t n;
135e8a634acSpooka
136e8a634acSpooka pfd.fd = ifp->raw_fd;
137e8a634acSpooka pfd.events = POLLIN;
138e8a634acSpooka
139e8a634acSpooka raw = xmalloc(udp_dhcp_len);
140e8a634acSpooka for (;;) {
141e8a634acSpooka switch (rump_sys_poll(&pfd, 1, RESPWAIT)) {
142e8a634acSpooka case 0:
143*43e4027dSchristos errx(EXIT_FAILURE, "timed out waiting for response");
144e8a634acSpooka case -1:
145*43e4027dSchristos err(EXIT_FAILURE, "poll failed");
146e8a634acSpooka default:
147e8a634acSpooka break;
148e8a634acSpooka }
149e8a634acSpooka
150e8a634acSpooka if ((n = get_raw_packet(ifp, ETHERTYPE_IP,
151e8a634acSpooka raw, udp_dhcp_len)) < 1)
152e8a634acSpooka continue;
153e8a634acSpooka
154e8a634acSpooka if (valid_udp_packet(raw, n, NULL) == -1) {
155e8a634acSpooka fprintf(stderr, "invalid packet received. retrying\n");
156e8a634acSpooka continue;
157e8a634acSpooka }
158e8a634acSpooka
159e8a634acSpooka n = get_udp_data(&data, raw);
160e8a634acSpooka if ((size_t)n > sizeof(*dhcp)) {
161e8a634acSpooka fprintf(stderr, "invalid packet size. retrying\n");
162e8a634acSpooka continue;
163e8a634acSpooka }
164e8a634acSpooka dhcp = (const void *)data;
165e8a634acSpooka
166e8a634acSpooka /* XXX: what if packet is too small? */
167e8a634acSpooka
168e8a634acSpooka /* some sanity checks */
169e8a634acSpooka if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
170e8a634acSpooka /* ignore */
171e8a634acSpooka continue;
172e8a634acSpooka }
173e8a634acSpooka
174e8a634acSpooka if (ifp->state->xid != dhcp->xid) {
175e8a634acSpooka fprintf(stderr, "invalid transaction. retrying\n");
176e8a634acSpooka continue;
177e8a634acSpooka }
178e8a634acSpooka
179e8a634acSpooka break;
180e8a634acSpooka }
181e8a634acSpooka
182e8a634acSpooka *rawp = raw;
183e8a634acSpooka *dhcpp = dhcp;
184e8a634acSpooka }
185e8a634acSpooka
186e8a634acSpooka static void
get_offer(struct interface * ifp)187e8a634acSpooka get_offer(struct interface *ifp)
188e8a634acSpooka {
189e8a634acSpooka const struct dhcp_message *dhcp;
190e8a634acSpooka uint8_t *raw;
191e8a634acSpooka uint8_t type;
192e8a634acSpooka
193e8a634acSpooka get_network(ifp, &raw, &dhcp);
194e8a634acSpooka
195e8a634acSpooka get_option_uint8(&type, dhcp, DHO_MESSAGETYPE);
196e8a634acSpooka switch (type) {
197e8a634acSpooka case DHCP_OFFER:
198e8a634acSpooka break;
199e8a634acSpooka case DHCP_NAK:
200*43e4027dSchristos errx(EXIT_FAILURE, "got NAK from dhcp server");
201e8a634acSpooka default:
202*43e4027dSchristos errx(EXIT_FAILURE, "didn't receive offer");
203e8a634acSpooka }
204e8a634acSpooka
205e8a634acSpooka ifp->state->offer = xzalloc(sizeof(*ifp->state->offer));
206e8a634acSpooka memcpy(ifp->state->offer, dhcp, sizeof(*ifp->state->offer));
207b407763dSpooka ifp->state->lease.addr.s_addr = dhcp->yiaddr;
208b407763dSpooka ifp->state->lease.cookie = dhcp->cookie;
209e8a634acSpooka free(raw);
210e8a634acSpooka }
211e8a634acSpooka
212e8a634acSpooka static void
get_ack(struct interface * ifp)213e8a634acSpooka get_ack(struct interface *ifp)
214e8a634acSpooka {
215e8a634acSpooka const struct dhcp_message *dhcp;
216e8a634acSpooka uint8_t *raw;
217e8a634acSpooka uint8_t type;
218e8a634acSpooka
219e8a634acSpooka get_network(ifp, &raw, &dhcp);
220e8a634acSpooka get_option_uint8(&type, dhcp, DHO_MESSAGETYPE);
221e8a634acSpooka if (type != DHCP_ACK)
222*43e4027dSchristos errx(EXIT_FAILURE, "didn't receive ack");
223e8a634acSpooka
224e8a634acSpooka ifp->state->new = ifp->state->offer;
225e8a634acSpooka get_lease(&ifp->state->lease, ifp->state->new);
226e8a634acSpooka }
227e8a634acSpooka
228e8a634acSpooka int
main(int argc,char * argv[])229e8a634acSpooka main(int argc, char *argv[])
230e8a634acSpooka {
231e8a634acSpooka struct interface *ifp;
232e8a634acSpooka struct if_options *ifo;
233e8a634acSpooka const int mib[] = { CTL_KERN, KERN_HOSTNAME };
234e8a634acSpooka size_t hlen;
235e8a634acSpooka
236e8a634acSpooka setprogname(argv[0]);
237e8a634acSpooka if (argc != 2)
238e8a634acSpooka usage();
239e8a634acSpooka
240e8a634acSpooka if (rumpclient_init() == -1)
241*43e4027dSchristos err(EXIT_FAILURE, "init failed");
242e8a634acSpooka
243e8a634acSpooka if (init_sockets() == -1)
244*43e4027dSchristos err(EXIT_FAILURE, "failed to init sockets");
245e8a634acSpooka if ((ifp = init_interface(argv[1])) == NULL)
246*43e4027dSchristos err(EXIT_FAILURE, "cannot init %s", argv[1]);
247e8a634acSpooka ifaces = ifp;
248e8a634acSpooka if (open_socket(ifp, ETHERTYPE_IP) == -1)
249*43e4027dSchristos err(EXIT_FAILURE, "bpf");
250e8a634acSpooka up_interface(ifp);
251e8a634acSpooka
252e8a634acSpooka ifp->state = xzalloc(sizeof(*ifp->state));
253e8a634acSpooka ifp->state->options = ifo = xzalloc(sizeof(*ifp->state->options));
254e8a634acSpooka ifp->state->xid = arc4random();
255e8a634acSpooka
256e8a634acSpooka hlen = sizeof(ifo->hostname);
257e8a634acSpooka if (rump_sys___sysctl(mib, __arraycount(mib), ifo->hostname, &hlen,
258e8a634acSpooka NULL, 0) == -1)
259e8a634acSpooka snprintf(ifo->hostname, sizeof(ifo->hostname),
260e8a634acSpooka "unknown.hostname");
261e8a634acSpooka ifo->options = DHCPCD_GATEWAY | DHCPCD_HOSTNAME;
262e8a634acSpooka
263e8a634acSpooka if (get_hwaddr(ifp) == -1)
264*43e4027dSchristos err(EXIT_FAILURE, "failed to get hwaddr for %s", ifp->name);
265e8a634acSpooka
266e8a634acSpooka send_discover(ifp);
267e8a634acSpooka get_offer(ifp);
268e8a634acSpooka send_request(ifp);
269e8a634acSpooka get_ack(ifp);
270e8a634acSpooka
271e8a634acSpooka configure(ifp);
272e8a634acSpooka
273e8a634acSpooka return 0;
274e8a634acSpooka }
275