1 /*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
4 * 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 AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE 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
21 * OR 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/kmem.h>
30 #include <sys/stat.h>
31 #include <sys/uio.h>
32 #include <sys/wait.h>
33
34 #include <netinet/in.h>
35
36 #include "dhcp_common.h"
37 #include "dhcp_configure.h"
38 #include "dhcp_dhcp.h"
39 #include "dhcp_if-options.h"
40 #include "dhcp_net.h"
41
42 #define HAVE_ROUTE_METRIC 0
43
44 static struct rt *routes;
45
46 static struct rt *
find_route(struct rt * rts,const struct rt * r,struct rt ** lrt,const struct rt * srt)47 find_route(struct rt *rts, const struct rt *r, struct rt **lrt,
48 const struct rt *srt)
49 {
50 struct rt *rt;
51
52 if (lrt)
53 *lrt = NULL;
54 for (rt = rts; rt; rt = rt->next) {
55 if (rt->dest.s_addr == r->dest.s_addr &&
56 #if HAVE_ROUTE_METRIC
57 (srt || (!rt->iface ||
58 rt->iface->metric == r->iface->metric)) &&
59 #endif
60 (!srt || srt != rt) &&
61 rt->net.s_addr == r->net.s_addr)
62 return rt;
63 if (lrt)
64 *lrt = rt;
65 }
66 return NULL;
67 }
68
69 static void
desc_route(const char * cmd,const struct rt * rt,const char * ifname)70 desc_route(const char *cmd, const struct rt *rt, const char *ifname)
71 {
72 char addr[sizeof("000.000.000.000") + 1];
73
74 strlcpy(addr, inet_ntoa(rt->dest), sizeof(addr));
75 if (rt->gate.s_addr == INADDR_ANY)
76 printf("dhcp: %s: %s route to %s/%d\n", ifname, cmd,
77 addr, inet_ntocidr(rt->net));
78 else if (rt->gate.s_addr == rt->dest.s_addr &&
79 rt->net.s_addr == INADDR_BROADCAST)
80 printf("dhcp: %s: %s host route to %s\n", ifname, cmd,
81 addr);
82 else if (rt->dest.s_addr == INADDR_ANY && rt->net.s_addr == INADDR_ANY)
83 printf("dhcp: %s: %s default route via %s\n", ifname, cmd,
84 inet_ntoa(rt->gate));
85 else
86 printf("dhcp: %s: %s route to %s/%d via %s\n", ifname, cmd,
87 addr, inet_ntocidr(rt->net), inet_ntoa(rt->gate));
88 }
89
90 /* If something other than dhcpcd removes a route,
91 * we need to remove it from our internal table. */
92 int
route_deleted(const struct rt * rt)93 route_deleted(const struct rt *rt)
94 {
95 struct rt *f, *l;
96
97 f = find_route(routes, rt, &l, NULL);
98 if (f == NULL)
99 return 0;
100 desc_route("removing", f, f->iface->name);
101 if (l)
102 l->next = f->next;
103 else
104 routes = f->next;
105 kmem_free(f, sizeof(*f));
106 return 1;
107 }
108
109 static int
n_route(struct rt * rt,const struct interface * iface)110 n_route(struct rt *rt, const struct interface *iface)
111 {
112 int error;
113
114 /* Don't set default routes if not asked to */
115 if (rt->dest.s_addr == 0 &&
116 rt->net.s_addr == 0 &&
117 !(iface->state->options->options & DHCPCD_GATEWAY))
118 return -1;
119
120 desc_route("adding", rt, iface->name);
121 if ((error = add_route(iface, &rt->dest, &rt->net, &rt->gate, iface->metric)) == 0)
122 return 0;
123 if (error == EEXIST) {
124 /* Pretend we added the subnet route */
125 if (rt->dest.s_addr == (iface->addr.s_addr & iface->net.s_addr) &&
126 rt->net.s_addr == iface->net.s_addr &&
127 rt->gate.s_addr == 0)
128 return 0;
129 else
130 return error;
131 }
132 printf("dhcp: %s: add_route failed\n", iface->name);
133 return error;
134 }
135
136 static int
c_route(struct rt * ort,struct rt * nrt,const struct interface * iface)137 c_route(struct rt *ort, struct rt *nrt, const struct interface *iface)
138 {
139 int error;
140
141 /* Don't set default routes if not asked to */
142 if (nrt->dest.s_addr == 0 &&
143 nrt->net.s_addr == 0 &&
144 !(iface->state->options->options & DHCPCD_GATEWAY))
145 return 1;
146
147 desc_route("changing", nrt, iface->name);
148 /* We delete and add the route so that we can change metric.
149 * This also has the nice side effect of flushing ARP entries so
150 * we don't have to do that manually. */
151 del_route(ort->iface, &ort->dest, &ort->net, &ort->gate,
152 ort->iface->metric);
153 if ((error = add_route(iface, &nrt->dest, &nrt->net, &nrt->gate,
154 iface->metric)) == 0)
155 return 0;
156 printf("%s: add_route failed: %d\n", iface->name, error);
157 return 1;
158 }
159
160 static int
d_route(struct rt * rt,const struct interface * iface,int metric)161 d_route(struct rt *rt, const struct interface *iface, int metric)
162 {
163 int retval;
164
165 desc_route("deleting", rt, iface->name);
166 retval = del_route(iface, &rt->dest, &rt->net, &rt->gate, metric);
167 if (retval != ENOENT && retval != ESRCH)
168 printf("%s: del_route: %d\n", iface->name, retval);
169 return retval;
170 }
171
172 static struct rt *
get_subnet_route(struct dhcp_message * dhcp)173 get_subnet_route(struct dhcp_message *dhcp)
174 {
175 in_addr_t addr;
176 struct in_addr net;
177 struct rt *rt;
178
179 addr = dhcp->yiaddr;
180 if (addr == 0)
181 addr = dhcp->ciaddr;
182 /* Ensure we have all the needed values */
183 if (get_option_addr(&net, dhcp, DHO_SUBNETMASK) != 0)
184 net.s_addr = get_netmask(addr);
185 if (net.s_addr == INADDR_BROADCAST || net.s_addr == INADDR_ANY)
186 return NULL;
187 rt = kmem_alloc(sizeof(*rt), KM_SLEEP);
188 rt->dest.s_addr = addr & net.s_addr;
189 rt->net.s_addr = net.s_addr;
190 rt->gate.s_addr = 0;
191 return rt;
192 }
193
194 static struct rt *
add_subnet_route(struct rt * rt,const struct interface * iface)195 add_subnet_route(struct rt *rt, const struct interface *iface)
196 {
197 struct rt *r;
198
199 if (iface->net.s_addr == INADDR_BROADCAST ||
200 iface->net.s_addr == INADDR_ANY ||
201 (iface->state->options->options &
202 (DHCPCD_INFORM | DHCPCD_STATIC) &&
203 iface->state->options->req_addr.s_addr == INADDR_ANY))
204 return rt;
205
206 r = kmem_alloc(sizeof(*r), KM_SLEEP);
207 r->dest.s_addr = iface->addr.s_addr & iface->net.s_addr;
208 r->net.s_addr = iface->net.s_addr;
209 r->gate.s_addr = 0;
210 r->next = rt;
211 return r;
212 }
213
214 static struct rt *
get_routes(const struct interface * iface)215 get_routes(const struct interface *iface)
216 {
217 struct rt *rt, *nrt = NULL, *r = NULL;
218
219 if (iface->state->options->routes != NULL) {
220 for (rt = iface->state->options->routes;
221 rt != NULL;
222 rt = rt->next)
223 {
224 if (rt->gate.s_addr == 0)
225 break;
226 if (r == NULL)
227 r = nrt = kmem_alloc(sizeof(*r), KM_SLEEP);
228 else {
229 r->next = kmem_alloc(sizeof(*r), KM_SLEEP);
230 r = r->next;
231 }
232 memcpy(r, rt, sizeof(*r));
233 r->next = NULL;
234 }
235 return nrt;
236 }
237
238 return get_option_routes(iface->state->new,
239 iface->name, &iface->state->options->options);
240 }
241
242 /* Some DHCP servers add set host routes by setting the gateway
243 * to the assinged IP address. This differs from our notion of a host route
244 * where the gateway is the destination address, so we fix it. */
245 static struct rt *
massage_host_routes(struct rt * rt,const struct interface * iface)246 massage_host_routes(struct rt *rt, const struct interface *iface)
247 {
248 struct rt *r;
249
250 for (r = rt; r; r = r->next)
251 if (r->gate.s_addr == iface->addr.s_addr &&
252 r->net.s_addr == INADDR_BROADCAST)
253 r->gate.s_addr = r->dest.s_addr;
254 return rt;
255 }
256
257 static struct rt *
add_destination_route(struct rt * rt,const struct interface * iface)258 add_destination_route(struct rt *rt, const struct interface *iface)
259 {
260 struct rt *r;
261
262 if (!(iface->flags & IFF_POINTOPOINT) ||
263 !has_option_mask(iface->state->options->dstmask, DHO_ROUTER))
264 return rt;
265 r = kmem_alloc(sizeof(*r), KM_SLEEP);
266 r->dest.s_addr = INADDR_ANY;
267 r->net.s_addr = INADDR_ANY;
268 r->gate.s_addr = iface->dst.s_addr;
269 r->next = rt;
270 return r;
271 }
272
273 /* We should check to ensure the routers are on the same subnet
274 * OR supply a host route. If not, warn and add a host route. */
275 static struct rt *
add_router_host_route(struct rt * rt,const struct interface * ifp)276 add_router_host_route(struct rt *rt, const struct interface *ifp)
277 {
278 struct rt *rtp, *rtl, *rtn;
279 const char *cp, *cp2, *cp3, *cplim;
280
281 for (rtp = rt, rtl = NULL; rtp; rtl = rtp, rtp = rtp->next) {
282 if (rtp->dest.s_addr != INADDR_ANY)
283 continue;
284 /* Scan for a route to match */
285 for (rtn = rt; rtn != rtp; rtn = rtn->next) {
286 /* match host */
287 if (rtn->dest.s_addr == rtp->gate.s_addr)
288 break;
289 /* match subnet */
290 cp = (const char *)&rtp->gate.s_addr;
291 cp2 = (const char *)&rtn->dest.s_addr;
292 cp3 = (const char *)&rtn->net.s_addr;
293 cplim = cp3 + sizeof(rtn->net.s_addr);
294 while (cp3 < cplim) {
295 if ((*cp++ ^ *cp2++) & *cp3++)
296 break;
297 }
298 if (cp3 == cplim)
299 break;
300 }
301 if (rtn != rtp)
302 continue;
303 if (ifp->flags & IFF_NOARP) {
304 printf("%s: forcing router %s through interface\n",
305 ifp->name, inet_ntoa(rtp->gate));
306 rtp->gate.s_addr = 0;
307 continue;
308 }
309 printf("%s: router %s requires a host route\n",
310 ifp->name, inet_ntoa(rtp->gate));
311 rtn = kmem_alloc(sizeof(*rtn), KM_SLEEP);
312 rtn->dest.s_addr = rtp->gate.s_addr;
313 rtn->net.s_addr = INADDR_BROADCAST;
314 rtn->gate.s_addr = rtp->gate.s_addr;
315 rtn->next = rtp;
316 if (rtl == NULL)
317 rt = rtn;
318 else
319 rtl->next = rtn;
320 }
321 return rt;
322 }
323
324 void
build_routes(void)325 build_routes(void)
326 {
327 struct rt *nrs = NULL, *dnr, *or, *rt, *rtn, *rtl, *lrt = NULL;
328 const struct interface *ifp;
329
330 for (ifp = ifaces; ifp; ifp = ifp->next) {
331 if (ifp->state->new == NULL)
332 continue;
333 dnr = get_routes(ifp);
334 dnr = massage_host_routes(dnr, ifp);
335 dnr = add_subnet_route(dnr, ifp);
336 dnr = add_router_host_route(dnr, ifp);
337 dnr = add_destination_route(dnr, ifp);
338 for (rt = dnr; rt && (rtn = rt->next, 1); lrt = rt, rt = rtn) {
339 rt->iface = ifp;
340 /* Is this route already in our table? */
341 if ((find_route(nrs, rt, NULL, NULL)) != NULL)
342 continue;
343 /* Do we already manage it? */
344 if ((or = find_route(routes, rt, &rtl, NULL))) {
345 if (or->iface != ifp ||
346 rt->gate.s_addr != or->gate.s_addr)
347 {
348 if (c_route(or, rt, ifp) != 0)
349 continue;
350 }
351 if (rtl != NULL)
352 rtl->next = or->next;
353 else
354 routes = or->next;
355 kmem_free(or, sizeof(*or));
356 } else {
357 if (n_route(rt, ifp) != 0)
358 continue;
359 }
360 if (dnr == rt)
361 dnr = rtn;
362 else if (lrt)
363 lrt->next = rtn;
364 rt->next = nrs;
365 nrs = rt;
366 }
367 free_routes(dnr);
368 }
369
370 /* Remove old routes we used to manage */
371 for (rt = routes; rt; rt = rt->next) {
372 if (find_route(nrs, rt, NULL, NULL) == NULL)
373 d_route(rt, rt->iface, rt->iface->metric);
374 }
375
376 free_routes(routes);
377 routes = nrs;
378 }
379
380 static int
delete_address(struct interface * iface)381 delete_address(struct interface *iface)
382 {
383 int error;
384 struct if_options *ifo;
385
386 ifo = iface->state->options;
387 if (ifo->options & DHCPCD_INFORM ||
388 (ifo->options & DHCPCD_STATIC && ifo->req_addr.s_addr == 0))
389 return 0;
390 printf("%s: deleting IP address %s/%d\n",
391 iface->name,
392 inet_ntoa(iface->addr),
393 inet_ntocidr(iface->net));
394 error = del_address(iface, &iface->addr, &iface->net);
395 if (error != EADDRNOTAVAIL)
396 printf("dhcp: del_address failed: %d\n", error);
397 iface->addr.s_addr = 0;
398 iface->net.s_addr = 0;
399 return error;
400 }
401
402 int
configure(struct interface * iface)403 configure(struct interface *iface)
404 {
405 struct dhcp_message *dhcp = iface->state->new;
406 struct dhcp_lease *lease = &iface->state->lease;
407 struct if_options *ifo = iface->state->options;
408 struct rt *rt;
409 int error;
410
411 /* This also changes netmask */
412 if (!(ifo->options & DHCPCD_INFORM) ||
413 !has_address(iface->name, &lease->addr, &lease->net))
414 {
415 printf("dhcp: %s: adding IP address %s/%d\n",
416 iface->name, inet_ntoa(lease->addr),
417 inet_ntocidr(lease->net));
418 if ((error = add_address(iface,
419 &lease->addr, &lease->net, &lease->brd)) != 0 &&
420 error != EEXIST)
421 {
422 printf("dhcp: add_address failed\n");
423 return error;
424 }
425 }
426
427 /* Now delete the old address if different */
428 if (iface->addr.s_addr != lease->addr.s_addr &&
429 iface->addr.s_addr != 0)
430 delete_address(iface);
431
432 iface->addr.s_addr = lease->addr.s_addr;
433 iface->net.s_addr = lease->net.s_addr;
434
435 /* We need to delete the subnet route to have our metric or
436 * prefer the interface. */
437 rt = get_subnet_route(dhcp);
438 if (rt != NULL) {
439 rt->iface = iface;
440 if (!find_route(routes, rt, NULL, NULL))
441 del_route(iface, &rt->dest, &rt->net, &rt->gate, 0);
442 kmem_free(rt, sizeof(*rt));
443 }
444
445 build_routes();
446
447 printf("lease time: ");
448 if (lease->leasetime == ~0U)
449 printf("infinite\n");
450 else
451 printf("%u seconds\n", lease->leasetime);
452
453 return 0;
454 }
455