Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * dhcpcd - route management
      3  * SPDX-License-Identifier: BSD-2-Clause
      4  * Copyright (c) 2006-2025 Roy Marples <roy (at) 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 <syslog.h>
     37 #include <unistd.h>
     38 
     39 #include "config.h" // IWYU pragma: keep
     40 #include "common.h"
     41 #include "dhcpcd.h"
     42 #include "if-options.h"
     43 #include "if.h"
     44 #include "ipv4.h"
     45 #include "ipv4ll.h"
     46 #include "ipv6.h"
     47 #include "logerr.h"
     48 #include "route.h"
     49 #include "sa.h"
     50 
     51 /* Needed for NetBSD-6, 7 and 8. */
     52 #ifndef RB_TREE_FOREACH_SAFE
     53 #ifndef RB_TREE_PREV
     54 #define RB_TREE_NEXT(T, N) rb_tree_iterate((T), (N), RB_DIR_RIGHT)
     55 #define RB_TREE_PREV(T, N) rb_tree_iterate((T), (N), RB_DIR_LEFT)
     56 #endif
     57 #define RB_TREE_FOREACH_SAFE(N, T, S)                                        \
     58 	for ((N) = RB_TREE_MIN(T); (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); (N) && ((S) = RB_TREE_PREV((T), (N)), 1); \
     62 	    (N) = (S))
     63 #endif
     64 
     65 /*
     66  * For our purposes, RTF_CONNECTED is the same as RTF_CLONING.
     67  * If we change the route, we want to flush anything dynamically created.
     68  */
     69 #if defined(BSD) && !defined(RTF_CLONING) && defined(RTF_CONNECTED)
     70 #define RTF_CLONING RTF_CONNECTED
     71 #endif
     72 
     73 #ifdef RT_FREE_ROUTE_TABLE_STATS
     74 static size_t croutes;
     75 static size_t nroutes;
     76 static size_t froutes;
     77 static size_t mroutes;
     78 #endif
     79 
     80 static void
     81 rt_maskedaddr(struct sockaddr *dst, const struct sockaddr *addr,
     82     const struct sockaddr *netmask)
     83 {
     84 	const char *addrp = addr->sa_data, *netmaskp = netmask->sa_data;
     85 	char *dstp = dst->sa_data;
     86 	const char *addre = (char *)dst + sa_len(addr);
     87 	const char *netmaske = (char *)dst + MIN(sa_len(addr), sa_len(netmask));
     88 
     89 	dst->sa_family = addr->sa_family;
     90 #ifdef HAVE_SA_LEN
     91 	dst->sa_len = addr->sa_len;
     92 #endif
     93 
     94 	if (sa_is_unspecified(netmask)) {
     95 		if (addre > dstp)
     96 			memcpy(dstp, addrp, (size_t)(addre - dstp));
     97 		return;
     98 	}
     99 
    100 	while (dstp < netmaske)
    101 		*dstp++ = *addrp++ & *netmaskp++;
    102 	if (dstp < addre)
    103 		memset(dstp, 0, (size_t)(addre - dstp));
    104 }
    105 
    106 /*
    107  * On some systems, host routes have no need for a netmask.
    108  * However DHCP specifies host routes using an all-ones netmask.
    109  * This handy function allows easy comparison when the two
    110  * differ.
    111  */
    112 static int
    113 rt_cmp_netmask(const struct rt *rt1, const struct rt *rt2)
    114 {
    115 	if (rt1->rt_flags & RTF_HOST && rt2->rt_flags & RTF_HOST)
    116 		return 0;
    117 	return sa_cmp(rt1->rt_netmask, rt2->rt_netmask);
    118 }
    119 
    120 int
    121 rt_cmp_dest(const struct rt *rt1, const struct rt *rt2)
    122 {
    123 	struct sockaddr_storage ss1 = { .ss_family = AF_UNSPEC };
    124 	struct sockaddr_storage ss2 = { .ss_family = AF_UNSPEC };
    125 	struct sockaddr *ma1 = (struct sockaddr *)&ss1;
    126 	struct sockaddr *ma2 = (struct sockaddr *)&ss2;
    127 	int c;
    128 
    129 	rt_maskedaddr(ma1, rt1->rt_dest, rt1->rt_netmask);
    130 	rt_maskedaddr(ma2, rt2->rt_dest, rt2->rt_netmask);
    131 	c = sa_cmp(ma1, ma2);
    132 	if (c != 0)
    133 		return c;
    134 
    135 	return rt_cmp_netmask(rt1, rt2);
    136 }
    137 
    138 static int
    139 rt_compare_os(__unused void *context, const void *node1, const void *node2)
    140 {
    141 	const struct rt *rt1 = node1, *rt2 = node2;
    142 	int c;
    143 
    144 	/* Sort by masked destination. */
    145 	c = rt_cmp_dest(rt1, rt2);
    146 	if (c != 0)
    147 		return c;
    148 
    149 #ifdef HAVE_ROUTE_METRIC
    150 	c = (int)(rt1->rt_ifp->metric - rt2->rt_ifp->metric);
    151 #endif
    152 	return c;
    153 }
    154 
    155 static int
    156 rt_compare_list(__unused void *context, const void *node1, const void *node2)
    157 {
    158 	const struct rt *rt1 = node1, *rt2 = node2;
    159 
    160 	if (rt1->rt_order > rt2->rt_order)
    161 		return 1;
    162 	if (rt1->rt_order < rt2->rt_order)
    163 		return -1;
    164 	return 0;
    165 }
    166 
    167 static int
    168 rt_compare_proto(void *context, const void *node1, const void *node2)
    169 {
    170 	const struct rt *rt1 = node1, *rt2 = node2;
    171 	int c;
    172 	struct interface *ifp1, *ifp2;
    173 
    174 	assert(rt1->rt_ifp != NULL);
    175 	assert(rt2->rt_ifp != NULL);
    176 	ifp1 = rt1->rt_ifp;
    177 	ifp2 = rt2->rt_ifp;
    178 
    179 	/* Prefer interfaces with a carrier. */
    180 	c = ifp1->carrier - ifp2->carrier;
    181 	if (c != 0)
    182 		return -c;
    183 
    184 	/* Prefer roaming over non roaming if both carriers are down. */
    185 	if (ifp1->carrier == LINK_DOWN && ifp2->carrier == LINK_DOWN) {
    186 		bool roam1 = if_roaming(ifp1);
    187 		bool roam2 = if_roaming(ifp2);
    188 
    189 		if (roam1 != roam2)
    190 			return roam1 ? 1 : -1;
    191 	}
    192 
    193 #ifdef INET
    194 	/* IPv4LL routes always come last */
    195 	if (rt1->rt_dflags & RTDF_IPV4LL && !(rt2->rt_dflags & RTDF_IPV4LL))
    196 		return -1;
    197 	else if (!(rt1->rt_dflags & RTDF_IPV4LL) &&
    198 	    rt2->rt_dflags & RTDF_IPV4LL)
    199 		return 1;
    200 #endif
    201 
    202 	/* Lower metric interfaces come first. */
    203 	c = (int)(ifp1->metric - ifp2->metric);
    204 	if (c != 0)
    205 		return c;
    206 
    207 	/* Finally the order in which the route was given to us. */
    208 	return rt_compare_list(context, rt1, rt2);
    209 }
    210 
    211 static const rb_tree_ops_t rt_compare_os_ops = {
    212 	.rbto_compare_nodes = rt_compare_os,
    213 	.rbto_compare_key = rt_compare_os,
    214 	.rbto_node_offset = offsetof(struct rt, rt_tree),
    215 	.rbto_context = NULL
    216 };
    217 
    218 const rb_tree_ops_t rt_compare_list_ops = {
    219 	.rbto_compare_nodes = rt_compare_list,
    220 	.rbto_compare_key = rt_compare_list,
    221 	.rbto_node_offset = offsetof(struct rt, rt_tree),
    222 	.rbto_context = NULL
    223 };
    224 
    225 const rb_tree_ops_t rt_compare_proto_ops = {
    226 	.rbto_compare_nodes = rt_compare_proto,
    227 	.rbto_compare_key = rt_compare_proto,
    228 	.rbto_node_offset = offsetof(struct rt, rt_tree),
    229 	.rbto_context = NULL
    230 };
    231 
    232 #ifdef RT_FREE_ROUTE_TABLE
    233 static int
    234 rt_compare_free(__unused void *context, const void *node1, const void *node2)
    235 {
    236 	return node1 == node2 ? 0 : node1 < node2 ? -1 : 1;
    237 }
    238 
    239 static const rb_tree_ops_t rt_compare_free_ops = {
    240 	.rbto_compare_nodes = rt_compare_free,
    241 	.rbto_compare_key = rt_compare_free,
    242 	.rbto_node_offset = offsetof(struct rt, rt_tree),
    243 	.rbto_context = NULL
    244 };
    245 #endif
    246 
    247 void
    248 rt_init_routes(struct dhcpcd_ctx *ctx)
    249 {
    250 	rb_tree_init(&ctx->routes, &rt_compare_os_ops);
    251 #ifdef RT_FREE_ROUTE_TABLE
    252 	rb_tree_init(&ctx->froutes, &rt_compare_free_ops);
    253 #endif
    254 }
    255 
    256 bool
    257 rt_is_default(const struct rt *rt)
    258 {
    259 	return sa_is_unspecified(rt->rt_dest) &&
    260 	    sa_is_unspecified(rt->rt_netmask);
    261 }
    262 
    263 static void
    264 rt_desc(int loglevel, const char *cmd, const struct rt *rt)
    265 {
    266 	char dest[INET_MAX_ADDRSTRLEN], gateway[INET_MAX_ADDRSTRLEN];
    267 	int prefix;
    268 	const char *ifname;
    269 	bool gateway_unspec;
    270 
    271 	assert(cmd != NULL);
    272 	assert(rt != NULL);
    273 
    274 	sa_addrtop(rt->rt_dest, dest, sizeof(dest));
    275 	prefix = sa_toprefix(rt->rt_netmask);
    276 	sa_addrtop(rt->rt_gateway, gateway, sizeof(gateway));
    277 	gateway_unspec = sa_is_unspecified(rt->rt_gateway);
    278 	ifname = rt->rt_ifp == NULL ? "(null)" : rt->rt_ifp->name;
    279 
    280 	if (rt->rt_flags & RTF_HOST) {
    281 		if (gateway_unspec)
    282 			logmessage(loglevel, "%s: %s host route to %s", ifname,
    283 			    cmd, dest);
    284 		else
    285 			logmessage(loglevel, "%s: %s host route to %s via %s",
    286 			    ifname, cmd, dest, gateway);
    287 	} else if (rt_is_default(rt)) {
    288 		if (gateway_unspec)
    289 			logmessage(loglevel, "%s: %s default route", ifname,
    290 			    cmd);
    291 		else
    292 			logmessage(loglevel, "%s: %s default route via %s",
    293 			    ifname, cmd, gateway);
    294 	} else if (gateway_unspec)
    295 		logmessage(loglevel, "%s: %s%s route to %s/%d", ifname, cmd,
    296 		    rt->rt_flags & RTF_REJECT ? " reject" : "", dest, prefix);
    297 	else
    298 		logmessage(loglevel, "%s: %s%s route to %s/%d via %s", ifname,
    299 		    cmd, rt->rt_flags & RTF_REJECT ? " reject" : "", dest,
    300 		    prefix, gateway);
    301 }
    302 
    303 void
    304 rt_headclear0(struct dhcpcd_ctx *ctx, rb_tree_t *rts, int af)
    305 {
    306 	struct rt *rt, *rtn;
    307 
    308 	if (rts == NULL)
    309 		return;
    310 #ifdef RT_FREE_ROUTE_TABLE
    311 	if (ctx != NULL)
    312 		assert(&ctx->froutes != rts);
    313 #endif
    314 
    315 	RB_TREE_FOREACH_SAFE(rt, rts, rtn)
    316 	{
    317 		if (af != AF_UNSPEC && rt->rt_dest->sa_family != af &&
    318 		    rt->rt_gateway->sa_family != af)
    319 			continue;
    320 		rb_tree_remove_node(rts, rt);
    321 		rt_free(rt);
    322 	}
    323 }
    324 
    325 void
    326 rt_headclear(rb_tree_t *rts, int af)
    327 {
    328 	struct rt *rt;
    329 
    330 	if (rts == NULL || (rt = RB_TREE_MIN(rts)) == NULL)
    331 		return;
    332 	rt_headclear0(rt->rt_ifp ? rt->rt_ifp->ctx : NULL, rts, af);
    333 }
    334 
    335 static void
    336 rt_headfree(rb_tree_t *rts)
    337 {
    338 	struct rt *rt;
    339 
    340 	while ((rt = RB_TREE_MIN(rts)) != NULL) {
    341 		rb_tree_remove_node(rts, rt);
    342 		free(rt);
    343 	}
    344 }
    345 
    346 void
    347 rt_dispose(struct dhcpcd_ctx *ctx)
    348 {
    349 	assert(ctx != NULL);
    350 	rt_headfree(&ctx->routes);
    351 #ifdef RT_FREE_ROUTE_TABLE
    352 	rt_headfree(&ctx->froutes);
    353 #ifdef RT_FREE_ROUTE_TABLE_STATS
    354 	logdebugx("free route list used %zu times", froutes);
    355 	logdebugx("new routes from route free list %zu", nroutes);
    356 	logdebugx("maximum route free list size %zu", mroutes);
    357 #endif
    358 #endif
    359 }
    360 
    361 static void
    362 rt_setup_sa(struct rt *rt)
    363 {
    364 	rt->rt_dest = (struct sockaddr *)&rt->rt_ss_dest;
    365 	rt->rt_netmask = (struct sockaddr *)&rt->rt_ss_netmask;
    366 	rt->rt_gateway = (struct sockaddr *)&rt->rt_ss_gateway;
    367 	rt->rt_ifa = (struct sockaddr *)&rt->rt_ss_ifa;
    368 }
    369 
    370 void
    371 rt_init(struct rt *rt)
    372 {
    373 	memset(rt, 0, sizeof(*rt));
    374 	rt_setup_sa(rt);
    375 }
    376 
    377 void
    378 rt_copy(struct rt *dst, const struct rt *src)
    379 {
    380 	memcpy(dst, src, sizeof(*dst));
    381 	rt_setup_sa(dst);
    382 }
    383 
    384 struct rt *
    385 rt_new0(struct dhcpcd_ctx *ctx)
    386 {
    387 	struct rt *rt;
    388 
    389 	assert(ctx != NULL);
    390 #ifdef RT_FREE_ROUTE_TABLE
    391 	if ((rt = RB_TREE_MIN(&ctx->froutes)) != NULL) {
    392 		rb_tree_remove_node(&ctx->froutes, rt);
    393 #ifdef RT_FREE_ROUTE_TABLE_STATS
    394 		croutes--;
    395 		nroutes++;
    396 #endif
    397 	} else
    398 #endif
    399 	    if ((rt = malloc(sizeof(*rt))) == NULL) {
    400 		logerr(__func__);
    401 		return NULL;
    402 	}
    403 	rt_init(rt);
    404 	return rt;
    405 }
    406 
    407 void
    408 rt_setif(struct rt *rt, struct interface *ifp)
    409 {
    410 	assert(rt != NULL);
    411 	assert(ifp != NULL);
    412 	rt->rt_ifp = ifp;
    413 #ifdef HAVE_ROUTE_METRIC
    414 	rt->rt_metric = ifp->metric;
    415 	if (if_roaming(ifp))
    416 		rt->rt_metric += RTMETRIC_ROAM;
    417 #endif
    418 }
    419 
    420 struct rt *
    421 rt_new(struct interface *ifp)
    422 {
    423 	struct rt *rt;
    424 
    425 	assert(ifp != NULL);
    426 	if ((rt = rt_new0(ifp->ctx)) == NULL)
    427 		return NULL;
    428 	rt_setif(rt, ifp);
    429 	return rt;
    430 }
    431 
    432 struct rt *
    433 rt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx)
    434 {
    435 	rt->rt_order = ctx->rt_order++;
    436 	if (rb_tree_insert_node(tree, rt) == rt)
    437 		return rt;
    438 
    439 	rt_free(rt);
    440 	errno = EEXIST;
    441 	return NULL;
    442 }
    443 
    444 struct rt *
    445 rt_proto_add(rb_tree_t *tree, struct rt *rt)
    446 {
    447 	assert(rt->rt_ifp != NULL);
    448 	return rt_proto_add_ctx(tree, rt, rt->rt_ifp->ctx);
    449 }
    450 
    451 void
    452 rt_free(struct rt *rt)
    453 {
    454 #ifdef RT_FREE_ROUTE_TABLE
    455 	struct dhcpcd_ctx *ctx;
    456 
    457 	assert(rt != NULL);
    458 	if (rt->rt_ifp == NULL) {
    459 		free(rt);
    460 		return;
    461 	}
    462 
    463 	ctx = rt->rt_ifp->ctx;
    464 	rb_tree_insert_node(&ctx->froutes, rt);
    465 #ifdef RT_FREE_ROUTE_TABLE_STATS
    466 	croutes++;
    467 	froutes++;
    468 	if (croutes > mroutes)
    469 		mroutes = croutes;
    470 #endif
    471 #else
    472 	free(rt);
    473 #endif
    474 }
    475 
    476 void
    477 rt_freeif(struct interface *ifp)
    478 {
    479 	struct dhcpcd_ctx *ctx;
    480 	struct rt *rt, *rtn;
    481 
    482 	if (ifp == NULL)
    483 		return;
    484 	ctx = ifp->ctx;
    485 	RB_TREE_FOREACH_SAFE(rt, &ctx->routes, rtn)
    486 	{
    487 		if (rt->rt_ifp == ifp) {
    488 			rb_tree_remove_node(&ctx->routes, rt);
    489 			rt_free(rt);
    490 		}
    491 	}
    492 }
    493 
    494 /* If something other than dhcpcd removes a route,
    495  * we need to remove it from our internal table. */
    496 void
    497 rt_recvrt(int cmd, const struct rt *rt, pid_t pid)
    498 {
    499 	struct dhcpcd_ctx *ctx;
    500 	struct rt *f;
    501 
    502 	assert(rt != NULL);
    503 	assert(rt->rt_ifp != NULL);
    504 	assert(rt->rt_ifp->ctx != NULL);
    505 
    506 	ctx = rt->rt_ifp->ctx;
    507 
    508 	switch (cmd) {
    509 	case RTM_DELETE:
    510 		f = rb_tree_find_node(&ctx->routes, rt);
    511 		if (f != NULL) {
    512 			char buf[32];
    513 
    514 			rb_tree_remove_node(&ctx->routes, f);
    515 			snprintf(buf, sizeof(buf), "pid %d deleted", (int)pid);
    516 			rt_desc(LOG_WARNING, buf, f);
    517 			rt_free(f);
    518 		}
    519 		break;
    520 	}
    521 
    522 #if defined(IPV4LL) && defined(HAVE_ROUTE_METRIC)
    523 	if (rt->rt_dest->sa_family == AF_INET)
    524 		ipv4ll_recvrt(cmd, rt);
    525 #endif
    526 }
    527 
    528 /* Compare miscellaneous route details */
    529 static int
    530 rt_cmp_mtu(struct rt *nrt, struct rt *ort)
    531 {
    532 #if defined(__FreeBSD__) || defined(__DragonFly__)
    533 	/* FreeBSD puts the interface MTU into the route MTU
    534 	 * if the route does not define it's own. */
    535 	unsigned int nmtu, omtu;
    536 
    537 	nmtu = nrt->rt_mtu ? nrt->rt_mtu : (unsigned int)nrt->rt_ifp->mtu;
    538 	omtu = ort->rt_mtu ? ort->rt_mtu : (unsigned int)ort->rt_ifp->mtu;
    539 	if (omtu != nmtu)
    540 		return 1;
    541 #else
    542 	if (ort->rt_mtu != nrt->rt_mtu)
    543 		return 1;
    544 #endif
    545 
    546 	return 0;
    547 }
    548 
    549 #ifdef HAVE_ROUTE_LIFETIME
    550 static int
    551 rt_cmp_lifetime(struct rt *nrt, struct rt *ort)
    552 {
    553 	/* There might be a minor difference between kernel route
    554 	 * lifetime and our lifetime due to processing times.
    555 	 * We allow a small deviation to avoid needless route changes.
    556 	 * dhcpcd will expire the route regardless of route lifetime support.
    557 	 */
    558 	struct timespec ts;
    559 	uint32_t deviation;
    560 
    561 	timespecsub(&nrt->rt_acquired, &ort->rt_acquired, &ts);
    562 	if (ts.tv_sec < 0)
    563 		ts.tv_sec = -ts.tv_sec;
    564 	if (ts.tv_sec > RTLIFETIME_DEV_MAX)
    565 		return 1;
    566 	if (nrt->rt_lifetime > ort->rt_lifetime)
    567 		deviation = nrt->rt_lifetime - ort->rt_lifetime;
    568 	else
    569 		deviation = ort->rt_lifetime - nrt->rt_lifetime;
    570 	if (deviation > RTLIFETIME_DEV_MAX)
    571 		return 1;
    572 
    573 	return 0;
    574 }
    575 #endif
    576 
    577 static bool
    578 rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
    579 {
    580 	struct dhcpcd_ctx *ctx;
    581 	struct rt *krt;
    582 	int loglevel = LOG_INFO;
    583 	bool change, result = false;
    584 
    585 	assert(nrt != NULL);
    586 	ctx = nrt->rt_ifp->ctx;
    587 
    588 	/*
    589 	 * Don't install a gateway if not asked to.
    590 	 * This option is mainly for VPN users who want their VPN to be the
    591 	 * default route.
    592 	 * Because VPN's generally don't care about route management
    593 	 * beyond their own, a longer term solution would be to remove this
    594 	 * and get the VPN to inject the default route into dhcpcd somehow.
    595 	 */
    596 	if (((nrt->rt_ifp->active &&
    597 		 !(nrt->rt_ifp->options->options & DHCPCD_GATEWAY)) ||
    598 		(!nrt->rt_ifp->active && !(ctx->options & DHCPCD_GATEWAY))) &&
    599 	    sa_is_unspecified(nrt->rt_dest) &&
    600 	    sa_is_unspecified(nrt->rt_netmask))
    601 		return false;
    602 
    603 	krt = rb_tree_find_node(kroutes, nrt);
    604 	if (krt != NULL && krt->rt_ifp == nrt->rt_ifp &&
    605 	    /* Only test flags dhcpcd controls */
    606 	    (krt->rt_flags & (RTF_HOST | RTF_REJECT)) == nrt->rt_flags &&
    607 #ifdef HAVE_ROUTE_METRIC
    608 	    krt->rt_metric == nrt->rt_metric &&
    609 #endif
    610 	    sa_cmp(krt->rt_dest, nrt->rt_dest) == 0 &&
    611 	    rt_cmp_netmask(krt, nrt) == 0 &&
    612 	    sa_cmp(krt->rt_gateway, nrt->rt_gateway) == 0 &&
    613 	    (nrt->rt_ifp->flags & IFF_LOOPBACK || rt_cmp_mtu(krt, nrt) == 0)) {
    614 #ifdef HAVE_ROUTE_LIFETIME
    615 		if (rt_cmp_lifetime(krt, nrt) == 0) {
    616 			rt_desc(LOG_DEBUG, "keeping", krt);
    617 			return true;
    618 		} else
    619 			loglevel = LOG_DEBUG;
    620 #else
    621 		rt_desc(LOG_DEBUG, "keeping", krt);
    622 		return true;
    623 #endif
    624 	}
    625 
    626 	rt_desc(loglevel, ort == NULL ? "adding" : "changing", nrt);
    627 
    628 	change = krt != NULL;
    629 #ifdef RTF_CLONING
    630 	/* BSD can set routes to be cloning routes.
    631 	 * Cloned routes inherit the parent flags.
    632 	 * As such, we need to delete and re-add the route to flush children
    633 	 * to correct the flags. */
    634 	if (change && krt != NULL && krt->rt_flags & RTF_CLONING)
    635 		change = false;
    636 #endif
    637 	/* Reject routes have a gateway, non reject routes don't.
    638 	 * BSD kernels at least preserve RTF_GATEWAY so we need to punt it. */
    639 	if (change && krt->rt_flags & RTF_REJECT &&
    640 	    !(nrt->rt_flags & RTF_REJECT))
    641 		change = false;
    642 
    643 	if (change) {
    644 		if (if_route(RTM_CHANGE, nrt) != -1) {
    645 			result = true;
    646 			goto out;
    647 		}
    648 		if (errno != ESRCH)
    649 			logerr("if_route (CHG)");
    650 	}
    651 
    652 #ifdef HAVE_ROUTE_METRIC
    653 	/* With route metrics, we can safely add the new route before
    654 	 * deleting the old route. */
    655 	if (if_route(RTM_ADD, nrt) != -1) {
    656 		if (krt != NULL) {
    657 			if (if_route(RTM_DELETE, krt) == -1 && errno != ESRCH)
    658 				logerr("if_route (DEL)");
    659 		}
    660 		result = true;
    661 		goto out;
    662 	}
    663 
    664 	/* If the kernel claims the route exists we need to rip out the
    665 	 * old one first. */
    666 	if (errno != EEXIST || ort == NULL)
    667 		goto logerr;
    668 #endif
    669 
    670 	/* No route metrics, we need to delete the old route before
    671 	 * adding the new one. */
    672 #ifdef ROUTE_PER_GATEWAY
    673 	errno = 0;
    674 #endif
    675 	if (krt != NULL) {
    676 		if (if_route(RTM_DELETE, krt) == -1 && errno != ESRCH)
    677 			logerr("if_route (DEL)");
    678 	}
    679 #ifdef ROUTE_PER_GATEWAY
    680 	/* The OS allows many routes to the same dest with different gateways.
    681 	 * dhcpcd does not support this yet, so for the time being just keep on
    682 	 * deleting the route until there is an error. */
    683 	if (krt != NULL && errno == 0) {
    684 		for (;;) {
    685 			if (if_route(RTM_DELETE, krt) == -1)
    686 				break;
    687 		}
    688 	}
    689 #endif
    690 
    691 	/* Shouldn't need to check for EEXIST, but some kernels don't
    692 	 * dump the subnet route just after we added the address. */
    693 	if (if_route(RTM_ADD, nrt) != -1 || errno == EEXIST) {
    694 		result = true;
    695 		goto out;
    696 	}
    697 
    698 #ifdef HAVE_ROUTE_METRIC
    699 logerr:
    700 #endif
    701 	logerr("if_route (ADD)");
    702 
    703 out:
    704 	if (krt != NULL) {
    705 		rb_tree_remove_node(kroutes, krt);
    706 		rt_free(krt);
    707 	}
    708 	return result;
    709 }
    710 
    711 static bool
    712 rt_delete(struct rt *rt)
    713 {
    714 	int retval;
    715 
    716 	rt_desc(LOG_INFO, "deleting", rt);
    717 	retval = if_route(RTM_DELETE, rt) == -1 ? false : true;
    718 	if (!retval && errno != ENOENT && errno != ESRCH)
    719 		logerr(__func__);
    720 	return retval;
    721 }
    722 
    723 static int
    724 rt_cmp(const struct rt *r1, const struct rt *r2)
    725 {
    726 	if (r1->rt_ifp == r2->rt_ifp &&
    727 #ifdef HAVE_ROUTE_METRIC
    728 	    r1->rt_metric == r2->rt_metric &&
    729 #endif
    730 	    sa_cmp(r1->rt_gateway, r2->rt_gateway) == 0)
    731 		return 0;
    732 	return 1;
    733 }
    734 
    735 static bool
    736 rt_doroute(rb_tree_t *kroutes, struct rt *rt)
    737 {
    738 	struct dhcpcd_ctx *ctx;
    739 	struct rt * or ;
    740 
    741 	ctx = rt->rt_ifp->ctx;
    742 	/* Do we already manage it? */
    743 	or = rb_tree_find_node(&ctx->routes, rt);
    744 	if (or != NULL) {
    745 		if (rt->rt_dflags & RTDF_FAKE)
    746 			return true;
    747 		if (or->rt_dflags & RTDF_FAKE || rt_cmp(rt, or) != 0 ||
    748 		    (rt->rt_ifa->sa_family != AF_UNSPEC &&
    749 			sa_cmp(or->rt_ifa, rt->rt_ifa) != 0) ||
    750 #ifdef HAVE_ROUTE_LIFETIME
    751 		    rt_cmp_lifetime(rt, or) != 0 ||
    752 #endif
    753 		    rt_cmp_mtu(rt, or) != 0) {
    754 			if (!rt_add(kroutes, rt, or))
    755 				return false;
    756 		} else {
    757 #ifdef HAVE_ROUTE_LIFETIME
    758 			/* The existing kernel route matches what we want
    759 			 * and the lifetime is inside the allowed deviation.
    760 			 * Persist the original acquisition time so the
    761 			 * deviaton can drop outside what is allowed and the
    762 			 * kernel route is re-added with a new lifetime. */
    763 			rt->rt_acquired = or->rt_acquired;
    764 #endif
    765 		}
    766 
    767 		rb_tree_remove_node(&ctx->routes, or);
    768 		rt_free(or);
    769 	} else {
    770 		if (rt->rt_dflags & RTDF_FAKE) {
    771 			or = rb_tree_find_node(kroutes, rt);
    772 			if (or == NULL)
    773 				return false;
    774 			if (rt_cmp(rt, or) == 0)
    775 				return false;
    776 		} else {
    777 			if (!rt_add(kroutes, rt, NULL))
    778 				return false;
    779 		}
    780 	}
    781 
    782 	return true;
    783 }
    784 
    785 void
    786 rt_build(struct dhcpcd_ctx *ctx, int af)
    787 {
    788 	rb_tree_t routes, added, kroutes;
    789 	struct rt *rt, *rtn;
    790 	unsigned long long o;
    791 
    792 	/* When exiting with persistence, don't change any routing
    793 	 * which maybe affected by interfaces stopping. */
    794 	if ((ctx->options & (DHCPCD_EXITING | DHCPCD_PERSISTENT)) ==
    795 	    (DHCPCD_EXITING | DHCPCD_PERSISTENT))
    796 		return;
    797 
    798 	rb_tree_init(&routes, &rt_compare_proto_ops);
    799 	rb_tree_init(&added, &rt_compare_os_ops);
    800 	rb_tree_init(&kroutes, &rt_compare_os_ops);
    801 	if (if_initrt(ctx, &kroutes, af) != 0)
    802 		logerr("%s: if_initrt", __func__);
    803 	ctx->rt_order = 0;
    804 	ctx->options |= DHCPCD_RTBUILD;
    805 
    806 #ifdef INET
    807 	if (!inet_getroutes(ctx, &routes))
    808 		goto getfail;
    809 #endif
    810 #ifdef INET6
    811 	if (!inet6_getroutes(ctx, &routes))
    812 		goto getfail;
    813 #endif
    814 
    815 #ifdef HAVE_RT_MISSFILTER
    816 	/* Rewind the miss filter */
    817 	ctx->rt_missfilterlen = 0;
    818 #endif
    819 
    820 	RB_TREE_FOREACH_SAFE(rt, &routes, rtn)
    821 	{
    822 		if (rt->rt_ifp->active) {
    823 			if (!(rt->rt_ifp->options->options & DHCPCD_CONFIGURE))
    824 				continue;
    825 		} else if (!(ctx->options & DHCPCD_CONFIGURE))
    826 			continue;
    827 #ifdef HAVE_RT_MISSFILTER
    828 		if (rt_is_default(rt) &&
    829 		    if_missfilter(rt->rt_ifp, rt->rt_gateway) == -1)
    830 			logerr("if_missfilter");
    831 #endif
    832 		if ((rt->rt_dest->sa_family != af &&
    833 			rt->rt_dest->sa_family != AF_UNSPEC) ||
    834 		    (rt->rt_gateway->sa_family != af &&
    835 			rt->rt_gateway->sa_family != AF_UNSPEC))
    836 			continue;
    837 		/* Is this route already in our table? */
    838 		if (rb_tree_find_node(&added, rt) != NULL)
    839 			continue;
    840 		if (rt_doroute(&kroutes, rt)) {
    841 			rb_tree_remove_node(&routes, rt);
    842 			if (rb_tree_insert_node(&added, rt) != rt) {
    843 				errno = EEXIST;
    844 				logerr(__func__);
    845 				rt_free(rt);
    846 			}
    847 		}
    848 	}
    849 
    850 #ifdef HAVE_RT_MISSFILTER
    851 	if (!(ctx->options & DHCPCD_EXITING) &&
    852 	    if_missfilter_apply(ctx) == -1 && errno != ENOTSUP)
    853 		logerr("if_missfilter_apply");
    854 #endif
    855 
    856 	/* Remove old routes we used to manage. */
    857 	RB_TREE_FOREACH_REVERSE_SAFE(rt, &ctx->routes, rtn)
    858 	{
    859 		if ((rt->rt_dest->sa_family != af &&
    860 			rt->rt_dest->sa_family != AF_UNSPEC) ||
    861 		    (rt->rt_gateway->sa_family != af &&
    862 			rt->rt_gateway->sa_family != AF_UNSPEC))
    863 			continue;
    864 		rb_tree_remove_node(&ctx->routes, rt);
    865 		if (rb_tree_find_node(&added, rt) == NULL) {
    866 			o = rt->rt_ifp->options ? rt->rt_ifp->options->options :
    867 						  ctx->options;
    868 			if ((o & (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
    869 			    (DHCPCD_EXITING | DHCPCD_PERSISTENT))
    870 				rt_delete(rt);
    871 		}
    872 		rt_free(rt);
    873 	}
    874 
    875 	/* XXX This needs to be optimised. */
    876 	while ((rt = RB_TREE_MIN(&added)) != NULL) {
    877 		rb_tree_remove_node(&added, rt);
    878 		if (rb_tree_insert_node(&ctx->routes, rt) != rt) {
    879 			errno = EEXIST;
    880 			logerr(__func__);
    881 			rt_free(rt);
    882 		}
    883 	}
    884 
    885 getfail:
    886 	rt_headclear(&routes, AF_UNSPEC);
    887 	rt_headclear(&kroutes, AF_UNSPEC);
    888 }
    889