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