Update to dhcpcd-9.3.4 with the following changes:
[dragonfly.git] / contrib / dhcpcd / src / route.c
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 #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
178
179         /* Lower metric interfaces come first. */
180         c = (int)(ifp1->metric - ifp2->metric);
181         if (c != 0)
182                 return c;
183
184         /* Finally the order in which the route was given to us. */
185         return rt_compare_list(context, rt1, rt2);
186 }
187
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
193 };
194
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
200 };
201
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
207 };
208
209 #ifdef RT_FREE_ROUTE_TABLE
210 static int
211 rt_compare_free(__unused void *context, const void *node1, const void *node2)
212 {
213
214         return node1 == node2 ? 0 : node1 < node2 ? -1 : 1;
215 }
216
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
222 };
223 #endif
224
225 void
226 rt_init(struct dhcpcd_ctx *ctx)
227 {
228
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
233 }
234
235 bool
236 rt_is_default(const struct rt *rt)
237 {
238
239         return sa_is_unspecified(&rt->rt_dest) &&
240             sa_is_unspecified(&rt->rt_netmask);
241 }
242
243 static void
244 rt_desc(const char *cmd, const struct rt *rt)
245 {
246         char dest[INET_MAX_ADDRSTRLEN], gateway[INET_MAX_ADDRSTRLEN];
247         int prefix;
248         const char *ifname;
249         bool gateway_unspec;
250
251         assert(cmd != NULL);
252         assert(rt != NULL);
253
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;
259
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);
284 }
285
286 void
287 rt_headclear0(struct dhcpcd_ctx *ctx, rb_tree_t *rts, int af)
288 {
289         struct rt *rt, *rtn;
290
291         if (rts == NULL)
292                 return;
293         assert(ctx != NULL);
294 #ifdef RT_FREE_ROUTE_TABLE
295         assert(&ctx->froutes != rts);
296 #endif
297
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);
305         }
306 }
307
308 void
309 rt_headclear(rb_tree_t *rts, int af)
310 {
311         struct rt *rt;
312
313         if (rts == NULL || (rt = RB_TREE_MIN(rts)) == NULL)
314                 return;
315         rt_headclear0(rt->rt_ifp->ctx, rts, af);
316 }
317
318 static void
319 rt_headfree(rb_tree_t *rts)
320 {
321         struct rt *rt;
322
323         while ((rt = RB_TREE_MIN(rts)) != NULL) {
324                 rb_tree_remove_node(rts, rt);
325                 free(rt);
326         }
327 }
328
329 void
330 rt_dispose(struct dhcpcd_ctx *ctx)
331 {
332
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
343 }
344
345 struct rt *
346 rt_new0(struct dhcpcd_ctx *ctx)
347 {
348         struct rt *rt;
349
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;
363         }
364         memset(rt, 0, sizeof(*rt));
365         return rt;
366 }
367
368 void
369 rt_setif(struct rt *rt, struct interface *ifp)
370 {
371
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
378 }
379
380 struct rt *
381 rt_new(struct interface *ifp)
382 {
383         struct rt *rt;
384
385         assert(ifp != NULL);
386         if ((rt = rt_new0(ifp->ctx)) == NULL)
387                 return NULL;
388         rt_setif(rt, ifp);
389         return rt;
390 }
391
392 struct rt *
393 rt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx)
394 {
395
396         rt->rt_order = ctx->rt_order++;
397         if (rb_tree_insert_node(tree, rt) == rt)
398                 return rt;
399
400         rt_free(rt);
401         return NULL;
402 }
403
404 struct rt *
405 rt_proto_add(rb_tree_t *tree, struct rt *rt)
406 {
407
408         assert (rt->rt_ifp != NULL);
409         return rt_proto_add_ctx(tree, rt, rt->rt_ifp->ctx);
410 }
411
412 void
413 rt_free(struct rt *rt)
414 {
415 #ifdef RT_FREE_ROUTE_TABLE
416         struct dhcpcd_ctx *ctx;
417
418         assert(rt != NULL);
419         if (rt->rt_ifp == NULL) {
420                 free(rt);
421                 return;
422         }
423
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
435 }
436
437 void
438 rt_freeif(struct interface *ifp)
439 {
440         struct dhcpcd_ctx *ctx;
441         struct rt *rt, *rtn;
442
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);
450                 }
451         }
452 }
453
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)
458 {
459         struct dhcpcd_ctx *ctx;
460         struct rt *f;
461
462         assert(rt != NULL);
463         assert(rt->rt_ifp != NULL);
464         assert(rt->rt_ifp->ctx != NULL);
465
466         ctx = rt->rt_ifp->ctx;
467
468         switch(cmd) {
469         case RTM_DELETE:
470                 f = rb_tree_find_node(&ctx->routes, rt);
471                 if (f != NULL) {
472                         char buf[32];
473
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);
478                 }
479                 break;
480         }
481
482 #if defined(IPV4LL) && defined(HAVE_ROUTE_METRIC)
483         if (rt->rt_dest.sa_family == AF_INET)
484                 ipv4ll_recvrt(cmd, rt);
485 #endif
486 }
487
488 static bool
489 rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
490 {
491         struct dhcpcd_ctx *ctx;
492         bool change, kroute, result;
493
494         assert(nrt != NULL);
495         ctx = nrt->rt_ifp->ctx;
496
497         /*
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.
504          */
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;
511
512         rt_desc(ort == NULL ? "adding" : "changing", nrt);
513
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)))
525                 {
526                         if (ort->rt_mtu == nrt->rt_mtu)
527                                 return true;
528                         change = true;
529                         kroute = true;
530                 }
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)
540         {
541                 if (ort->rt_mtu == nrt->rt_mtu)
542                         return true;
543                 change = true;
544         }
545
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
554
555         if (change) {
556                 if (if_route(RTM_CHANGE, nrt) != -1) {
557                         result = true;
558                         goto out;
559                 }
560                 if (errno != ESRCH)
561                         logerr("if_route (CHG)");
562         }
563
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)");
571                 }
572                 result = true;
573                 goto out;
574         }
575
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
581
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;
592         }
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;
601                 }
602         }
603 #endif
604
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;
610         }
611
612 #ifdef HAVE_ROUTE_METRIC
613 logerr:
614 #endif
615         logerr("if_route (ADD)");
616
617 out:
618         if (kroute) {
619                 rb_tree_remove_node(kroutes, ort);
620                 rt_free(ort);
621         }
622         return result;
623 }
624
625 static bool
626 rt_delete(struct rt *rt)
627 {
628         int retval;
629
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;
635 }
636
637 static bool
638 rt_cmp(const struct rt *r1, const struct rt *r2)
639 {
640
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);
646 }
647
648 static bool
649 rt_doroute(rb_tree_t *kroutes, struct rt *rt)
650 {
651         struct dhcpcd_ctx *ctx;
652         struct rt *or;
653
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)
665                 {
666                         if (!rt_add(kroutes, rt, or))
667                                 return false;
668                 }
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;
681                 }
682         }
683
684         return true;
685 }
686
687 void
688 rt_build(struct dhcpcd_ctx *ctx, int af)
689 {
690         rb_tree_t routes, added, kroutes;
691         struct rt *rt, *rtn;
692         unsigned long long o;
693
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;
700
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
709
710 #ifdef BSD
711         /* Rewind the miss filter */
712         ctx->rt_missfilterlen = 0;
713 #endif
714
715         RB_TREE_FOREACH_SAFE(rt, &routes, rtn) {
716                 if (rt->rt_ifp->active) {
717                         if (!(rt->rt_ifp->options->options & DHCPCD_CONFIGURE))
718                                 continue;
719                 } else if (!(ctx->options & DHCPCD_CONFIGURE))
720                         continue;
721 #ifdef BSD
722                 if (rt_is_default(rt) &&
723                     if_missfilter(rt->rt_ifp, &rt->rt_gateway) == -1)
724                         logerr("if_missfilter");
725 #endif
726                 if ((rt->rt_dest.sa_family != af &&
727                     rt->rt_dest.sa_family != AF_UNSPEC) ||
728                     (rt->rt_gateway.sa_family != af &&
729                     rt->rt_gateway.sa_family != AF_UNSPEC))
730                         continue;
731                 /* Is this route already in our table? */
732                 if (rb_tree_find_node(&added, rt) != NULL)
733                         continue;
734                 if (rt_doroute(&kroutes, rt)) {
735                         rb_tree_remove_node(&routes, rt);
736                         if (rb_tree_insert_node(&added, rt) != rt) {
737                                 errno = EEXIST;
738                                 logerr(__func__);
739                                 rt_free(rt);
740                         }
741                 }
742         }
743
744 #ifdef BSD
745         if (if_missfilter_apply(ctx) == -1 && errno != ENOTSUP)
746                 logerr("if_missfilter_apply");
747 #endif
748
749         /* Remove old routes we used to manage. */
750         RB_TREE_FOREACH_REVERSE_SAFE(rt, &ctx->routes, rtn) {
751                 if ((rt->rt_dest.sa_family != af &&
752                     rt->rt_dest.sa_family != AF_UNSPEC) ||
753                     (rt->rt_gateway.sa_family != af &&
754                     rt->rt_gateway.sa_family != AF_UNSPEC))
755                         continue;
756                 rb_tree_remove_node(&ctx->routes, rt);
757                 if (rb_tree_find_node(&added, rt) == NULL) {
758                         o = rt->rt_ifp->options ?
759                             rt->rt_ifp->options->options :
760                             ctx->options;
761                         if ((o &
762                                 (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
763                                 (DHCPCD_EXITING | DHCPCD_PERSISTENT))
764                                 rt_delete(rt);
765                 }
766                 rt_free(rt);
767         }
768
769         /* XXX This needs to be optimised. */
770         while ((rt = RB_TREE_MIN(&added)) != NULL) {
771                 rb_tree_remove_node(&added, rt);
772                 if (rb_tree_insert_node(&ctx->routes, rt) != rt) {
773                         errno = EEXIST;
774                         logerr(__func__);
775                         rt_free(rt);
776                 }
777         }
778
779 getfail:
780         rt_headclear(&routes, AF_UNSPEC);
781         rt_headclear(&kroutes, AF_UNSPEC);
782 }