xref: /netbsd/usr.bin/rump_dhcpclient/main.c (revision 4624cd84)
1*4624cd84Sjoerg /*	$NetBSD: main.c,v 1.3 2011/09/16 15:39:28 joerg 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 
51*4624cd84Sjoerg __dead static void
52e8a634acSpooka usage(void)
53e8a634acSpooka {
54e8a634acSpooka 
55e8a634acSpooka 	fprintf(stderr, "usage: %s ifname\n", getprogname());
56e8a634acSpooka 	exit(1);
57e8a634acSpooka }
58e8a634acSpooka 
59e8a634acSpooka int
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
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)
105e8a634acSpooka 		err(1, "sending discover failed");
106e8a634acSpooka }
107e8a634acSpooka 
108e8a634acSpooka static void
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)
121e8a634acSpooka 		err(1, "sending discover failed");
122e8a634acSpooka }
123e8a634acSpooka 
124e8a634acSpooka /* wait for 5s by default */
125e8a634acSpooka #define RESPWAIT 5000
126e8a634acSpooka static void
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:
143e8a634acSpooka 			errx(1, "timed out waiting for response");
144e8a634acSpooka 		case -1:
145e8a634acSpooka 			err(1, "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
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:
200e8a634acSpooka 		errx(1, "got NAK from dhcp server");
201e8a634acSpooka 	default:
202e8a634acSpooka 		errx(1, "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
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)
222e8a634acSpooka 		errx(1, "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
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)
241e8a634acSpooka 		err(1, "init failed");
242e8a634acSpooka 
243e8a634acSpooka 	if (init_sockets() == -1)
244e8a634acSpooka 		err(1, "failed to init sockets");
245e8a634acSpooka 	if ((ifp = init_interface(argv[1])) == NULL)
246e8a634acSpooka 		err(1, "cannot init %s\n", argv[1]);
247e8a634acSpooka 	ifaces = ifp;
248e8a634acSpooka 	if (open_socket(ifp, ETHERTYPE_IP) == -1)
249e8a634acSpooka 		err(1, "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)
264e8a634acSpooka 		err(1, "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