1 /* $NetBSD: main.c,v 1.4 2015/06/16 22:54:10 christos Exp $ */
2
3 /*-
4 * Copyright (c) 2011 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/types.h>
29 #include <sys/ioctl.h>
30 #include <sys/socket.h>
31 #include <sys/sysctl.h>
32
33 #include <net/if.h>
34 #include <net/if_dl.h>
35
36 #include <err.h>
37 #include <errno.h>
38 #include <poll.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include <rump/rump_syscalls.h>
43 #include <rump/rumpclient.h>
44
45 #include "configure.h"
46 #include "dhcp.h"
47 #include "net.h"
48
49 struct interface *ifaces;
50
51 __dead static void
usage(void)52 usage(void)
53 {
54
55 fprintf(stderr, "Usage: %s ifname\n", getprogname());
56 exit(1);
57 }
58
59 int
get_hwaddr(struct interface * ifp)60 get_hwaddr(struct interface *ifp)
61 {
62 struct if_laddrreq iflr;
63 struct sockaddr_dl *sdl;
64 int s, sverrno;
65
66 memset(&iflr, 0, sizeof(iflr));
67 strlcpy(iflr.iflr_name, ifp->name, sizeof(iflr.iflr_name));
68 iflr.addr.ss_family = AF_LINK;
69
70 sdl = satosdl(&iflr.addr);
71 sdl->sdl_alen = ETHER_ADDR_LEN;
72
73 if ((s = rump_sys_socket(AF_LINK, SOCK_DGRAM, 0)) == -1)
74 return -1;
75
76 if (rump_sys_ioctl(s, SIOCGLIFADDR, &iflr) == -1) {
77 sverrno = errno;
78 rump_sys_close(s);
79 errno = sverrno;
80 return -1;
81 }
82
83 /* XXX: is that the right way to copy the link address? */
84 memcpy(ifp->hwaddr, sdl->sdl_data+strlen(ifp->name), ETHER_ADDR_LEN);
85 ifp->hwlen = ETHER_ADDR_LEN;
86 ifp->family = ARPHRD_ETHER;
87
88 rump_sys_close(s);
89 return 0;
90 }
91
92 static void
send_discover(struct interface * ifp)93 send_discover(struct interface *ifp)
94 {
95 struct dhcp_message *dhcp;
96 uint8_t *udp;
97 ssize_t mlen, ulen;
98 struct in_addr ia;
99
100 memset(&ia, 0, sizeof(ia));
101
102 mlen = make_message(&dhcp, ifp, DHCP_DISCOVER);
103 ulen = make_udp_packet(&udp, (void *)dhcp, mlen, ia, ia);
104 if (send_raw_packet(ifp, ETHERTYPE_IP, udp, ulen) == -1)
105 err(EXIT_FAILURE, "sending discover failed");
106 }
107
108 static void
send_request(struct interface * ifp)109 send_request(struct interface *ifp)
110 {
111 struct dhcp_message *dhcp;
112 uint8_t *udp;
113 ssize_t mlen, ulen;
114 struct in_addr ia;
115
116 memset(&ia, 0, sizeof(ia));
117
118 mlen = make_message(&dhcp, ifp, DHCP_REQUEST);
119 ulen = make_udp_packet(&udp, (void *)dhcp, mlen, ia, ia);
120 if (send_raw_packet(ifp, ETHERTYPE_IP, udp, ulen) == -1)
121 err(EXIT_FAILURE, "sending discover failed");
122 }
123
124 /* wait for 5s by default */
125 #define RESPWAIT 5000
126 static void
get_network(struct interface * ifp,uint8_t ** rawp,const struct dhcp_message ** dhcpp)127 get_network(struct interface *ifp, uint8_t **rawp,
128 const struct dhcp_message **dhcpp)
129 {
130 struct pollfd pfd;
131 const struct dhcp_message *dhcp;
132 const uint8_t *data;
133 uint8_t *raw;
134 ssize_t n;
135
136 pfd.fd = ifp->raw_fd;
137 pfd.events = POLLIN;
138
139 raw = xmalloc(udp_dhcp_len);
140 for (;;) {
141 switch (rump_sys_poll(&pfd, 1, RESPWAIT)) {
142 case 0:
143 errx(EXIT_FAILURE, "timed out waiting for response");
144 case -1:
145 err(EXIT_FAILURE, "poll failed");
146 default:
147 break;
148 }
149
150 if ((n = get_raw_packet(ifp, ETHERTYPE_IP,
151 raw, udp_dhcp_len)) < 1)
152 continue;
153
154 if (valid_udp_packet(raw, n, NULL) == -1) {
155 fprintf(stderr, "invalid packet received. retrying\n");
156 continue;
157 }
158
159 n = get_udp_data(&data, raw);
160 if ((size_t)n > sizeof(*dhcp)) {
161 fprintf(stderr, "invalid packet size. retrying\n");
162 continue;
163 }
164 dhcp = (const void *)data;
165
166 /* XXX: what if packet is too small? */
167
168 /* some sanity checks */
169 if (dhcp->cookie != htonl(MAGIC_COOKIE)) {
170 /* ignore */
171 continue;
172 }
173
174 if (ifp->state->xid != dhcp->xid) {
175 fprintf(stderr, "invalid transaction. retrying\n");
176 continue;
177 }
178
179 break;
180 }
181
182 *rawp = raw;
183 *dhcpp = dhcp;
184 }
185
186 static void
get_offer(struct interface * ifp)187 get_offer(struct interface *ifp)
188 {
189 const struct dhcp_message *dhcp;
190 uint8_t *raw;
191 uint8_t type;
192
193 get_network(ifp, &raw, &dhcp);
194
195 get_option_uint8(&type, dhcp, DHO_MESSAGETYPE);
196 switch (type) {
197 case DHCP_OFFER:
198 break;
199 case DHCP_NAK:
200 errx(EXIT_FAILURE, "got NAK from dhcp server");
201 default:
202 errx(EXIT_FAILURE, "didn't receive offer");
203 }
204
205 ifp->state->offer = xzalloc(sizeof(*ifp->state->offer));
206 memcpy(ifp->state->offer, dhcp, sizeof(*ifp->state->offer));
207 ifp->state->lease.addr.s_addr = dhcp->yiaddr;
208 ifp->state->lease.cookie = dhcp->cookie;
209 free(raw);
210 }
211
212 static void
get_ack(struct interface * ifp)213 get_ack(struct interface *ifp)
214 {
215 const struct dhcp_message *dhcp;
216 uint8_t *raw;
217 uint8_t type;
218
219 get_network(ifp, &raw, &dhcp);
220 get_option_uint8(&type, dhcp, DHO_MESSAGETYPE);
221 if (type != DHCP_ACK)
222 errx(EXIT_FAILURE, "didn't receive ack");
223
224 ifp->state->new = ifp->state->offer;
225 get_lease(&ifp->state->lease, ifp->state->new);
226 }
227
228 int
main(int argc,char * argv[])229 main(int argc, char *argv[])
230 {
231 struct interface *ifp;
232 struct if_options *ifo;
233 const int mib[] = { CTL_KERN, KERN_HOSTNAME };
234 size_t hlen;
235
236 setprogname(argv[0]);
237 if (argc != 2)
238 usage();
239
240 if (rumpclient_init() == -1)
241 err(EXIT_FAILURE, "init failed");
242
243 if (init_sockets() == -1)
244 err(EXIT_FAILURE, "failed to init sockets");
245 if ((ifp = init_interface(argv[1])) == NULL)
246 err(EXIT_FAILURE, "cannot init %s", argv[1]);
247 ifaces = ifp;
248 if (open_socket(ifp, ETHERTYPE_IP) == -1)
249 err(EXIT_FAILURE, "bpf");
250 up_interface(ifp);
251
252 ifp->state = xzalloc(sizeof(*ifp->state));
253 ifp->state->options = ifo = xzalloc(sizeof(*ifp->state->options));
254 ifp->state->xid = arc4random();
255
256 hlen = sizeof(ifo->hostname);
257 if (rump_sys___sysctl(mib, __arraycount(mib), ifo->hostname, &hlen,
258 NULL, 0) == -1)
259 snprintf(ifo->hostname, sizeof(ifo->hostname),
260 "unknown.hostname");
261 ifo->options = DHCPCD_GATEWAY | DHCPCD_HOSTNAME;
262
263 if (get_hwaddr(ifp) == -1)
264 err(EXIT_FAILURE, "failed to get hwaddr for %s", ifp->name);
265
266 send_discover(ifp);
267 get_offer(ifp);
268 send_request(ifp);
269 get_ack(ifp);
270
271 configure(ifp);
272
273 return 0;
274 }
275