xref: /dragonfly/contrib/dhcpcd/src/route.c (revision 5ca0a96d)
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * dhcpcd - route management
4  * Copyright (c) 2006-2020 Roy Marples <roy@marples.name>
5  * All rights reserved
6 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <assert.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <stdbool.h>
33 #include <stddef.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 
38 #include "config.h"
39 #include "common.h"
40 #include "dhcpcd.h"
41 #include "if.h"
42 #include "if-options.h"
43 #include "ipv4.h"
44 #include "ipv4ll.h"
45 #include "ipv6.h"
46 #include "logerr.h"
47 #include "route.h"
48 #include "sa.h"
49 
50 /* Needed for NetBSD-6, 7 and 8. */
51 #ifndef RB_TREE_FOREACH_SAFE
52 #ifndef RB_TREE_PREV
53 #define RB_TREE_NEXT(T, N) rb_tree_iterate((T), (N), RB_DIR_RIGHT)
54 #define RB_TREE_PREV(T, N) rb_tree_iterate((T), (N), RB_DIR_LEFT)
55 #endif
56 #define RB_TREE_FOREACH_SAFE(N, T, S) \
57     for ((N) = RB_TREE_MIN(T); \
58         (N) && ((S) = RB_TREE_NEXT((T), (N)), 1); \
59         (N) = (S))
60 #define RB_TREE_FOREACH_REVERSE_SAFE(N, T, S) \
61     for ((N) = RB_TREE_MAX(T); \
62         (N) && ((S) = RB_TREE_PREV((T), (N)), 1); \
63         (N) = (S))
64 #endif
65 
66 #ifdef RT_FREE_ROUTE_TABLE_STATS
67 static size_t croutes;
68 static size_t nroutes;
69 static size_t froutes;
70 static size_t mroutes;
71 #endif
72 
73 static void
74 rt_maskedaddr(struct sockaddr *dst,
75 	const struct sockaddr *addr, const struct sockaddr *netmask)
76 {
77 	const char *addrp = addr->sa_data, *netmaskp = netmask->sa_data;
78 	char *dstp = dst->sa_data;
79 	const char *addre = (char *)dst + sa_len(addr);
80 	const char *netmaske = (char *)dst + MIN(sa_len(addr), sa_len(netmask));
81 
82 	dst->sa_family = addr->sa_family;
83 #ifdef HAVE_SA_LEN
84 	dst->sa_len = addr->sa_len;
85 #endif
86 
87 	if (sa_is_unspecified(netmask)) {
88 		if (addre > dstp)
89 			memcpy(dstp, addrp, (size_t)(addre - dstp));
90 		return;
91 	}
92 
93 	while (dstp < netmaske)
94 		*dstp++ = *addrp++ & *netmaskp++;
95 	if (dstp < addre)
96 		memset(dstp, 0, (size_t)(addre - dstp));
97 }
98 
99 int
100 rt_cmp_dest(const struct rt *rt1, const struct rt *rt2)
101 {
102 	union sa_ss ma1 = { .sa.sa_family = AF_UNSPEC };
103 	union sa_ss ma2 = { .sa.sa_family = AF_UNSPEC };
104 
105 	rt_maskedaddr(&ma1.sa, &rt1->rt_dest, &rt1->rt_netmask);
106 	rt_maskedaddr(&ma2.sa, &rt2->rt_dest, &rt2->rt_netmask);
107 	return sa_cmp(&ma1.sa, &ma2.sa);
108 }
109 
110 /*
111  * On some systems, host routes have no need for a netmask.
112  * However DHCP specifies host routes using an all-ones netmask.
113  * This handy function allows easy comparison when the two
114  * differ.
115  */
116 static int
117 rt_cmp_netmask(const struct rt *rt1, const struct rt *rt2)
118 {
119 
120 	if (rt1->rt_flags & RTF_HOST && rt2->rt_flags & RTF_HOST)
121 		return 0;
122 	return sa_cmp(&rt1->rt_netmask, &rt2->rt_netmask);
123 }
124 
125 static int
126 rt_compare_os(__unused void *context, const void *node1, const void *node2)
127 {
128 	const struct rt *rt1 = node1, *rt2 = node2;
129 	int c;
130 
131 	/* Sort by masked destination. */
132 	c = rt_cmp_dest(rt1, rt2);
133 	if (c != 0)
134 		return c;
135 
136 #ifdef HAVE_ROUTE_METRIC
137 	c = (int)(rt1->rt_ifp->metric - rt2->rt_ifp->metric);
138 #endif
139 	return c;
140 }
141 
142 static int
143 rt_compare_list(__unused void *context, const void *node1, const void *node2)
144 {
145 	const struct rt *rt1 = node1, *rt2 = node2;
146 
147 	if (rt1->rt_order > rt2->rt_order)
148 		return 1;
149 	if (rt1->rt_order < rt2->rt_order)
150 		return -1;
151 	return 0;
152 }
153 
154 static int
155 rt_compare_proto(void *context, const void *node1, const void *node2)
156 {
157 	const struct rt *rt1 = node1, *rt2 = node2;
158 	int c;
159 	struct interface *ifp1, *ifp2;
160 
161 	assert(rt1->rt_ifp != NULL);
162 	assert(rt2->rt_ifp != NULL);
163 	ifp1 = rt1->rt_ifp;
164 	ifp2 = rt2->rt_ifp;
165 
166 	/* Prefer interfaces with a carrier. */
167 	c = ifp1->carrier - ifp2->carrier;
168 	if (c != 0)
169 		return -c;
170 
171 	/* Prefer roaming over non roaming if both carriers are down. */
172 	if (ifp1->carrier == LINK_DOWN && ifp2->carrier == LINK_DOWN) {
173 		bool roam1 = if_roaming(ifp1);
174 		bool roam2 = if_roaming(ifp2);
175 
176 		if (roam1 != roam2)
177 			return roam1 ? 1 : -1;
178 	}
179 
180 #ifdef INET
181 	/* IPv4LL routes always come last */
182 	if (rt1->rt_dflags & RTDF_IPV4LL && !(rt2->rt_dflags & RTDF_IPV4LL))
183 		return -1;
184 	else if (!(rt1->rt_dflags & RTDF_IPV4LL) && rt2->rt_dflags & RTDF_IPV4LL)
185 		return 1;
186 #endif
187 
188 	/* Lower metric interfaces come first. */
189 	c = (int)(ifp1->metric - ifp2->metric);
190 	if (c != 0)
191 		return c;
192 
193 	/* Finally the order in which the route was given to us. */
194 	return rt_compare_list(context, rt1, rt2);
195 }
196 
197 static const rb_tree_ops_t rt_compare_os_ops = {
198 	.rbto_compare_nodes = rt_compare_os,
199 	.rbto_compare_key = rt_compare_os,
200 	.rbto_node_offset = offsetof(struct rt, rt_tree),
201 	.rbto_context = NULL
202 };
203 
204 const rb_tree_ops_t rt_compare_list_ops = {
205 	.rbto_compare_nodes = rt_compare_list,
206 	.rbto_compare_key = rt_compare_list,
207 	.rbto_node_offset = offsetof(struct rt, rt_tree),
208 	.rbto_context = NULL
209 };
210 
211 const rb_tree_ops_t rt_compare_proto_ops = {
212 	.rbto_compare_nodes = rt_compare_proto,
213 	.rbto_compare_key = rt_compare_proto,
214 	.rbto_node_offset = offsetof(struct rt, rt_tree),
215 	.rbto_context = NULL
216 };
217 
218 #ifdef RT_FREE_ROUTE_TABLE
219 static int
220 rt_compare_free(__unused void *context, const void *node1, const void *node2)
221 {
222 
223 	return node1 == node2 ? 0 : node1 < node2 ? -1 : 1;
224 }
225 
226 static const rb_tree_ops_t rt_compare_free_ops = {
227 	.rbto_compare_nodes = rt_compare_free,
228 	.rbto_compare_key = rt_compare_free,
229 	.rbto_node_offset = offsetof(struct rt, rt_tree),
230 	.rbto_context = NULL
231 };
232 #endif
233 
234 void
235 rt_init(struct dhcpcd_ctx *ctx)
236 {
237 
238 	rb_tree_init(&ctx->routes, &rt_compare_os_ops);
239 #ifdef RT_FREE_ROUTE_TABLE
240 	rb_tree_init(&ctx->froutes, &rt_compare_free_ops);
241 #endif
242 }
243 
244 bool
245 rt_is_default(const struct rt *rt)
246 {
247 
248 	return sa_is_unspecified(&rt->rt_dest) &&
249 	    sa_is_unspecified(&rt->rt_netmask);
250 }
251 
252 static void
253 rt_desc(const char *cmd, const struct rt *rt)
254 {
255 	char dest[INET_MAX_ADDRSTRLEN], gateway[INET_MAX_ADDRSTRLEN];
256 	int prefix;
257 	const char *ifname;
258 	bool gateway_unspec;
259 
260 	assert(cmd != NULL);
261 	assert(rt != NULL);
262 
263 	sa_addrtop(&rt->rt_dest, dest, sizeof(dest));
264 	prefix = sa_toprefix(&rt->rt_netmask);
265 	sa_addrtop(&rt->rt_gateway, gateway, sizeof(gateway));
266 	gateway_unspec = sa_is_unspecified(&rt->rt_gateway);
267 	ifname = rt->rt_ifp == NULL ? "(null)" : rt->rt_ifp->name;
268 
269 	if (rt->rt_flags & RTF_HOST) {
270 		if (gateway_unspec)
271 			loginfox("%s: %s host route to %s",
272 			    ifname, cmd, dest);
273 		else
274 			loginfox("%s: %s host route to %s via %s",
275 			    ifname, cmd, dest, gateway);
276 	} else if (rt_is_default(rt)) {
277 		if (gateway_unspec)
278 			loginfox("%s: %s default route",
279 			    ifname, cmd);
280 		else
281 			loginfox("%s: %s default route via %s",
282 			    ifname, cmd, gateway);
283 	} else if (gateway_unspec)
284 		loginfox("%s: %s%s route to %s/%d",
285 		    ifname, cmd,
286 		    rt->rt_flags & RTF_REJECT ? " reject" : "",
287 		    dest, prefix);
288 	else
289 		loginfox("%s: %s%s route to %s/%d via %s",
290 		    ifname, cmd,
291 		    rt->rt_flags & RTF_REJECT ? " reject" : "",
292 		    dest, prefix, gateway);
293 }
294 
295 void
296 rt_headclear0(struct dhcpcd_ctx *ctx, rb_tree_t *rts, int af)
297 {
298 	struct rt *rt, *rtn;
299 
300 	if (rts == NULL)
301 		return;
302 	assert(ctx != NULL);
303 #ifdef RT_FREE_ROUTE_TABLE
304 	assert(&ctx->froutes != rts);
305 #endif
306 
307 	RB_TREE_FOREACH_SAFE(rt, rts, rtn) {
308 		if (af != AF_UNSPEC &&
309 		    rt->rt_dest.sa_family != af &&
310 		    rt->rt_gateway.sa_family != af)
311 			continue;
312 		rb_tree_remove_node(rts, rt);
313 		rt_free(rt);
314 	}
315 }
316 
317 void
318 rt_headclear(rb_tree_t *rts, int af)
319 {
320 	struct rt *rt;
321 
322 	if (rts == NULL || (rt = RB_TREE_MIN(rts)) == NULL)
323 		return;
324 	rt_headclear0(rt->rt_ifp->ctx, rts, af);
325 }
326 
327 static void
328 rt_headfree(rb_tree_t *rts)
329 {
330 	struct rt *rt;
331 
332 	while ((rt = RB_TREE_MIN(rts)) != NULL) {
333 		rb_tree_remove_node(rts, rt);
334 		free(rt);
335 	}
336 }
337 
338 void
339 rt_dispose(struct dhcpcd_ctx *ctx)
340 {
341 
342 	assert(ctx != NULL);
343 	rt_headfree(&ctx->routes);
344 #ifdef RT_FREE_ROUTE_TABLE
345 	rt_headfree(&ctx->froutes);
346 #ifdef RT_FREE_ROUTE_TABLE_STATS
347 	logdebugx("free route list used %zu times", froutes);
348 	logdebugx("new routes from route free list %zu", nroutes);
349 	logdebugx("maximum route free list size %zu", mroutes);
350 #endif
351 #endif
352 }
353 
354 struct rt *
355 rt_new0(struct dhcpcd_ctx *ctx)
356 {
357 	struct rt *rt;
358 
359 	assert(ctx != NULL);
360 #ifdef RT_FREE_ROUTE_TABLE
361 	if ((rt = RB_TREE_MIN(&ctx->froutes)) != NULL) {
362 		rb_tree_remove_node(&ctx->froutes, rt);
363 #ifdef RT_FREE_ROUTE_TABLE_STATS
364 		croutes--;
365 		nroutes++;
366 #endif
367 	} else
368 #endif
369 	if ((rt = malloc(sizeof(*rt))) == NULL) {
370 		logerr(__func__);
371 		return NULL;
372 	}
373 	memset(rt, 0, sizeof(*rt));
374 	return rt;
375 }
376 
377 void
378 rt_setif(struct rt *rt, struct interface *ifp)
379 {
380 
381 	assert(rt != NULL);
382 	assert(ifp != NULL);
383 	rt->rt_ifp = ifp;
384 #ifdef HAVE_ROUTE_METRIC
385 	rt->rt_metric = ifp->metric;
386 	if (if_roaming(ifp))
387 		rt->rt_metric += RTMETRIC_ROAM;
388 #endif
389 }
390 
391 struct rt *
392 rt_new(struct interface *ifp)
393 {
394 	struct rt *rt;
395 
396 	assert(ifp != NULL);
397 	if ((rt = rt_new0(ifp->ctx)) == NULL)
398 		return NULL;
399 	rt_setif(rt, ifp);
400 	return rt;
401 }
402 
403 struct rt *
404 rt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx)
405 {
406 
407 	rt->rt_order = ctx->rt_order++;
408 	if (rb_tree_insert_node(tree, rt) == rt)
409 		return rt;
410 
411 	rt_free(rt);
412 	return NULL;
413 }
414 
415 struct rt *
416 rt_proto_add(rb_tree_t *tree, struct rt *rt)
417 {
418 
419 	assert (rt->rt_ifp != NULL);
420 	return rt_proto_add_ctx(tree, rt, rt->rt_ifp->ctx);
421 }
422 
423 void
424 rt_free(struct rt *rt)
425 {
426 #ifdef RT_FREE_ROUTE_TABLE
427 	struct dhcpcd_ctx *ctx;
428 
429 	assert(rt != NULL);
430 	if (rt->rt_ifp == NULL) {
431 		free(rt);
432 		return;
433 	}
434 
435 	ctx = rt->rt_ifp->ctx;
436 	rb_tree_insert_node(&ctx->froutes, rt);
437 #ifdef RT_FREE_ROUTE_TABLE_STATS
438 	croutes++;
439 	froutes++;
440 	if (croutes > mroutes)
441 		mroutes = croutes;
442 #endif
443 #else
444 	free(rt);
445 #endif
446 }
447 
448 void
449 rt_freeif(struct interface *ifp)
450 {
451 	struct dhcpcd_ctx *ctx;
452 	struct rt *rt, *rtn;
453 
454 	if (ifp == NULL)
455 		return;
456 	ctx = ifp->ctx;
457 	RB_TREE_FOREACH_SAFE(rt, &ctx->routes, rtn) {
458 		if (rt->rt_ifp == ifp) {
459 			rb_tree_remove_node(&ctx->routes, rt);
460 			rt_free(rt);
461 		}
462 	}
463 }
464 
465 /* If something other than dhcpcd removes a route,
466  * we need to remove it from our internal table. */
467 void
468 rt_recvrt(int cmd, const struct rt *rt, pid_t pid)
469 {
470 	struct dhcpcd_ctx *ctx;
471 	struct rt *f;
472 
473 	assert(rt != NULL);
474 	assert(rt->rt_ifp != NULL);
475 	assert(rt->rt_ifp->ctx != NULL);
476 
477 	ctx = rt->rt_ifp->ctx;
478 
479 	switch(cmd) {
480 	case RTM_DELETE:
481 		f = rb_tree_find_node(&ctx->routes, rt);
482 		if (f != NULL) {
483 			char buf[32];
484 
485 			rb_tree_remove_node(&ctx->routes, f);
486 			snprintf(buf, sizeof(buf), "pid %d deleted", pid);
487 			rt_desc(buf, f);
488 			rt_free(f);
489 		}
490 		break;
491 	}
492 
493 #if defined(IPV4LL) && defined(HAVE_ROUTE_METRIC)
494 	if (rt->rt_dest.sa_family == AF_INET)
495 		ipv4ll_recvrt(cmd, rt);
496 #endif
497 }
498 
499 static bool
500 rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
501 {
502 	struct dhcpcd_ctx *ctx;
503 	bool change, kroute, result;
504 
505 	assert(nrt != NULL);
506 	ctx = nrt->rt_ifp->ctx;
507 
508 	/*
509 	 * Don't install a gateway if not asked to.
510 	 * This option is mainly for VPN users who want their VPN to be the
511 	 * default route.
512 	 * Because VPN's generally don't care about route management
513 	 * beyond their own, a longer term solution would be to remove this
514 	 * and get the VPN to inject the default route into dhcpcd somehow.
515 	 */
516 	if (((nrt->rt_ifp->active &&
517 	    !(nrt->rt_ifp->options->options & DHCPCD_GATEWAY)) ||
518 	    (!nrt->rt_ifp->active && !(ctx->options & DHCPCD_GATEWAY))) &&
519 	    sa_is_unspecified(&nrt->rt_dest) &&
520 	    sa_is_unspecified(&nrt->rt_netmask))
521 		return false;
522 
523 	rt_desc(ort == NULL ? "adding" : "changing", nrt);
524 
525 	change = kroute = result = false;
526 	if (ort == NULL) {
527 		ort = rb_tree_find_node(kroutes, nrt);
528 		if (ort != NULL &&
529 		    ((ort->rt_flags & RTF_REJECT &&
530 		      nrt->rt_flags & RTF_REJECT) ||
531 		     (ort->rt_ifp == nrt->rt_ifp &&
532 #ifdef HAVE_ROUTE_METRIC
533 		    ort->rt_metric == nrt->rt_metric &&
534 #endif
535 		    sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)))
536 		{
537 			if (ort->rt_mtu == nrt->rt_mtu)
538 				return true;
539 			change = true;
540 			kroute = true;
541 		}
542 	} else if (ort->rt_dflags & RTDF_FAKE &&
543 	    !(nrt->rt_dflags & RTDF_FAKE) &&
544 	    ort->rt_ifp == nrt->rt_ifp &&
545 #ifdef HAVE_ROUTE_METRIC
546 	    ort->rt_metric == nrt->rt_metric &&
547 #endif
548 	    sa_cmp(&ort->rt_dest, &nrt->rt_dest) == 0 &&
549 	    rt_cmp_netmask(ort, nrt) == 0 &&
550 	    sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)
551 	{
552 		if (ort->rt_mtu == nrt->rt_mtu)
553 			return true;
554 		change = true;
555 	}
556 
557 #ifdef RTF_CLONING
558 	/* BSD can set routes to be cloning routes.
559 	 * Cloned routes inherit the parent flags.
560 	 * As such, we need to delete and re-add the route to flush children
561 	 * to correct the flags. */
562 	if (change && ort != NULL && ort->rt_flags & RTF_CLONING)
563 		change = false;
564 #endif
565 
566 	if (change) {
567 		if (if_route(RTM_CHANGE, nrt) != -1) {
568 			result = true;
569 			goto out;
570 		}
571 		if (errno != ESRCH)
572 			logerr("if_route (CHG)");
573 	}
574 
575 #ifdef HAVE_ROUTE_METRIC
576 	/* With route metrics, we can safely add the new route before
577 	 * deleting the old route. */
578 	if (if_route(RTM_ADD, nrt) != -1) {
579 		if (ort != NULL) {
580 			if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
581 				logerr("if_route (DEL)");
582 		}
583 		result = true;
584 		goto out;
585 	}
586 
587 	/* If the kernel claims the route exists we need to rip out the
588 	 * old one first. */
589 	if (errno != EEXIST || ort == NULL)
590 		goto logerr;
591 #endif
592 
593 	/* No route metrics, we need to delete the old route before
594 	 * adding the new one. */
595 #ifdef ROUTE_PER_GATEWAY
596 	errno = 0;
597 #endif
598 	if (ort != NULL) {
599 		if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
600 			logerr("if_route (DEL)");
601 		else
602 			kroute = false;
603 	}
604 #ifdef ROUTE_PER_GATEWAY
605 	/* The OS allows many routes to the same dest with different gateways.
606 	 * dhcpcd does not support this yet, so for the time being just keep on
607 	 * deleting the route until there is an error. */
608 	if (ort != NULL && errno == 0) {
609 		for (;;) {
610 			if (if_route(RTM_DELETE, ort) == -1)
611 				break;
612 		}
613 	}
614 #endif
615 
616 	/* Shouldn't need to check for EEXIST, but some kernels don't
617 	 * dump the subnet route just after we added the address. */
618 	if (if_route(RTM_ADD, nrt) != -1 || errno == EEXIST) {
619 		result = true;
620 		goto out;
621 	}
622 
623 #ifdef HAVE_ROUTE_METRIC
624 logerr:
625 #endif
626 	logerr("if_route (ADD)");
627 
628 out:
629 	if (kroute) {
630 		rb_tree_remove_node(kroutes, ort);
631 		rt_free(ort);
632 	}
633 	return result;
634 }
635 
636 static bool
637 rt_delete(struct rt *rt)
638 {
639 	int retval;
640 
641 	rt_desc("deleting", rt);
642 	retval = if_route(RTM_DELETE, rt) == -1 ? false : true;
643 	if (!retval && errno != ENOENT && errno != ESRCH)
644 		logerr(__func__);
645 	return retval;
646 }
647 
648 static bool
649 rt_cmp(const struct rt *r1, const struct rt *r2)
650 {
651 
652 	return (r1->rt_ifp == r2->rt_ifp &&
653 #ifdef HAVE_ROUTE_METRIC
654 	    r1->rt_metric == r2->rt_metric &&
655 #endif
656 	    sa_cmp(&r1->rt_gateway, &r2->rt_gateway) == 0);
657 }
658 
659 static bool
660 rt_doroute(rb_tree_t *kroutes, struct rt *rt)
661 {
662 	struct dhcpcd_ctx *ctx;
663 	struct rt *or;
664 
665 	ctx = rt->rt_ifp->ctx;
666 	/* Do we already manage it? */
667 	or = rb_tree_find_node(&ctx->routes, rt);
668 	if (or != NULL) {
669 		if (rt->rt_dflags & RTDF_FAKE)
670 			return true;
671 		if (or->rt_dflags & RTDF_FAKE ||
672 		    !rt_cmp(rt, or) ||
673 		    (rt->rt_ifa.sa_family != AF_UNSPEC &&
674 		    sa_cmp(&or->rt_ifa, &rt->rt_ifa) != 0) ||
675 		    or->rt_mtu != rt->rt_mtu)
676 		{
677 			if (!rt_add(kroutes, rt, or))
678 				return false;
679 		}
680 		rb_tree_remove_node(&ctx->routes, or);
681 		rt_free(or);
682 	} else {
683 		if (rt->rt_dflags & RTDF_FAKE) {
684 			or = rb_tree_find_node(kroutes, rt);
685 			if (or == NULL)
686 				return false;
687 			if (!rt_cmp(rt, or))
688 				return false;
689 		} else {
690 			if (!rt_add(kroutes, rt, NULL))
691 				return false;
692 		}
693 	}
694 
695 	return true;
696 }
697 
698 void
699 rt_build(struct dhcpcd_ctx *ctx, int af)
700 {
701 	rb_tree_t routes, added, kroutes;
702 	struct rt *rt, *rtn;
703 	unsigned long long o;
704 
705 	rb_tree_init(&routes, &rt_compare_proto_ops);
706 	rb_tree_init(&added, &rt_compare_os_ops);
707 	rb_tree_init(&kroutes, &rt_compare_os_ops);
708 	if_initrt(ctx, &kroutes, af);
709 	ctx->rt_order = 0;
710 	ctx->options |= DHCPCD_RTBUILD;
711 
712 #ifdef INET
713 	if (!inet_getroutes(ctx, &routes))
714 		goto getfail;
715 #endif
716 #ifdef INET6
717 	if (!inet6_getroutes(ctx, &routes))
718 		goto getfail;
719 #endif
720 
721 #ifdef BSD
722 	/* Rewind the miss filter */
723 	ctx->rt_missfilterlen = 0;
724 #endif
725 
726 	RB_TREE_FOREACH_SAFE(rt, &routes, rtn) {
727 		if (rt->rt_ifp->active) {
728 			if (!(rt->rt_ifp->options->options & DHCPCD_CONFIGURE))
729 				continue;
730 		} else if (!(ctx->options & DHCPCD_CONFIGURE))
731 			continue;
732 #ifdef BSD
733 		if (rt_is_default(rt) &&
734 		    if_missfilter(rt->rt_ifp, &rt->rt_gateway) == -1)
735 			logerr("if_missfilter");
736 #endif
737 		if ((rt->rt_dest.sa_family != af &&
738 		    rt->rt_dest.sa_family != AF_UNSPEC) ||
739 		    (rt->rt_gateway.sa_family != af &&
740 		    rt->rt_gateway.sa_family != AF_UNSPEC))
741 			continue;
742 		/* Is this route already in our table? */
743 		if (rb_tree_find_node(&added, rt) != NULL)
744 			continue;
745 		if (rt_doroute(&kroutes, rt)) {
746 			rb_tree_remove_node(&routes, rt);
747 			if (rb_tree_insert_node(&added, rt) != rt) {
748 				errno = EEXIST;
749 				logerr(__func__);
750 				rt_free(rt);
751 			}
752 		}
753 	}
754 
755 #ifdef BSD
756 	if (if_missfilter_apply(ctx) == -1 && errno != ENOTSUP)
757 		logerr("if_missfilter_apply");
758 #endif
759 
760 	/* Remove old routes we used to manage. */
761 	RB_TREE_FOREACH_REVERSE_SAFE(rt, &ctx->routes, rtn) {
762 		if ((rt->rt_dest.sa_family != af &&
763 		    rt->rt_dest.sa_family != AF_UNSPEC) ||
764 		    (rt->rt_gateway.sa_family != af &&
765 		    rt->rt_gateway.sa_family != AF_UNSPEC))
766 			continue;
767 		rb_tree_remove_node(&ctx->routes, rt);
768 		if (rb_tree_find_node(&added, rt) == NULL) {
769 			o = rt->rt_ifp->options ?
770 			    rt->rt_ifp->options->options :
771 			    ctx->options;
772 			if ((o &
773 				(DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
774 				(DHCPCD_EXITING | DHCPCD_PERSISTENT))
775 				rt_delete(rt);
776 		}
777 		rt_free(rt);
778 	}
779 
780 	/* XXX This needs to be optimised. */
781 	while ((rt = RB_TREE_MIN(&added)) != NULL) {
782 		rb_tree_remove_node(&added, rt);
783 		if (rb_tree_insert_node(&ctx->routes, rt) != rt) {
784 			errno = EEXIST;
785 			logerr(__func__);
786 			rt_free(rt);
787 		}
788 	}
789 
790 getfail:
791 	rt_headclear(&routes, AF_UNSPEC);
792 	rt_headclear(&kroutes, AF_UNSPEC);
793 }
794