Update to dhcpcd-9.2.0 with the following changes:
[dragonfly.git] / contrib / dhcpcd / src / route.c
blob83bc37c7773159958be6585e2df167bd4fafffd4
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
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.
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.
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>
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"
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
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
73 static void
74 rt_maskedaddr(struct sockaddr *dst,
75 const struct sockaddr *addr, const struct sockaddr *netmask)
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));
82 dst->sa_family = addr->sa_family;
83 #ifdef HAVE_SA_LEN
84 dst->sa_len = addr->sa_len;
85 #endif
87 if (sa_is_unspecified(netmask)) {
88 if (addre > dstp)
89 memcpy(dstp, addrp, (size_t)(addre - dstp));
90 return;
93 while (dstp < netmaske)
94 *dstp++ = *addrp++ & *netmaskp++;
95 if (dstp < addre)
96 memset(dstp, 0, (size_t)(addre - dstp));
99 int
100 rt_cmp_dest(const struct rt *rt1, const struct rt *rt2)
102 union sa_ss ma1 = { .sa.sa_family = AF_UNSPEC };
103 union sa_ss ma2 = { .sa.sa_family = AF_UNSPEC };
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);
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.
116 static int
117 rt_cmp_netmask(const struct rt *rt1, const struct rt *rt2)
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);
125 static int
126 rt_compare_os(__unused void *context, const void *node1, const void *node2)
128 const struct rt *rt1 = node1, *rt2 = node2;
129 int c;
131 /* Sort by masked destination. */
132 c = rt_cmp_dest(rt1, rt2);
133 if (c != 0)
134 return c;
136 #ifdef HAVE_ROUTE_METRIC
137 c = (int)(rt1->rt_ifp->metric - rt2->rt_ifp->metric);
138 #endif
139 return c;
142 static int
143 rt_compare_list(__unused void *context, const void *node1, const void *node2)
145 const struct rt *rt1 = node1, *rt2 = node2;
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;
154 static int
155 rt_compare_proto(void *context, const void *node1, const void *node2)
157 const struct rt *rt1 = node1, *rt2 = node2;
158 int c;
159 struct interface *ifp1, *ifp2;
161 assert(rt1->rt_ifp != NULL);
162 assert(rt2->rt_ifp != NULL);
163 ifp1 = rt1->rt_ifp;
164 ifp2 = rt2->rt_ifp;
166 /* Prefer interfaces with a carrier. */
167 c = ifp1->carrier - ifp2->carrier;
168 if (c != 0)
169 return -c;
171 #ifdef INET
172 /* IPv4LL routes always come last */
173 if (rt1->rt_dflags & RTDF_IPV4LL && !(rt2->rt_dflags & RTDF_IPV4LL))
174 return -1;
175 else if (!(rt1->rt_dflags & RTDF_IPV4LL) && rt2->rt_dflags & RTDF_IPV4LL)
176 return 1;
177 #endif
179 /* Lower metric interfaces come first. */
180 c = (int)(ifp1->metric - ifp2->metric);
181 if (c != 0)
182 return c;
184 /* Finally the order in which the route was given to us. */
185 return rt_compare_list(context, rt1, rt2);
188 static const rb_tree_ops_t rt_compare_os_ops = {
189 .rbto_compare_nodes = rt_compare_os,
190 .rbto_compare_key = rt_compare_os,
191 .rbto_node_offset = offsetof(struct rt, rt_tree),
192 .rbto_context = NULL
195 const rb_tree_ops_t rt_compare_list_ops = {
196 .rbto_compare_nodes = rt_compare_list,
197 .rbto_compare_key = rt_compare_list,
198 .rbto_node_offset = offsetof(struct rt, rt_tree),
199 .rbto_context = NULL
202 const rb_tree_ops_t rt_compare_proto_ops = {
203 .rbto_compare_nodes = rt_compare_proto,
204 .rbto_compare_key = rt_compare_proto,
205 .rbto_node_offset = offsetof(struct rt, rt_tree),
206 .rbto_context = NULL
209 #ifdef RT_FREE_ROUTE_TABLE
210 static int
211 rt_compare_free(__unused void *context, const void *node1, const void *node2)
214 return node1 == node2 ? 0 : node1 < node2 ? -1 : 1;
217 static const rb_tree_ops_t rt_compare_free_ops = {
218 .rbto_compare_nodes = rt_compare_free,
219 .rbto_compare_key = rt_compare_free,
220 .rbto_node_offset = offsetof(struct rt, rt_tree),
221 .rbto_context = NULL
223 #endif
225 void
226 rt_init(struct dhcpcd_ctx *ctx)
229 rb_tree_init(&ctx->routes, &rt_compare_os_ops);
230 #ifdef RT_FREE_ROUTE_TABLE
231 rb_tree_init(&ctx->froutes, &rt_compare_free_ops);
232 #endif
235 bool
236 rt_is_default(const struct rt *rt)
239 return sa_is_unspecified(&rt->rt_dest) &&
240 sa_is_unspecified(&rt->rt_netmask);
243 static void
244 rt_desc(const char *cmd, const struct rt *rt)
246 char dest[INET_MAX_ADDRSTRLEN], gateway[INET_MAX_ADDRSTRLEN];
247 int prefix;
248 const char *ifname;
249 bool gateway_unspec;
251 assert(cmd != NULL);
252 assert(rt != NULL);
254 sa_addrtop(&rt->rt_dest, dest, sizeof(dest));
255 prefix = sa_toprefix(&rt->rt_netmask);
256 sa_addrtop(&rt->rt_gateway, gateway, sizeof(gateway));
257 gateway_unspec = sa_is_unspecified(&rt->rt_gateway);
258 ifname = rt->rt_ifp == NULL ? "(null)" : rt->rt_ifp->name;
260 if (rt->rt_flags & RTF_HOST) {
261 if (gateway_unspec)
262 loginfox("%s: %s host route to %s",
263 ifname, cmd, dest);
264 else
265 loginfox("%s: %s host route to %s via %s",
266 ifname, cmd, dest, gateway);
267 } else if (rt_is_default(rt)) {
268 if (gateway_unspec)
269 loginfox("%s: %s default route",
270 ifname, cmd);
271 else
272 loginfox("%s: %s default route via %s",
273 ifname, cmd, gateway);
274 } else if (gateway_unspec)
275 loginfox("%s: %s%s route to %s/%d",
276 ifname, cmd,
277 rt->rt_flags & RTF_REJECT ? " reject" : "",
278 dest, prefix);
279 else
280 loginfox("%s: %s%s route to %s/%d via %s",
281 ifname, cmd,
282 rt->rt_flags & RTF_REJECT ? " reject" : "",
283 dest, prefix, gateway);
286 void
287 rt_headclear0(struct dhcpcd_ctx *ctx, rb_tree_t *rts, int af)
289 struct rt *rt, *rtn;
291 if (rts == NULL)
292 return;
293 assert(ctx != NULL);
294 #ifdef RT_FREE_ROUTE_TABLE
295 assert(&ctx->froutes != rts);
296 #endif
298 RB_TREE_FOREACH_SAFE(rt, rts, rtn) {
299 if (af != AF_UNSPEC &&
300 rt->rt_dest.sa_family != af &&
301 rt->rt_gateway.sa_family != af)
302 continue;
303 rb_tree_remove_node(rts, rt);
304 rt_free(rt);
308 void
309 rt_headclear(rb_tree_t *rts, int af)
311 struct rt *rt;
313 if (rts == NULL || (rt = RB_TREE_MIN(rts)) == NULL)
314 return;
315 rt_headclear0(rt->rt_ifp->ctx, rts, af);
318 static void
319 rt_headfree(rb_tree_t *rts)
321 struct rt *rt;
323 while ((rt = RB_TREE_MIN(rts)) != NULL) {
324 rb_tree_remove_node(rts, rt);
325 free(rt);
329 void
330 rt_dispose(struct dhcpcd_ctx *ctx)
333 assert(ctx != NULL);
334 rt_headfree(&ctx->routes);
335 #ifdef RT_FREE_ROUTE_TABLE
336 rt_headfree(&ctx->froutes);
337 #ifdef RT_FREE_ROUTE_TABLE_STATS
338 logdebugx("free route list used %zu times", froutes);
339 logdebugx("new routes from route free list %zu", nroutes);
340 logdebugx("maximum route free list size %zu", mroutes);
341 #endif
342 #endif
345 struct rt *
346 rt_new0(struct dhcpcd_ctx *ctx)
348 struct rt *rt;
350 assert(ctx != NULL);
351 #ifdef RT_FREE_ROUTE_TABLE
352 if ((rt = RB_TREE_MIN(&ctx->froutes)) != NULL) {
353 rb_tree_remove_node(&ctx->froutes, rt);
354 #ifdef RT_FREE_ROUTE_TABLE_STATS
355 croutes--;
356 nroutes++;
357 #endif
358 } else
359 #endif
360 if ((rt = malloc(sizeof(*rt))) == NULL) {
361 logerr(__func__);
362 return NULL;
364 memset(rt, 0, sizeof(*rt));
365 return rt;
368 void
369 rt_setif(struct rt *rt, struct interface *ifp)
372 assert(rt != NULL);
373 assert(ifp != NULL);
374 rt->rt_ifp = ifp;
375 #ifdef HAVE_ROUTE_METRIC
376 rt->rt_metric = ifp->metric;
377 #endif
380 struct rt *
381 rt_new(struct interface *ifp)
383 struct rt *rt;
385 assert(ifp != NULL);
386 if ((rt = rt_new0(ifp->ctx)) == NULL)
387 return NULL;
388 rt_setif(rt, ifp);
389 return rt;
392 struct rt *
393 rt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx)
396 rt->rt_order = ctx->rt_order++;
397 if (rb_tree_insert_node(tree, rt) == rt)
398 return rt;
400 rt_free(rt);
401 return NULL;
404 struct rt *
405 rt_proto_add(rb_tree_t *tree, struct rt *rt)
408 assert (rt->rt_ifp != NULL);
409 return rt_proto_add_ctx(tree, rt, rt->rt_ifp->ctx);
412 void
413 rt_free(struct rt *rt)
415 #ifdef RT_FREE_ROUTE_TABLE
416 struct dhcpcd_ctx *ctx;
418 assert(rt != NULL);
419 if (rt->rt_ifp == NULL) {
420 free(rt);
421 return;
424 ctx = rt->rt_ifp->ctx;
425 rb_tree_insert_node(&ctx->froutes, rt);
426 #ifdef RT_FREE_ROUTE_TABLE_STATS
427 croutes++;
428 froutes++;
429 if (croutes > mroutes)
430 mroutes = croutes;
431 #endif
432 #else
433 free(rt);
434 #endif
437 void
438 rt_freeif(struct interface *ifp)
440 struct dhcpcd_ctx *ctx;
441 struct rt *rt, *rtn;
443 if (ifp == NULL)
444 return;
445 ctx = ifp->ctx;
446 RB_TREE_FOREACH_SAFE(rt, &ctx->routes, rtn) {
447 if (rt->rt_ifp == ifp) {
448 rb_tree_remove_node(&ctx->routes, rt);
449 rt_free(rt);
454 /* If something other than dhcpcd removes a route,
455 * we need to remove it from our internal table. */
456 void
457 rt_recvrt(int cmd, const struct rt *rt, pid_t pid)
459 struct dhcpcd_ctx *ctx;
460 struct rt *f;
462 assert(rt != NULL);
463 assert(rt->rt_ifp != NULL);
464 assert(rt->rt_ifp->ctx != NULL);
466 ctx = rt->rt_ifp->ctx;
468 switch(cmd) {
469 case RTM_DELETE:
470 f = rb_tree_find_node(&ctx->routes, rt);
471 if (f != NULL) {
472 char buf[32];
474 rb_tree_remove_node(&ctx->routes, f);
475 snprintf(buf, sizeof(buf), "pid %d deleted", pid);
476 rt_desc(buf, f);
477 rt_free(f);
479 break;
482 #if defined(IPV4LL) && defined(HAVE_ROUTE_METRIC)
483 if (rt->rt_dest.sa_family == AF_INET)
484 ipv4ll_recvrt(cmd, rt);
485 #endif
488 static bool
489 rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
491 struct dhcpcd_ctx *ctx;
492 bool change, kroute, result;
494 assert(nrt != NULL);
495 ctx = nrt->rt_ifp->ctx;
498 * Don't install a gateway if not asked to.
499 * This option is mainly for VPN users who want their VPN to be the
500 * default route.
501 * Because VPN's generally don't care about route management
502 * beyond their own, a longer term solution would be to remove this
503 * and get the VPN to inject the default route into dhcpcd somehow.
505 if (((nrt->rt_ifp->active &&
506 !(nrt->rt_ifp->options->options & DHCPCD_GATEWAY)) ||
507 (!nrt->rt_ifp->active && !(ctx->options & DHCPCD_GATEWAY))) &&
508 sa_is_unspecified(&nrt->rt_dest) &&
509 sa_is_unspecified(&nrt->rt_netmask))
510 return false;
512 rt_desc(ort == NULL ? "adding" : "changing", nrt);
514 change = kroute = result = false;
515 if (ort == NULL) {
516 ort = rb_tree_find_node(kroutes, nrt);
517 if (ort != NULL &&
518 ((ort->rt_flags & RTF_REJECT &&
519 nrt->rt_flags & RTF_REJECT) ||
520 (ort->rt_ifp == nrt->rt_ifp &&
521 #ifdef HAVE_ROUTE_METRIC
522 ort->rt_metric == nrt->rt_metric &&
523 #endif
524 sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)))
526 if (ort->rt_mtu == nrt->rt_mtu)
527 return true;
528 change = true;
529 kroute = true;
531 } else if (ort->rt_dflags & RTDF_FAKE &&
532 !(nrt->rt_dflags & RTDF_FAKE) &&
533 ort->rt_ifp == nrt->rt_ifp &&
534 #ifdef HAVE_ROUTE_METRIC
535 ort->rt_metric == nrt->rt_metric &&
536 #endif
537 sa_cmp(&ort->rt_dest, &nrt->rt_dest) == 0 &&
538 rt_cmp_netmask(ort, nrt) == 0 &&
539 sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)
541 if (ort->rt_mtu == nrt->rt_mtu)
542 return true;
543 change = true;
546 #ifdef RTF_CLONING
547 /* BSD can set routes to be cloning routes.
548 * Cloned routes inherit the parent flags.
549 * As such, we need to delete and re-add the route to flush children
550 * to correct the flags. */
551 if (change && ort != NULL && ort->rt_flags & RTF_CLONING)
552 change = false;
553 #endif
555 if (change) {
556 if (if_route(RTM_CHANGE, nrt) != -1) {
557 result = true;
558 goto out;
560 if (errno != ESRCH)
561 logerr("if_route (CHG)");
564 #ifdef HAVE_ROUTE_METRIC
565 /* With route metrics, we can safely add the new route before
566 * deleting the old route. */
567 if (if_route(RTM_ADD, nrt) != -1) {
568 if (ort != NULL) {
569 if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
570 logerr("if_route (DEL)");
572 result = true;
573 goto out;
576 /* If the kernel claims the route exists we need to rip out the
577 * old one first. */
578 if (errno != EEXIST || ort == NULL)
579 goto logerr;
580 #endif
582 /* No route metrics, we need to delete the old route before
583 * adding the new one. */
584 #ifdef ROUTE_PER_GATEWAY
585 errno = 0;
586 #endif
587 if (ort != NULL) {
588 if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
589 logerr("if_route (DEL)");
590 else
591 kroute = false;
593 #ifdef ROUTE_PER_GATEWAY
594 /* The OS allows many routes to the same dest with different gateways.
595 * dhcpcd does not support this yet, so for the time being just keep on
596 * deleting the route until there is an error. */
597 if (ort != NULL && errno == 0) {
598 for (;;) {
599 if (if_route(RTM_DELETE, ort) == -1)
600 break;
603 #endif
605 /* Shouldn't need to check for EEXIST, but some kernels don't
606 * dump the subnet route just after we added the address. */
607 if (if_route(RTM_ADD, nrt) != -1 || errno == EEXIST) {
608 result = true;
609 goto out;
612 #ifdef HAVE_ROUTE_METRIC
613 logerr:
614 #endif
615 logerr("if_route (ADD)");
617 out:
618 if (kroute) {
619 rb_tree_remove_node(kroutes, ort);
620 rt_free(ort);
622 return result;
625 static bool
626 rt_delete(struct rt *rt)
628 int retval;
630 rt_desc("deleting", rt);
631 retval = if_route(RTM_DELETE, rt) == -1 ? false : true;
632 if (!retval && errno != ENOENT && errno != ESRCH)
633 logerr(__func__);
634 return retval;
637 static bool
638 rt_cmp(const struct rt *r1, const struct rt *r2)
641 return (r1->rt_ifp == r2->rt_ifp &&
642 #ifdef HAVE_ROUTE_METRIC
643 r1->rt_metric == r2->rt_metric &&
644 #endif
645 sa_cmp(&r1->rt_gateway, &r2->rt_gateway) == 0);
648 static bool
649 rt_doroute(rb_tree_t *kroutes, struct rt *rt)
651 struct dhcpcd_ctx *ctx;
652 struct rt *or;
654 ctx = rt->rt_ifp->ctx;
655 /* Do we already manage it? */
656 or = rb_tree_find_node(&ctx->routes, rt);
657 if (or != NULL) {
658 if (rt->rt_dflags & RTDF_FAKE)
659 return true;
660 if (or->rt_dflags & RTDF_FAKE ||
661 !rt_cmp(rt, or) ||
662 (rt->rt_ifa.sa_family != AF_UNSPEC &&
663 sa_cmp(&or->rt_ifa, &rt->rt_ifa) != 0) ||
664 or->rt_mtu != rt->rt_mtu)
666 if (!rt_add(kroutes, rt, or))
667 return false;
669 rb_tree_remove_node(&ctx->routes, or);
670 rt_free(or);
671 } else {
672 if (rt->rt_dflags & RTDF_FAKE) {
673 or = rb_tree_find_node(kroutes, rt);
674 if (or == NULL)
675 return false;
676 if (!rt_cmp(rt, or))
677 return false;
678 } else {
679 if (!rt_add(kroutes, rt, NULL))
680 return false;
684 return true;
687 void
688 rt_build(struct dhcpcd_ctx *ctx, int af)
690 rb_tree_t routes, added, kroutes;
691 struct rt *rt, *rtn;
692 unsigned long long o;
694 rb_tree_init(&routes, &rt_compare_proto_ops);
695 rb_tree_init(&added, &rt_compare_os_ops);
696 rb_tree_init(&kroutes, &rt_compare_os_ops);
697 if_initrt(ctx, &kroutes, af);
698 ctx->rt_order = 0;
699 ctx->options |= DHCPCD_RTBUILD;
701 #ifdef INET
702 if (!inet_getroutes(ctx, &routes))
703 goto getfail;
704 #endif
705 #ifdef INET6
706 if (!inet6_getroutes(ctx, &routes))
707 goto getfail;
708 #endif
710 #ifdef BSD
711 /* Rewind the miss filter */
712 ctx->rt_missfilterlen = 0;
713 #endif
715 RB_TREE_FOREACH_SAFE(rt, &routes, rtn) {
716 #ifdef BSD
717 if (rt_is_default(rt) &&
718 if_missfilter(rt->rt_ifp, &rt->rt_gateway) == -1)
719 logerr("if_missfilter");
720 #endif
721 if ((rt->rt_dest.sa_family != af &&
722 rt->rt_dest.sa_family != AF_UNSPEC) ||
723 (rt->rt_gateway.sa_family != af &&
724 rt->rt_gateway.sa_family != AF_UNSPEC))
725 continue;
726 /* Is this route already in our table? */
727 if (rb_tree_find_node(&added, rt) != NULL)
728 continue;
729 if (rt_doroute(&kroutes, rt)) {
730 rb_tree_remove_node(&routes, rt);
731 if (rb_tree_insert_node(&added, rt) != rt) {
732 errno = EEXIST;
733 logerr(__func__);
734 rt_free(rt);
739 #ifdef BSD
740 if (if_missfilter_apply(ctx) == -1 && errno != ENOTSUP)
741 logerr("if_missfilter_apply");
742 #endif
744 /* Remove old routes we used to manage. */
745 RB_TREE_FOREACH_REVERSE_SAFE(rt, &ctx->routes, rtn) {
746 if ((rt->rt_dest.sa_family != af &&
747 rt->rt_dest.sa_family != AF_UNSPEC) ||
748 (rt->rt_gateway.sa_family != af &&
749 rt->rt_gateway.sa_family != AF_UNSPEC))
750 continue;
751 rb_tree_remove_node(&ctx->routes, rt);
752 if (rb_tree_find_node(&added, rt) == NULL) {
753 o = rt->rt_ifp->options ?
754 rt->rt_ifp->options->options :
755 ctx->options;
756 if ((o &
757 (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
758 (DHCPCD_EXITING | DHCPCD_PERSISTENT))
759 rt_delete(rt);
761 rt_free(rt);
764 /* XXX This needs to be optimised. */
765 while ((rt = RB_TREE_MIN(&added)) != NULL) {
766 rb_tree_remove_node(&added, rt);
767 if (rb_tree_insert_node(&ctx->routes, rt) != rt) {
768 errno = EEXIST;
769 logerr(__func__);
770 rt_free(rt);
775 getfail:
776 rt_headclear(&routes, AF_UNSPEC);
777 rt_headclear(&kroutes, AF_UNSPEC);