xref: /dflybsd-src/contrib/dhcpcd/src/route.c (revision c80c9bba1b2fa2824af94c686145cb7eb7db2cd5)
18d36e1dfSRoy Marples /* SPDX-License-Identifier: BSD-2-Clause */
27827cba2SAaron LI /*
37827cba2SAaron LI  * dhcpcd - route management
4*80aa9461SRoy Marples  * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
57827cba2SAaron LI  * All rights reserved
67827cba2SAaron LI 
77827cba2SAaron LI  * Redistribution and use in source and binary forms, with or without
87827cba2SAaron LI  * modification, are permitted provided that the following conditions
97827cba2SAaron LI  * are met:
107827cba2SAaron LI  * 1. Redistributions of source code must retain the above copyright
117827cba2SAaron LI  *    notice, this list of conditions and the following disclaimer.
127827cba2SAaron LI  * 2. Redistributions in binary form must reproduce the above copyright
137827cba2SAaron LI  *    notice, this list of conditions and the following disclaimer in the
147827cba2SAaron LI  *    documentation and/or other materials provided with the distribution.
157827cba2SAaron LI  *
167827cba2SAaron LI  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
177827cba2SAaron LI  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
187827cba2SAaron LI  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
197827cba2SAaron LI  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
207827cba2SAaron LI  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
217827cba2SAaron LI  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
227827cba2SAaron LI  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237827cba2SAaron LI  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
247827cba2SAaron LI  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257827cba2SAaron LI  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267827cba2SAaron LI  * SUCH DAMAGE.
277827cba2SAaron LI  */
287827cba2SAaron LI 
297827cba2SAaron LI #include <assert.h>
307827cba2SAaron LI #include <ctype.h>
317827cba2SAaron LI #include <errno.h>
327827cba2SAaron LI #include <stdbool.h>
338d36e1dfSRoy Marples #include <stddef.h>
347827cba2SAaron LI #include <stdlib.h>
357827cba2SAaron LI #include <string.h>
367827cba2SAaron LI #include <unistd.h>
377827cba2SAaron LI 
387827cba2SAaron LI #include "config.h"
397827cba2SAaron LI #include "common.h"
407827cba2SAaron LI #include "dhcpcd.h"
417827cba2SAaron LI #include "if.h"
428d36e1dfSRoy Marples #include "if-options.h"
437827cba2SAaron LI #include "ipv4.h"
447827cba2SAaron LI #include "ipv4ll.h"
457827cba2SAaron LI #include "ipv6.h"
467827cba2SAaron LI #include "logerr.h"
477827cba2SAaron LI #include "route.h"
487827cba2SAaron LI #include "sa.h"
497827cba2SAaron LI 
508d36e1dfSRoy Marples /* Needed for NetBSD-6, 7 and 8. */
518d36e1dfSRoy Marples #ifndef RB_TREE_FOREACH_SAFE
528d36e1dfSRoy Marples #ifndef RB_TREE_PREV
538d36e1dfSRoy Marples #define RB_TREE_NEXT(T, N) rb_tree_iterate((T), (N), RB_DIR_RIGHT)
548d36e1dfSRoy Marples #define RB_TREE_PREV(T, N) rb_tree_iterate((T), (N), RB_DIR_LEFT)
558d36e1dfSRoy Marples #endif
568d36e1dfSRoy Marples #define RB_TREE_FOREACH_SAFE(N, T, S) \
578d36e1dfSRoy Marples     for ((N) = RB_TREE_MIN(T); \
588d36e1dfSRoy Marples         (N) && ((S) = RB_TREE_NEXT((T), (N)), 1); \
598d36e1dfSRoy Marples         (N) = (S))
608d36e1dfSRoy Marples #define RB_TREE_FOREACH_REVERSE_SAFE(N, T, S) \
618d36e1dfSRoy Marples     for ((N) = RB_TREE_MAX(T); \
628d36e1dfSRoy Marples         (N) && ((S) = RB_TREE_PREV((T), (N)), 1); \
638d36e1dfSRoy Marples         (N) = (S))
648d36e1dfSRoy Marples #endif
658d36e1dfSRoy Marples 
668d36e1dfSRoy Marples #ifdef RT_FREE_ROUTE_TABLE_STATS
678d36e1dfSRoy Marples static size_t croutes;
688d36e1dfSRoy Marples static size_t nroutes;
698d36e1dfSRoy Marples static size_t froutes;
708d36e1dfSRoy Marples static size_t mroutes;
718d36e1dfSRoy Marples #endif
728d36e1dfSRoy Marples 
738d36e1dfSRoy Marples static void
rt_maskedaddr(struct sockaddr * dst,const struct sockaddr * addr,const struct sockaddr * netmask)748d36e1dfSRoy Marples rt_maskedaddr(struct sockaddr *dst,
758d36e1dfSRoy Marples 	const struct sockaddr *addr, const struct sockaddr *netmask)
768d36e1dfSRoy Marples {
778d36e1dfSRoy Marples 	const char *addrp = addr->sa_data, *netmaskp = netmask->sa_data;
788d36e1dfSRoy Marples 	char *dstp = dst->sa_data;
798d36e1dfSRoy Marples 	const char *addre = (char *)dst + sa_len(addr);
808d36e1dfSRoy Marples 	const char *netmaske = (char *)dst + MIN(sa_len(addr), sa_len(netmask));
818d36e1dfSRoy Marples 
828d36e1dfSRoy Marples 	dst->sa_family = addr->sa_family;
838d36e1dfSRoy Marples #ifdef HAVE_SA_LEN
848d36e1dfSRoy Marples 	dst->sa_len = addr->sa_len;
858d36e1dfSRoy Marples #endif
868d36e1dfSRoy Marples 
878d36e1dfSRoy Marples 	if (sa_is_unspecified(netmask)) {
888d36e1dfSRoy Marples 		if (addre > dstp)
898d36e1dfSRoy Marples 			memcpy(dstp, addrp, (size_t)(addre - dstp));
908d36e1dfSRoy Marples 		return;
918d36e1dfSRoy Marples 	}
928d36e1dfSRoy Marples 
938d36e1dfSRoy Marples 	while (dstp < netmaske)
948d36e1dfSRoy Marples 		*dstp++ = *addrp++ & *netmaskp++;
958d36e1dfSRoy Marples 	if (dstp < addre)
968d36e1dfSRoy Marples 		memset(dstp, 0, (size_t)(addre - dstp));
978d36e1dfSRoy Marples }
988d36e1dfSRoy Marples 
997827cba2SAaron LI /*
1007827cba2SAaron LI  * On some systems, host routes have no need for a netmask.
1017827cba2SAaron LI  * However DHCP specifies host routes using an all-ones netmask.
1027827cba2SAaron LI  * This handy function allows easy comparison when the two
1037827cba2SAaron LI  * differ.
1047827cba2SAaron LI  */
1057827cba2SAaron LI static int
rt_cmp_netmask(const struct rt * rt1,const struct rt * rt2)1067827cba2SAaron LI rt_cmp_netmask(const struct rt *rt1, const struct rt *rt2)
1077827cba2SAaron LI {
1087827cba2SAaron LI 
1097827cba2SAaron LI 	if (rt1->rt_flags & RTF_HOST && rt2->rt_flags & RTF_HOST)
1107827cba2SAaron LI 		return 0;
1117827cba2SAaron LI 	return sa_cmp(&rt1->rt_netmask, &rt2->rt_netmask);
1127827cba2SAaron LI }
1137827cba2SAaron LI 
1140a68f8d2SRoy Marples int
rt_cmp_dest(const struct rt * rt1,const struct rt * rt2)1150a68f8d2SRoy Marples rt_cmp_dest(const struct rt *rt1, const struct rt *rt2)
1160a68f8d2SRoy Marples {
1170a68f8d2SRoy Marples 	union sa_ss ma1 = { .sa.sa_family = AF_UNSPEC };
1180a68f8d2SRoy Marples 	union sa_ss ma2 = { .sa.sa_family = AF_UNSPEC };
1190a68f8d2SRoy Marples 	int c;
1200a68f8d2SRoy Marples 
1210a68f8d2SRoy Marples 	rt_maskedaddr(&ma1.sa, &rt1->rt_dest, &rt1->rt_netmask);
1220a68f8d2SRoy Marples 	rt_maskedaddr(&ma2.sa, &rt2->rt_dest, &rt2->rt_netmask);
1230a68f8d2SRoy Marples 	c = sa_cmp(&ma1.sa, &ma2.sa);
1240a68f8d2SRoy Marples 	if (c != 0)
1250a68f8d2SRoy Marples 		return c;
1260a68f8d2SRoy Marples 
1270a68f8d2SRoy Marples 	return rt_cmp_netmask(rt1, rt2);
1280a68f8d2SRoy Marples }
1290a68f8d2SRoy Marples 
1308d36e1dfSRoy Marples static int
rt_compare_os(__unused void * context,const void * node1,const void * node2)1318d36e1dfSRoy Marples rt_compare_os(__unused void *context, const void *node1, const void *node2)
1328d36e1dfSRoy Marples {
1338d36e1dfSRoy Marples 	const struct rt *rt1 = node1, *rt2 = node2;
1348d36e1dfSRoy Marples 	int c;
1358d36e1dfSRoy Marples 
1368d36e1dfSRoy Marples 	/* Sort by masked destination. */
1378d36e1dfSRoy Marples 	c = rt_cmp_dest(rt1, rt2);
1388d36e1dfSRoy Marples 	if (c != 0)
1398d36e1dfSRoy Marples 		return c;
1408d36e1dfSRoy Marples 
1418d36e1dfSRoy Marples #ifdef HAVE_ROUTE_METRIC
1428d36e1dfSRoy Marples 	c = (int)(rt1->rt_ifp->metric - rt2->rt_ifp->metric);
1438d36e1dfSRoy Marples #endif
1448d36e1dfSRoy Marples 	return c;
1458d36e1dfSRoy Marples }
1468d36e1dfSRoy Marples 
1478d36e1dfSRoy Marples static int
rt_compare_list(__unused void * context,const void * node1,const void * node2)1488d36e1dfSRoy Marples rt_compare_list(__unused void *context, const void *node1, const void *node2)
1498d36e1dfSRoy Marples {
1508d36e1dfSRoy Marples 	const struct rt *rt1 = node1, *rt2 = node2;
1518d36e1dfSRoy Marples 
1528d36e1dfSRoy Marples 	if (rt1->rt_order > rt2->rt_order)
1538d36e1dfSRoy Marples 		return 1;
1548d36e1dfSRoy Marples 	if (rt1->rt_order < rt2->rt_order)
1558d36e1dfSRoy Marples 		return -1;
1568d36e1dfSRoy Marples 	return 0;
1578d36e1dfSRoy Marples }
1588d36e1dfSRoy Marples 
1598d36e1dfSRoy Marples static int
rt_compare_proto(void * context,const void * node1,const void * node2)1608d36e1dfSRoy Marples rt_compare_proto(void *context, const void *node1, const void *node2)
1618d36e1dfSRoy Marples {
1628d36e1dfSRoy Marples 	const struct rt *rt1 = node1, *rt2 = node2;
1638d36e1dfSRoy Marples 	int c;
1648d36e1dfSRoy Marples 	struct interface *ifp1, *ifp2;
1658d36e1dfSRoy Marples 
1668d36e1dfSRoy Marples 	assert(rt1->rt_ifp != NULL);
1678d36e1dfSRoy Marples 	assert(rt2->rt_ifp != NULL);
1688d36e1dfSRoy Marples 	ifp1 = rt1->rt_ifp;
1698d36e1dfSRoy Marples 	ifp2 = rt2->rt_ifp;
1708d36e1dfSRoy Marples 
1718d36e1dfSRoy Marples 	/* Prefer interfaces with a carrier. */
1728d36e1dfSRoy Marples 	c = ifp1->carrier - ifp2->carrier;
1738d36e1dfSRoy Marples 	if (c != 0)
1748d36e1dfSRoy Marples 		return -c;
1758d36e1dfSRoy Marples 
1760aaf6155SRoy Marples 	/* Prefer roaming over non roaming if both carriers are down. */
1770aaf6155SRoy Marples 	if (ifp1->carrier == LINK_DOWN && ifp2->carrier == LINK_DOWN) {
1780aaf6155SRoy Marples 		bool roam1 = if_roaming(ifp1);
1790aaf6155SRoy Marples 		bool roam2 = if_roaming(ifp2);
1800aaf6155SRoy Marples 
1810aaf6155SRoy Marples 		if (roam1 != roam2)
1820aaf6155SRoy Marples 			return roam1 ? 1 : -1;
1830aaf6155SRoy Marples 	}
1840aaf6155SRoy Marples 
185b8b69544SRoy Marples #ifdef INET
186b8b69544SRoy Marples 	/* IPv4LL routes always come last */
187b8b69544SRoy Marples 	if (rt1->rt_dflags & RTDF_IPV4LL && !(rt2->rt_dflags & RTDF_IPV4LL))
188b8b69544SRoy Marples 		return -1;
189b8b69544SRoy Marples 	else if (!(rt1->rt_dflags & RTDF_IPV4LL) && rt2->rt_dflags & RTDF_IPV4LL)
190b8b69544SRoy Marples 		return 1;
191b8b69544SRoy Marples #endif
192b8b69544SRoy Marples 
1938d36e1dfSRoy Marples 	/* Lower metric interfaces come first. */
1948d36e1dfSRoy Marples 	c = (int)(ifp1->metric - ifp2->metric);
1958d36e1dfSRoy Marples 	if (c != 0)
1968d36e1dfSRoy Marples 		return c;
1978d36e1dfSRoy Marples 
1988d36e1dfSRoy Marples 	/* Finally the order in which the route was given to us. */
1998d36e1dfSRoy Marples 	return rt_compare_list(context, rt1, rt2);
2008d36e1dfSRoy Marples }
2018d36e1dfSRoy Marples 
2028d36e1dfSRoy Marples static const rb_tree_ops_t rt_compare_os_ops = {
2038d36e1dfSRoy Marples 	.rbto_compare_nodes = rt_compare_os,
2048d36e1dfSRoy Marples 	.rbto_compare_key = rt_compare_os,
2058d36e1dfSRoy Marples 	.rbto_node_offset = offsetof(struct rt, rt_tree),
2068d36e1dfSRoy Marples 	.rbto_context = NULL
2078d36e1dfSRoy Marples };
2088d36e1dfSRoy Marples 
2098d36e1dfSRoy Marples const rb_tree_ops_t rt_compare_list_ops = {
2108d36e1dfSRoy Marples 	.rbto_compare_nodes = rt_compare_list,
2118d36e1dfSRoy Marples 	.rbto_compare_key = rt_compare_list,
2128d36e1dfSRoy Marples 	.rbto_node_offset = offsetof(struct rt, rt_tree),
2138d36e1dfSRoy Marples 	.rbto_context = NULL
2148d36e1dfSRoy Marples };
2158d36e1dfSRoy Marples 
2168d36e1dfSRoy Marples const rb_tree_ops_t rt_compare_proto_ops = {
2178d36e1dfSRoy Marples 	.rbto_compare_nodes = rt_compare_proto,
2188d36e1dfSRoy Marples 	.rbto_compare_key = rt_compare_proto,
2198d36e1dfSRoy Marples 	.rbto_node_offset = offsetof(struct rt, rt_tree),
2208d36e1dfSRoy Marples 	.rbto_context = NULL
2218d36e1dfSRoy Marples };
2228d36e1dfSRoy Marples 
2238d36e1dfSRoy Marples #ifdef RT_FREE_ROUTE_TABLE
2248d36e1dfSRoy Marples static int
rt_compare_free(__unused void * context,const void * node1,const void * node2)2258d36e1dfSRoy Marples rt_compare_free(__unused void *context, const void *node1, const void *node2)
2268d36e1dfSRoy Marples {
2278d36e1dfSRoy Marples 
2288d36e1dfSRoy Marples 	return node1 == node2 ? 0 : node1 < node2 ? -1 : 1;
2298d36e1dfSRoy Marples }
2308d36e1dfSRoy Marples 
2318d36e1dfSRoy Marples static const rb_tree_ops_t rt_compare_free_ops = {
2328d36e1dfSRoy Marples 	.rbto_compare_nodes = rt_compare_free,
2338d36e1dfSRoy Marples 	.rbto_compare_key = rt_compare_free,
2348d36e1dfSRoy Marples 	.rbto_node_offset = offsetof(struct rt, rt_tree),
2358d36e1dfSRoy Marples 	.rbto_context = NULL
2368d36e1dfSRoy Marples };
2378d36e1dfSRoy Marples #endif
2388d36e1dfSRoy Marples 
2397827cba2SAaron LI void
rt_init(struct dhcpcd_ctx * ctx)2407827cba2SAaron LI rt_init(struct dhcpcd_ctx *ctx)
2417827cba2SAaron LI {
2427827cba2SAaron LI 
2438d36e1dfSRoy Marples 	rb_tree_init(&ctx->routes, &rt_compare_os_ops);
2448d36e1dfSRoy Marples #ifdef RT_FREE_ROUTE_TABLE
2458d36e1dfSRoy Marples 	rb_tree_init(&ctx->froutes, &rt_compare_free_ops);
2468d36e1dfSRoy Marples #endif
2478d36e1dfSRoy Marples }
2488d36e1dfSRoy Marples 
2498d36e1dfSRoy Marples bool
rt_is_default(const struct rt * rt)2508d36e1dfSRoy Marples rt_is_default(const struct rt *rt)
2518d36e1dfSRoy Marples {
2528d36e1dfSRoy Marples 
2538d36e1dfSRoy Marples 	return sa_is_unspecified(&rt->rt_dest) &&
2548d36e1dfSRoy Marples 	    sa_is_unspecified(&rt->rt_netmask);
2557827cba2SAaron LI }
2567827cba2SAaron LI 
2577827cba2SAaron LI static void
rt_desc(const char * cmd,const struct rt * rt)2587827cba2SAaron LI rt_desc(const char *cmd, const struct rt *rt)
2597827cba2SAaron LI {
2607827cba2SAaron LI 	char dest[INET_MAX_ADDRSTRLEN], gateway[INET_MAX_ADDRSTRLEN];
2617827cba2SAaron LI 	int prefix;
2627827cba2SAaron LI 	const char *ifname;
2637827cba2SAaron LI 	bool gateway_unspec;
2647827cba2SAaron LI 
2657827cba2SAaron LI 	assert(cmd != NULL);
2667827cba2SAaron LI 	assert(rt != NULL);
2677827cba2SAaron LI 
2687827cba2SAaron LI 	sa_addrtop(&rt->rt_dest, dest, sizeof(dest));
2697827cba2SAaron LI 	prefix = sa_toprefix(&rt->rt_netmask);
2707827cba2SAaron LI 	sa_addrtop(&rt->rt_gateway, gateway, sizeof(gateway));
2717827cba2SAaron LI 	gateway_unspec = sa_is_unspecified(&rt->rt_gateway);
2727827cba2SAaron LI 	ifname = rt->rt_ifp == NULL ? "(null)" : rt->rt_ifp->name;
2737827cba2SAaron LI 
2747827cba2SAaron LI 	if (rt->rt_flags & RTF_HOST) {
2757827cba2SAaron LI 		if (gateway_unspec)
2767827cba2SAaron LI 			loginfox("%s: %s host route to %s",
2777827cba2SAaron LI 			    ifname, cmd, dest);
2787827cba2SAaron LI 		else
2797827cba2SAaron LI 			loginfox("%s: %s host route to %s via %s",
2807827cba2SAaron LI 			    ifname, cmd, dest, gateway);
2818d36e1dfSRoy Marples 	} else if (rt_is_default(rt)) {
2827827cba2SAaron LI 		if (gateway_unspec)
2837827cba2SAaron LI 			loginfox("%s: %s default route",
2847827cba2SAaron LI 			    ifname, cmd);
2857827cba2SAaron LI 		else
2867827cba2SAaron LI 			loginfox("%s: %s default route via %s",
2877827cba2SAaron LI 			    ifname, cmd, gateway);
2887827cba2SAaron LI 	} else if (gateway_unspec)
2897827cba2SAaron LI 		loginfox("%s: %s%s route to %s/%d",
2907827cba2SAaron LI 		    ifname, cmd,
2917827cba2SAaron LI 		    rt->rt_flags & RTF_REJECT ? " reject" : "",
2927827cba2SAaron LI 		    dest, prefix);
2937827cba2SAaron LI 	else
2947827cba2SAaron LI 		loginfox("%s: %s%s route to %s/%d via %s",
2957827cba2SAaron LI 		    ifname, cmd,
2967827cba2SAaron LI 		    rt->rt_flags & RTF_REJECT ? " reject" : "",
2977827cba2SAaron LI 		    dest, prefix, gateway);
2987827cba2SAaron LI }
2997827cba2SAaron LI 
3007827cba2SAaron LI void
rt_headclear0(struct dhcpcd_ctx * ctx,rb_tree_t * rts,int af)3018d36e1dfSRoy Marples rt_headclear0(struct dhcpcd_ctx *ctx, rb_tree_t *rts, int af)
3027827cba2SAaron LI {
3037827cba2SAaron LI 	struct rt *rt, *rtn;
3047827cba2SAaron LI 
3057827cba2SAaron LI 	if (rts == NULL)
3067827cba2SAaron LI 		return;
3077827cba2SAaron LI 	assert(ctx != NULL);
3088d36e1dfSRoy Marples #ifdef RT_FREE_ROUTE_TABLE
3097827cba2SAaron LI 	assert(&ctx->froutes != rts);
3108d36e1dfSRoy Marples #endif
3117827cba2SAaron LI 
3128d36e1dfSRoy Marples 	RB_TREE_FOREACH_SAFE(rt, rts, rtn) {
3137827cba2SAaron LI 		if (af != AF_UNSPEC &&
3147827cba2SAaron LI 		    rt->rt_dest.sa_family != af &&
3157827cba2SAaron LI 		    rt->rt_gateway.sa_family != af)
3167827cba2SAaron LI 			continue;
3178d36e1dfSRoy Marples 		rb_tree_remove_node(rts, rt);
3188d36e1dfSRoy Marples 		rt_free(rt);
3197827cba2SAaron LI 	}
3207827cba2SAaron LI }
3217827cba2SAaron LI 
3227827cba2SAaron LI void
rt_headclear(rb_tree_t * rts,int af)3238d36e1dfSRoy Marples rt_headclear(rb_tree_t *rts, int af)
3247827cba2SAaron LI {
3257827cba2SAaron LI 	struct rt *rt;
3267827cba2SAaron LI 
3278d36e1dfSRoy Marples 	if (rts == NULL || (rt = RB_TREE_MIN(rts)) == NULL)
3287827cba2SAaron LI 		return;
3297827cba2SAaron LI 	rt_headclear0(rt->rt_ifp->ctx, rts, af);
3307827cba2SAaron LI }
3317827cba2SAaron LI 
3327827cba2SAaron LI static void
rt_headfree(rb_tree_t * rts)3338d36e1dfSRoy Marples rt_headfree(rb_tree_t *rts)
3347827cba2SAaron LI {
3357827cba2SAaron LI 	struct rt *rt;
3367827cba2SAaron LI 
3378d36e1dfSRoy Marples 	while ((rt = RB_TREE_MIN(rts)) != NULL) {
3388d36e1dfSRoy Marples 		rb_tree_remove_node(rts, rt);
3397827cba2SAaron LI 		free(rt);
3407827cba2SAaron LI 	}
3417827cba2SAaron LI }
3427827cba2SAaron LI 
3437827cba2SAaron LI void
rt_dispose(struct dhcpcd_ctx * ctx)3447827cba2SAaron LI rt_dispose(struct dhcpcd_ctx *ctx)
3457827cba2SAaron LI {
3467827cba2SAaron LI 
3477827cba2SAaron LI 	assert(ctx != NULL);
3487827cba2SAaron LI 	rt_headfree(&ctx->routes);
3498d36e1dfSRoy Marples #ifdef RT_FREE_ROUTE_TABLE
3507827cba2SAaron LI 	rt_headfree(&ctx->froutes);
3518d36e1dfSRoy Marples #ifdef RT_FREE_ROUTE_TABLE_STATS
3528d36e1dfSRoy Marples 	logdebugx("free route list used %zu times", froutes);
3538d36e1dfSRoy Marples 	logdebugx("new routes from route free list %zu", nroutes);
3548d36e1dfSRoy Marples 	logdebugx("maximum route free list size %zu", mroutes);
3558d36e1dfSRoy Marples #endif
3568d36e1dfSRoy Marples #endif
3577827cba2SAaron LI }
3587827cba2SAaron LI 
3597827cba2SAaron LI struct rt *
rt_new0(struct dhcpcd_ctx * ctx)3607827cba2SAaron LI rt_new0(struct dhcpcd_ctx *ctx)
3617827cba2SAaron LI {
3627827cba2SAaron LI 	struct rt *rt;
3637827cba2SAaron LI 
3647827cba2SAaron LI 	assert(ctx != NULL);
3658d36e1dfSRoy Marples #ifdef RT_FREE_ROUTE_TABLE
3668d36e1dfSRoy Marples 	if ((rt = RB_TREE_MIN(&ctx->froutes)) != NULL) {
3678d36e1dfSRoy Marples 		rb_tree_remove_node(&ctx->froutes, rt);
3688d36e1dfSRoy Marples #ifdef RT_FREE_ROUTE_TABLE_STATS
3698d36e1dfSRoy Marples 		croutes--;
3708d36e1dfSRoy Marples 		nroutes++;
3718d36e1dfSRoy Marples #endif
3728d36e1dfSRoy Marples 	} else
3738d36e1dfSRoy Marples #endif
3748d36e1dfSRoy Marples 	if ((rt = malloc(sizeof(*rt))) == NULL) {
3757827cba2SAaron LI 		logerr(__func__);
3767827cba2SAaron LI 		return NULL;
3777827cba2SAaron LI 	}
3787827cba2SAaron LI 	memset(rt, 0, sizeof(*rt));
3797827cba2SAaron LI 	return rt;
3807827cba2SAaron LI }
3817827cba2SAaron LI 
3827827cba2SAaron LI void
rt_setif(struct rt * rt,struct interface * ifp)3837827cba2SAaron LI rt_setif(struct rt *rt, struct interface *ifp)
3847827cba2SAaron LI {
3857827cba2SAaron LI 
3867827cba2SAaron LI 	assert(rt != NULL);
3877827cba2SAaron LI 	assert(ifp != NULL);
3887827cba2SAaron LI 	rt->rt_ifp = ifp;
3897827cba2SAaron LI #ifdef HAVE_ROUTE_METRIC
3907827cba2SAaron LI 	rt->rt_metric = ifp->metric;
3910aaf6155SRoy Marples 	if (if_roaming(ifp))
3920aaf6155SRoy Marples 		rt->rt_metric += RTMETRIC_ROAM;
3937827cba2SAaron LI #endif
3947827cba2SAaron LI }
3957827cba2SAaron LI 
3967827cba2SAaron LI struct rt *
rt_new(struct interface * ifp)3977827cba2SAaron LI rt_new(struct interface *ifp)
3987827cba2SAaron LI {
3997827cba2SAaron LI 	struct rt *rt;
4007827cba2SAaron LI 
4017827cba2SAaron LI 	assert(ifp != NULL);
4027827cba2SAaron LI 	if ((rt = rt_new0(ifp->ctx)) == NULL)
4037827cba2SAaron LI 		return NULL;
4047827cba2SAaron LI 	rt_setif(rt, ifp);
4057827cba2SAaron LI 	return rt;
4067827cba2SAaron LI }
4077827cba2SAaron LI 
4088d36e1dfSRoy Marples struct rt *
rt_proto_add_ctx(rb_tree_t * tree,struct rt * rt,struct dhcpcd_ctx * ctx)4098d36e1dfSRoy Marples rt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx)
4108d36e1dfSRoy Marples {
4118d36e1dfSRoy Marples 
4128d36e1dfSRoy Marples 	rt->rt_order = ctx->rt_order++;
4138d36e1dfSRoy Marples 	if (rb_tree_insert_node(tree, rt) == rt)
4148d36e1dfSRoy Marples 		return rt;
4158d36e1dfSRoy Marples 
4168d36e1dfSRoy Marples 	rt_free(rt);
4170a68f8d2SRoy Marples 	errno = EEXIST;
4188d36e1dfSRoy Marples 	return NULL;
4198d36e1dfSRoy Marples }
4208d36e1dfSRoy Marples 
4218d36e1dfSRoy Marples struct rt *
rt_proto_add(rb_tree_t * tree,struct rt * rt)4228d36e1dfSRoy Marples rt_proto_add(rb_tree_t *tree, struct rt *rt)
4238d36e1dfSRoy Marples {
4248d36e1dfSRoy Marples 
4258d36e1dfSRoy Marples 	assert (rt->rt_ifp != NULL);
4268d36e1dfSRoy Marples 	return rt_proto_add_ctx(tree, rt, rt->rt_ifp->ctx);
4278d36e1dfSRoy Marples }
4288d36e1dfSRoy Marples 
4297827cba2SAaron LI void
rt_free(struct rt * rt)4307827cba2SAaron LI rt_free(struct rt *rt)
4317827cba2SAaron LI {
4328d36e1dfSRoy Marples #ifdef RT_FREE_ROUTE_TABLE
4338d36e1dfSRoy Marples 	struct dhcpcd_ctx *ctx;
4347827cba2SAaron LI 
4357827cba2SAaron LI 	assert(rt != NULL);
4368d36e1dfSRoy Marples 	if (rt->rt_ifp == NULL) {
4378d36e1dfSRoy Marples 		free(rt);
4388d36e1dfSRoy Marples 		return;
4398d36e1dfSRoy Marples 	}
4408d36e1dfSRoy Marples 
4418d36e1dfSRoy Marples 	ctx = rt->rt_ifp->ctx;
4428d36e1dfSRoy Marples 	rb_tree_insert_node(&ctx->froutes, rt);
4438d36e1dfSRoy Marples #ifdef RT_FREE_ROUTE_TABLE_STATS
4448d36e1dfSRoy Marples 	croutes++;
4458d36e1dfSRoy Marples 	froutes++;
4468d36e1dfSRoy Marples 	if (croutes > mroutes)
4478d36e1dfSRoy Marples 		mroutes = croutes;
4488d36e1dfSRoy Marples #endif
4498d36e1dfSRoy Marples #else
4508d36e1dfSRoy Marples 	free(rt);
4518d36e1dfSRoy Marples #endif
4527827cba2SAaron LI }
4537827cba2SAaron LI 
4547827cba2SAaron LI void
rt_freeif(struct interface * ifp)4557827cba2SAaron LI rt_freeif(struct interface *ifp)
4567827cba2SAaron LI {
4577827cba2SAaron LI 	struct dhcpcd_ctx *ctx;
4587827cba2SAaron LI 	struct rt *rt, *rtn;
4597827cba2SAaron LI 
4607827cba2SAaron LI 	if (ifp == NULL)
4617827cba2SAaron LI 		return;
4627827cba2SAaron LI 	ctx = ifp->ctx;
4638d36e1dfSRoy Marples 	RB_TREE_FOREACH_SAFE(rt, &ctx->routes, rtn) {
4647827cba2SAaron LI 		if (rt->rt_ifp == ifp) {
4658d36e1dfSRoy Marples 			rb_tree_remove_node(&ctx->routes, rt);
4667827cba2SAaron LI 			rt_free(rt);
4677827cba2SAaron LI 		}
4687827cba2SAaron LI 	}
4697827cba2SAaron LI }
4707827cba2SAaron LI 
4717827cba2SAaron LI /* If something other than dhcpcd removes a route,
4727827cba2SAaron LI  * we need to remove it from our internal table. */
4737827cba2SAaron LI void
rt_recvrt(int cmd,const struct rt * rt,pid_t pid)4748d36e1dfSRoy Marples rt_recvrt(int cmd, const struct rt *rt, pid_t pid)
4757827cba2SAaron LI {
4767827cba2SAaron LI 	struct dhcpcd_ctx *ctx;
4777827cba2SAaron LI 	struct rt *f;
4787827cba2SAaron LI 
4797827cba2SAaron LI 	assert(rt != NULL);
4808d36e1dfSRoy Marples 	assert(rt->rt_ifp != NULL);
4818d36e1dfSRoy Marples 	assert(rt->rt_ifp->ctx != NULL);
4828d36e1dfSRoy Marples 
4837827cba2SAaron LI 	ctx = rt->rt_ifp->ctx;
4847827cba2SAaron LI 
4857827cba2SAaron LI 	switch(cmd) {
4867827cba2SAaron LI 	case RTM_DELETE:
4878d36e1dfSRoy Marples 		f = rb_tree_find_node(&ctx->routes, rt);
4887827cba2SAaron LI 		if (f != NULL) {
4898d36e1dfSRoy Marples 			char buf[32];
4908d36e1dfSRoy Marples 
4918d36e1dfSRoy Marples 			rb_tree_remove_node(&ctx->routes, f);
4928d36e1dfSRoy Marples 			snprintf(buf, sizeof(buf), "pid %d deleted", pid);
4938d36e1dfSRoy Marples 			rt_desc(buf, f);
4947827cba2SAaron LI 			rt_free(f);
4957827cba2SAaron LI 		}
4967827cba2SAaron LI 		break;
4977827cba2SAaron LI 	}
4987827cba2SAaron LI 
4998d36e1dfSRoy Marples #if defined(IPV4LL) && defined(HAVE_ROUTE_METRIC)
5007827cba2SAaron LI 	if (rt->rt_dest.sa_family == AF_INET)
5017827cba2SAaron LI 		ipv4ll_recvrt(cmd, rt);
5027827cba2SAaron LI #endif
5037827cba2SAaron LI }
5047827cba2SAaron LI 
5057827cba2SAaron LI static bool
rt_add(rb_tree_t * kroutes,struct rt * nrt,struct rt * ort)5068d36e1dfSRoy Marples rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
5077827cba2SAaron LI {
5087827cba2SAaron LI 	struct dhcpcd_ctx *ctx;
5098d36e1dfSRoy Marples 	bool change, kroute, result;
5107827cba2SAaron LI 
5117827cba2SAaron LI 	assert(nrt != NULL);
5127827cba2SAaron LI 	ctx = nrt->rt_ifp->ctx;
5137827cba2SAaron LI 
5147827cba2SAaron LI 	/*
5157827cba2SAaron LI 	 * Don't install a gateway if not asked to.
5167827cba2SAaron LI 	 * This option is mainly for VPN users who want their VPN to be the
5177827cba2SAaron LI 	 * default route.
5187827cba2SAaron LI 	 * Because VPN's generally don't care about route management
5197827cba2SAaron LI 	 * beyond their own, a longer term solution would be to remove this
5207827cba2SAaron LI 	 * and get the VPN to inject the default route into dhcpcd somehow.
5217827cba2SAaron LI 	 */
5227827cba2SAaron LI 	if (((nrt->rt_ifp->active &&
5237827cba2SAaron LI 	    !(nrt->rt_ifp->options->options & DHCPCD_GATEWAY)) ||
5247827cba2SAaron LI 	    (!nrt->rt_ifp->active && !(ctx->options & DHCPCD_GATEWAY))) &&
5257827cba2SAaron LI 	    sa_is_unspecified(&nrt->rt_dest) &&
5267827cba2SAaron LI 	    sa_is_unspecified(&nrt->rt_netmask))
5277827cba2SAaron LI 		return false;
5287827cba2SAaron LI 
5297827cba2SAaron LI 	rt_desc(ort == NULL ? "adding" : "changing", nrt);
5307827cba2SAaron LI 
5318d36e1dfSRoy Marples 	change = kroute = result = false;
5327827cba2SAaron LI 	if (ort == NULL) {
5338d36e1dfSRoy Marples 		ort = rb_tree_find_node(kroutes, nrt);
5347827cba2SAaron LI 		if (ort != NULL &&
5357827cba2SAaron LI 		    ((ort->rt_flags & RTF_REJECT &&
5367827cba2SAaron LI 		      nrt->rt_flags & RTF_REJECT) ||
5377827cba2SAaron LI 		     (ort->rt_ifp == nrt->rt_ifp &&
5387827cba2SAaron LI #ifdef HAVE_ROUTE_METRIC
5397827cba2SAaron LI 		    ort->rt_metric == nrt->rt_metric &&
5407827cba2SAaron LI #endif
5417827cba2SAaron LI 		    sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)))
5427827cba2SAaron LI 		{
5437827cba2SAaron LI 			if (ort->rt_mtu == nrt->rt_mtu)
5447827cba2SAaron LI 				return true;
5457827cba2SAaron LI 			change = true;
5468d36e1dfSRoy Marples 			kroute = true;
5477827cba2SAaron LI 		}
5487827cba2SAaron LI 	} else if (ort->rt_dflags & RTDF_FAKE &&
5497827cba2SAaron LI 	    !(nrt->rt_dflags & RTDF_FAKE) &&
5507827cba2SAaron LI 	    ort->rt_ifp == nrt->rt_ifp &&
5517827cba2SAaron LI #ifdef HAVE_ROUTE_METRIC
5527827cba2SAaron LI 	    ort->rt_metric == nrt->rt_metric &&
5537827cba2SAaron LI #endif
5547827cba2SAaron LI 	    sa_cmp(&ort->rt_dest, &nrt->rt_dest) == 0 &&
5557827cba2SAaron LI 	    rt_cmp_netmask(ort, nrt) == 0 &&
5567827cba2SAaron LI 	    sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)
5577827cba2SAaron LI 	{
5587827cba2SAaron LI 		if (ort->rt_mtu == nrt->rt_mtu)
5597827cba2SAaron LI 			return true;
5607827cba2SAaron LI 		change = true;
5617827cba2SAaron LI 	}
5627827cba2SAaron LI 
5637827cba2SAaron LI #ifdef RTF_CLONING
5647827cba2SAaron LI 	/* BSD can set routes to be cloning routes.
5657827cba2SAaron LI 	 * Cloned routes inherit the parent flags.
5667827cba2SAaron LI 	 * As such, we need to delete and re-add the route to flush children
5677827cba2SAaron LI 	 * to correct the flags. */
5687827cba2SAaron LI 	if (change && ort != NULL && ort->rt_flags & RTF_CLONING)
5697827cba2SAaron LI 		change = false;
5707827cba2SAaron LI #endif
5717827cba2SAaron LI 
5727827cba2SAaron LI 	if (change) {
5738d36e1dfSRoy Marples 		if (if_route(RTM_CHANGE, nrt) != -1) {
5748d36e1dfSRoy Marples 			result = true;
5758d36e1dfSRoy Marples 			goto out;
5768d36e1dfSRoy Marples 		}
5777827cba2SAaron LI 		if (errno != ESRCH)
5787827cba2SAaron LI 			logerr("if_route (CHG)");
5797827cba2SAaron LI 	}
5807827cba2SAaron LI 
5817827cba2SAaron LI #ifdef HAVE_ROUTE_METRIC
5827827cba2SAaron LI 	/* With route metrics, we can safely add the new route before
5837827cba2SAaron LI 	 * deleting the old route. */
5847827cba2SAaron LI 	if (if_route(RTM_ADD, nrt) != -1) {
5857827cba2SAaron LI 		if (ort != NULL) {
5867827cba2SAaron LI 			if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
5877827cba2SAaron LI 				logerr("if_route (DEL)");
5887827cba2SAaron LI 		}
5898d36e1dfSRoy Marples 		result = true;
5908d36e1dfSRoy Marples 		goto out;
5917827cba2SAaron LI 	}
5927827cba2SAaron LI 
5937827cba2SAaron LI 	/* If the kernel claims the route exists we need to rip out the
5947827cba2SAaron LI 	 * old one first. */
5957827cba2SAaron LI 	if (errno != EEXIST || ort == NULL)
5967827cba2SAaron LI 		goto logerr;
5977827cba2SAaron LI #endif
5987827cba2SAaron LI 
5997827cba2SAaron LI 	/* No route metrics, we need to delete the old route before
6007827cba2SAaron LI 	 * adding the new one. */
6017827cba2SAaron LI #ifdef ROUTE_PER_GATEWAY
6027827cba2SAaron LI 	errno = 0;
6037827cba2SAaron LI #endif
6047827cba2SAaron LI 	if (ort != NULL) {
6057827cba2SAaron LI 		if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
6067827cba2SAaron LI 			logerr("if_route (DEL)");
6077827cba2SAaron LI 		else
6088d36e1dfSRoy Marples 			kroute = false;
6097827cba2SAaron LI 	}
6107827cba2SAaron LI #ifdef ROUTE_PER_GATEWAY
6117827cba2SAaron LI 	/* The OS allows many routes to the same dest with different gateways.
6127827cba2SAaron LI 	 * dhcpcd does not support this yet, so for the time being just keep on
6137827cba2SAaron LI 	 * deleting the route until there is an error. */
6147827cba2SAaron LI 	if (ort != NULL && errno == 0) {
6157827cba2SAaron LI 		for (;;) {
6167827cba2SAaron LI 			if (if_route(RTM_DELETE, ort) == -1)
6177827cba2SAaron LI 				break;
6187827cba2SAaron LI 		}
6197827cba2SAaron LI 	}
6207827cba2SAaron LI #endif
6218d36e1dfSRoy Marples 
6221b3b16a2SRoy Marples 	/* Shouldn't need to check for EEXIST, but some kernels don't
6231b3b16a2SRoy Marples 	 * dump the subnet route just after we added the address. */
6241b3b16a2SRoy Marples 	if (if_route(RTM_ADD, nrt) != -1 || errno == EEXIST) {
6258d36e1dfSRoy Marples 		result = true;
6268d36e1dfSRoy Marples 		goto out;
6278d36e1dfSRoy Marples 	}
6288d36e1dfSRoy Marples 
6297827cba2SAaron LI #ifdef HAVE_ROUTE_METRIC
6307827cba2SAaron LI logerr:
6317827cba2SAaron LI #endif
6327827cba2SAaron LI 	logerr("if_route (ADD)");
6338d36e1dfSRoy Marples 
6348d36e1dfSRoy Marples out:
6358d36e1dfSRoy Marples 	if (kroute) {
6368d36e1dfSRoy Marples 		rb_tree_remove_node(kroutes, ort);
6378d36e1dfSRoy Marples 		rt_free(ort);
6388d36e1dfSRoy Marples 	}
6398d36e1dfSRoy Marples 	return result;
6407827cba2SAaron LI }
6417827cba2SAaron LI 
6427827cba2SAaron LI static bool
rt_delete(struct rt * rt)6437827cba2SAaron LI rt_delete(struct rt *rt)
6447827cba2SAaron LI {
6457827cba2SAaron LI 	int retval;
6467827cba2SAaron LI 
6477827cba2SAaron LI 	rt_desc("deleting", rt);
6487827cba2SAaron LI 	retval = if_route(RTM_DELETE, rt) == -1 ? false : true;
6497827cba2SAaron LI 	if (!retval && errno != ENOENT && errno != ESRCH)
6507827cba2SAaron LI 		logerr(__func__);
6517827cba2SAaron LI 	return retval;
6527827cba2SAaron LI }
6537827cba2SAaron LI 
6547827cba2SAaron LI static bool
rt_cmp(const struct rt * r1,const struct rt * r2)6557827cba2SAaron LI rt_cmp(const struct rt *r1, const struct rt *r2)
6567827cba2SAaron LI {
6577827cba2SAaron LI 
6587827cba2SAaron LI 	return (r1->rt_ifp == r2->rt_ifp &&
6597827cba2SAaron LI #ifdef HAVE_ROUTE_METRIC
6607827cba2SAaron LI 	    r1->rt_metric == r2->rt_metric &&
6617827cba2SAaron LI #endif
6627827cba2SAaron LI 	    sa_cmp(&r1->rt_gateway, &r2->rt_gateway) == 0);
6637827cba2SAaron LI }
6647827cba2SAaron LI 
6657827cba2SAaron LI static bool
rt_doroute(rb_tree_t * kroutes,struct rt * rt)6668d36e1dfSRoy Marples rt_doroute(rb_tree_t *kroutes, struct rt *rt)
6677827cba2SAaron LI {
6687827cba2SAaron LI 	struct dhcpcd_ctx *ctx;
6697827cba2SAaron LI 	struct rt *or;
6707827cba2SAaron LI 
6717827cba2SAaron LI 	ctx = rt->rt_ifp->ctx;
6727827cba2SAaron LI 	/* Do we already manage it? */
6738d36e1dfSRoy Marples 	or = rb_tree_find_node(&ctx->routes, rt);
6748d36e1dfSRoy Marples 	if (or != NULL) {
6757827cba2SAaron LI 		if (rt->rt_dflags & RTDF_FAKE)
6767827cba2SAaron LI 			return true;
6777827cba2SAaron LI 		if (or->rt_dflags & RTDF_FAKE ||
6787827cba2SAaron LI 		    !rt_cmp(rt, or) ||
6797827cba2SAaron LI 		    (rt->rt_ifa.sa_family != AF_UNSPEC &&
6807827cba2SAaron LI 		    sa_cmp(&or->rt_ifa, &rt->rt_ifa) != 0) ||
6817827cba2SAaron LI 		    or->rt_mtu != rt->rt_mtu)
6827827cba2SAaron LI 		{
6838d36e1dfSRoy Marples 			if (!rt_add(kroutes, rt, or))
6847827cba2SAaron LI 				return false;
6857827cba2SAaron LI 		}
6868d36e1dfSRoy Marples 		rb_tree_remove_node(&ctx->routes, or);
6877827cba2SAaron LI 		rt_free(or);
6887827cba2SAaron LI 	} else {
6897827cba2SAaron LI 		if (rt->rt_dflags & RTDF_FAKE) {
6908d36e1dfSRoy Marples 			or = rb_tree_find_node(kroutes, rt);
6918d36e1dfSRoy Marples 			if (or == NULL)
6927827cba2SAaron LI 				return false;
6937827cba2SAaron LI 			if (!rt_cmp(rt, or))
6947827cba2SAaron LI 				return false;
6957827cba2SAaron LI 		} else {
6968d36e1dfSRoy Marples 			if (!rt_add(kroutes, rt, NULL))
6977827cba2SAaron LI 				return false;
6987827cba2SAaron LI 		}
6997827cba2SAaron LI 	}
7007827cba2SAaron LI 
7017827cba2SAaron LI 	return true;
7027827cba2SAaron LI }
7037827cba2SAaron LI 
7047827cba2SAaron LI void
rt_build(struct dhcpcd_ctx * ctx,int af)7057827cba2SAaron LI rt_build(struct dhcpcd_ctx *ctx, int af)
7067827cba2SAaron LI {
7078d36e1dfSRoy Marples 	rb_tree_t routes, added, kroutes;
7087827cba2SAaron LI 	struct rt *rt, *rtn;
7097827cba2SAaron LI 	unsigned long long o;
7107827cba2SAaron LI 
7118d36e1dfSRoy Marples 	rb_tree_init(&routes, &rt_compare_proto_ops);
7128d36e1dfSRoy Marples 	rb_tree_init(&added, &rt_compare_os_ops);
7138d36e1dfSRoy Marples 	rb_tree_init(&kroutes, &rt_compare_os_ops);
714*80aa9461SRoy Marples 	if (if_initrt(ctx, &kroutes, af) != 0)
715*80aa9461SRoy Marples 		logerr("%s: if_initrt", __func__);
7168d36e1dfSRoy Marples 	ctx->rt_order = 0;
7178d36e1dfSRoy Marples 	ctx->options |= DHCPCD_RTBUILD;
7187827cba2SAaron LI 
7197827cba2SAaron LI #ifdef INET
7207827cba2SAaron LI 	if (!inet_getroutes(ctx, &routes))
7217827cba2SAaron LI 		goto getfail;
7227827cba2SAaron LI #endif
7237827cba2SAaron LI #ifdef INET6
7247827cba2SAaron LI 	if (!inet6_getroutes(ctx, &routes))
7257827cba2SAaron LI 		goto getfail;
7267827cba2SAaron LI #endif
7276e63cc1fSRoy Marples 
7286e63cc1fSRoy Marples #ifdef BSD
7296e63cc1fSRoy Marples 	/* Rewind the miss filter */
7306e63cc1fSRoy Marples 	ctx->rt_missfilterlen = 0;
7316e63cc1fSRoy Marples #endif
7327827cba2SAaron LI 
7338d36e1dfSRoy Marples 	RB_TREE_FOREACH_SAFE(rt, &routes, rtn) {
73420329f2aSRoy Marples 		if (rt->rt_ifp->active) {
735b2927f2bSRoy Marples 			if (!(rt->rt_ifp->options->options & DHCPCD_CONFIGURE))
736b2927f2bSRoy Marples 				continue;
73720329f2aSRoy Marples 		} else if (!(ctx->options & DHCPCD_CONFIGURE))
73820329f2aSRoy Marples 			continue;
7396e63cc1fSRoy Marples #ifdef BSD
7406e63cc1fSRoy Marples 		if (rt_is_default(rt) &&
7416e63cc1fSRoy Marples 		    if_missfilter(rt->rt_ifp, &rt->rt_gateway) == -1)
7426e63cc1fSRoy Marples 			logerr("if_missfilter");
7436e63cc1fSRoy Marples #endif
7448d36e1dfSRoy Marples 		if ((rt->rt_dest.sa_family != af &&
7458d36e1dfSRoy Marples 		    rt->rt_dest.sa_family != AF_UNSPEC) ||
7468d36e1dfSRoy Marples 		    (rt->rt_gateway.sa_family != af &&
7478d36e1dfSRoy Marples 		    rt->rt_gateway.sa_family != AF_UNSPEC))
7487827cba2SAaron LI 			continue;
7497827cba2SAaron LI 		/* Is this route already in our table? */
7508d36e1dfSRoy Marples 		if (rb_tree_find_node(&added, rt) != NULL)
7517827cba2SAaron LI 			continue;
7528d36e1dfSRoy Marples 		if (rt_doroute(&kroutes, rt)) {
7538d36e1dfSRoy Marples 			rb_tree_remove_node(&routes, rt);
7548d36e1dfSRoy Marples 			if (rb_tree_insert_node(&added, rt) != rt) {
7558d36e1dfSRoy Marples 				errno = EEXIST;
7568d36e1dfSRoy Marples 				logerr(__func__);
7578d36e1dfSRoy Marples 				rt_free(rt);
7588d36e1dfSRoy Marples 			}
7597827cba2SAaron LI 		}
7607827cba2SAaron LI 	}
7617827cba2SAaron LI 
7626e63cc1fSRoy Marples #ifdef BSD
7636e63cc1fSRoy Marples 	if (if_missfilter_apply(ctx) == -1 && errno != ENOTSUP)
7646e63cc1fSRoy Marples 		logerr("if_missfilter_apply");
7656e63cc1fSRoy Marples #endif
7666e63cc1fSRoy Marples 
7677827cba2SAaron LI 	/* Remove old routes we used to manage. */
7688d36e1dfSRoy Marples 	RB_TREE_FOREACH_REVERSE_SAFE(rt, &ctx->routes, rtn) {
7698d36e1dfSRoy Marples 		if ((rt->rt_dest.sa_family != af &&
7708d36e1dfSRoy Marples 		    rt->rt_dest.sa_family != AF_UNSPEC) ||
7718d36e1dfSRoy Marples 		    (rt->rt_gateway.sa_family != af &&
7728d36e1dfSRoy Marples 		    rt->rt_gateway.sa_family != AF_UNSPEC))
7737827cba2SAaron LI 			continue;
7748d36e1dfSRoy Marples 		rb_tree_remove_node(&ctx->routes, rt);
7758d36e1dfSRoy Marples 		if (rb_tree_find_node(&added, rt) == NULL) {
7767827cba2SAaron LI 			o = rt->rt_ifp->options ?
7777827cba2SAaron LI 			    rt->rt_ifp->options->options :
7787827cba2SAaron LI 			    ctx->options;
7797827cba2SAaron LI 			if ((o &
7807827cba2SAaron LI 				(DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
7817827cba2SAaron LI 				(DHCPCD_EXITING | DHCPCD_PERSISTENT))
7827827cba2SAaron LI 				rt_delete(rt);
7837827cba2SAaron LI 		}
7848d36e1dfSRoy Marples 		rt_free(rt);
7857827cba2SAaron LI 	}
7867827cba2SAaron LI 
7878d36e1dfSRoy Marples 	/* XXX This needs to be optimised. */
7888d36e1dfSRoy Marples 	while ((rt = RB_TREE_MIN(&added)) != NULL) {
7898d36e1dfSRoy Marples 		rb_tree_remove_node(&added, rt);
7908d36e1dfSRoy Marples 		if (rb_tree_insert_node(&ctx->routes, rt) != rt) {
7918d36e1dfSRoy Marples 			errno = EEXIST;
7928d36e1dfSRoy Marples 			logerr(__func__);
7938d36e1dfSRoy Marples 			rt_free(rt);
7948d36e1dfSRoy Marples 		}
7958d36e1dfSRoy Marples 	}
7968d36e1dfSRoy Marples 
7977827cba2SAaron LI getfail:
7987827cba2SAaron LI 	rt_headclear(&routes, AF_UNSPEC);
7998d36e1dfSRoy Marples 	rt_headclear(&kroutes, AF_UNSPEC);
8007827cba2SAaron LI }
801