Import dhcpcd-8.0.4 to vendor branch.
[dragonfly.git] / contrib / dhcpcd / src / route.c
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * dhcpcd - route management
4  * Copyright (c) 2006-2019 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         /* Lower metric interfaces come first. */
172         c = (int)(ifp1->metric - ifp2->metric);
173         if (c != 0)
174                 return c;
175
176         /* Finally the order in which the route was given to us. */
177         return rt_compare_list(context, rt1, rt2);
178 }
179
180 static const rb_tree_ops_t rt_compare_os_ops = {
181         .rbto_compare_nodes = rt_compare_os,
182         .rbto_compare_key = rt_compare_os,
183         .rbto_node_offset = offsetof(struct rt, rt_tree),
184         .rbto_context = NULL
185 };
186
187 const rb_tree_ops_t rt_compare_list_ops = {
188         .rbto_compare_nodes = rt_compare_list,
189         .rbto_compare_key = rt_compare_list,
190         .rbto_node_offset = offsetof(struct rt, rt_tree),
191         .rbto_context = NULL
192 };
193
194 const rb_tree_ops_t rt_compare_proto_ops = {
195         .rbto_compare_nodes = rt_compare_proto,
196         .rbto_compare_key = rt_compare_proto,
197         .rbto_node_offset = offsetof(struct rt, rt_tree),
198         .rbto_context = NULL
199 };
200
201 #ifdef RT_FREE_ROUTE_TABLE
202 static int
203 rt_compare_free(__unused void *context, const void *node1, const void *node2)
204 {
205
206         return node1 == node2 ? 0 : node1 < node2 ? -1 : 1;
207 }
208
209 static const rb_tree_ops_t rt_compare_free_ops = {
210         .rbto_compare_nodes = rt_compare_free,
211         .rbto_compare_key = rt_compare_free,
212         .rbto_node_offset = offsetof(struct rt, rt_tree),
213         .rbto_context = NULL
214 };
215 #endif
216
217 void
218 rt_init(struct dhcpcd_ctx *ctx)
219 {
220
221         rb_tree_init(&ctx->routes, &rt_compare_os_ops);
222 #ifdef RT_FREE_ROUTE_TABLE
223         rb_tree_init(&ctx->froutes, &rt_compare_free_ops);
224 #endif
225 }
226
227 bool
228 rt_is_default(const struct rt *rt)
229 {
230
231         return sa_is_unspecified(&rt->rt_dest) &&
232             sa_is_unspecified(&rt->rt_netmask);
233 }
234
235 static void
236 rt_desc(const char *cmd, const struct rt *rt)
237 {
238         char dest[INET_MAX_ADDRSTRLEN], gateway[INET_MAX_ADDRSTRLEN];
239         int prefix;
240         const char *ifname;
241         bool gateway_unspec;
242
243         assert(cmd != NULL);
244         assert(rt != NULL);
245
246         sa_addrtop(&rt->rt_dest, dest, sizeof(dest));
247         prefix = sa_toprefix(&rt->rt_netmask);
248         sa_addrtop(&rt->rt_gateway, gateway, sizeof(gateway));
249         gateway_unspec = sa_is_unspecified(&rt->rt_gateway);
250         ifname = rt->rt_ifp == NULL ? "(null)" : rt->rt_ifp->name;
251
252         if (rt->rt_flags & RTF_HOST) {
253                 if (gateway_unspec)
254                         loginfox("%s: %s host route to %s",
255                             ifname, cmd, dest);
256                 else
257                         loginfox("%s: %s host route to %s via %s",
258                             ifname, cmd, dest, gateway);
259         } else if (rt_is_default(rt)) {
260                 if (gateway_unspec)
261                         loginfox("%s: %s default route",
262                             ifname, cmd);
263                 else
264                         loginfox("%s: %s default route via %s",
265                             ifname, cmd, gateway);
266         } else if (gateway_unspec)
267                 loginfox("%s: %s%s route to %s/%d",
268                     ifname, cmd,
269                     rt->rt_flags & RTF_REJECT ? " reject" : "",
270                     dest, prefix);
271         else
272                 loginfox("%s: %s%s route to %s/%d via %s",
273                     ifname, cmd,
274                     rt->rt_flags & RTF_REJECT ? " reject" : "",
275                     dest, prefix, gateway);
276 }
277
278 void
279 rt_headclear0(struct dhcpcd_ctx *ctx, rb_tree_t *rts, int af)
280 {
281         struct rt *rt, *rtn;
282
283         if (rts == NULL)
284                 return;
285         assert(ctx != NULL);
286 #ifdef RT_FREE_ROUTE_TABLE
287         assert(&ctx->froutes != rts);
288 #endif
289
290         RB_TREE_FOREACH_SAFE(rt, rts, rtn) {
291                 if (af != AF_UNSPEC &&
292                     rt->rt_dest.sa_family != af &&
293                     rt->rt_gateway.sa_family != af)
294                         continue;
295                 rb_tree_remove_node(rts, rt);
296                 rt_free(rt);
297         }
298 }
299
300 void
301 rt_headclear(rb_tree_t *rts, int af)
302 {
303         struct rt *rt;
304
305         if (rts == NULL || (rt = RB_TREE_MIN(rts)) == NULL)
306                 return;
307         rt_headclear0(rt->rt_ifp->ctx, rts, af);
308 }
309
310 static void
311 rt_headfree(rb_tree_t *rts)
312 {
313         struct rt *rt;
314
315         while ((rt = RB_TREE_MIN(rts)) != NULL) {
316                 rb_tree_remove_node(rts, rt);
317                 free(rt);
318         }
319 }
320
321 void
322 rt_dispose(struct dhcpcd_ctx *ctx)
323 {
324
325         assert(ctx != NULL);
326         rt_headfree(&ctx->routes);
327 #ifdef RT_FREE_ROUTE_TABLE
328         rt_headfree(&ctx->froutes);
329 #ifdef RT_FREE_ROUTE_TABLE_STATS
330         logdebugx("free route list used %zu times", froutes);
331         logdebugx("new routes from route free list %zu", nroutes);
332         logdebugx("maximum route free list size %zu", mroutes);
333 #endif
334 #endif
335 }
336
337 struct rt *
338 rt_new0(struct dhcpcd_ctx *ctx)
339 {
340         struct rt *rt;
341
342         assert(ctx != NULL);
343 #ifdef RT_FREE_ROUTE_TABLE
344         if ((rt = RB_TREE_MIN(&ctx->froutes)) != NULL) {
345                 rb_tree_remove_node(&ctx->froutes, rt);
346 #ifdef RT_FREE_ROUTE_TABLE_STATS
347                 croutes--;
348                 nroutes++;
349 #endif
350         } else
351 #endif
352         if ((rt = malloc(sizeof(*rt))) == NULL) {
353                 logerr(__func__);
354                 return NULL;
355         }
356         memset(rt, 0, sizeof(*rt));
357         return rt;
358 }
359
360 void
361 rt_setif(struct rt *rt, struct interface *ifp)
362 {
363
364         assert(rt != NULL);
365         assert(ifp != NULL);
366         rt->rt_ifp = ifp;
367 #ifdef HAVE_ROUTE_METRIC
368         rt->rt_metric = ifp->metric;
369 #endif
370 }
371
372 struct rt *
373 rt_new(struct interface *ifp)
374 {
375         struct rt *rt;
376
377         assert(ifp != NULL);
378         if ((rt = rt_new0(ifp->ctx)) == NULL)
379                 return NULL;
380         rt_setif(rt, ifp);
381         return rt;
382 }
383
384 struct rt *
385 rt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx)
386 {
387
388         rt->rt_order = ctx->rt_order++;
389         if (rb_tree_insert_node(tree, rt) == rt)
390                 return rt;
391
392         rt_free(rt);
393         return NULL;
394 }
395
396 struct rt *
397 rt_proto_add(rb_tree_t *tree, struct rt *rt)
398 {
399
400         assert (rt->rt_ifp != NULL);
401         return rt_proto_add_ctx(tree, rt, rt->rt_ifp->ctx);
402 }
403
404 void
405 rt_free(struct rt *rt)
406 {
407 #ifdef RT_FREE_ROUTE_TABLE
408         struct dhcpcd_ctx *ctx;
409
410         assert(rt != NULL);
411         if (rt->rt_ifp == NULL) {
412                 free(rt);
413                 return;
414         }
415
416         ctx = rt->rt_ifp->ctx;
417         rb_tree_insert_node(&ctx->froutes, rt);
418 #ifdef RT_FREE_ROUTE_TABLE_STATS
419         croutes++;
420         froutes++;
421         if (croutes > mroutes)
422                 mroutes = croutes;
423 #endif
424 #else
425         free(rt);
426 #endif
427 }
428
429 void
430 rt_freeif(struct interface *ifp)
431 {
432         struct dhcpcd_ctx *ctx;
433         struct rt *rt, *rtn;
434
435         if (ifp == NULL)
436                 return;
437         ctx = ifp->ctx;
438         RB_TREE_FOREACH_SAFE(rt, &ctx->routes, rtn) {
439                 if (rt->rt_ifp == ifp) {
440                         rb_tree_remove_node(&ctx->routes, rt);
441                         rt_free(rt);
442                 }
443         }
444 }
445
446 /* If something other than dhcpcd removes a route,
447  * we need to remove it from our internal table. */
448 void
449 rt_recvrt(int cmd, const struct rt *rt, pid_t pid)
450 {
451         struct dhcpcd_ctx *ctx;
452         struct rt *f;
453
454         assert(rt != NULL);
455         assert(rt->rt_ifp != NULL);
456         assert(rt->rt_ifp->ctx != NULL);
457
458         ctx = rt->rt_ifp->ctx;
459
460         switch(cmd) {
461         case RTM_DELETE:
462                 f = rb_tree_find_node(&ctx->routes, rt);
463                 if (f != NULL) {
464                         char buf[32];
465
466                         rb_tree_remove_node(&ctx->routes, f);
467                         snprintf(buf, sizeof(buf), "pid %d deleted", pid);
468                         rt_desc(buf, f);
469                         rt_free(f);
470                 }
471                 break;
472         }
473
474 #if defined(IPV4LL) && defined(HAVE_ROUTE_METRIC)
475         if (rt->rt_dest.sa_family == AF_INET)
476                 ipv4ll_recvrt(cmd, rt);
477 #endif
478 }
479
480 static bool
481 rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
482 {
483         struct dhcpcd_ctx *ctx;
484         bool change, kroute, result;
485
486         assert(nrt != NULL);
487         ctx = nrt->rt_ifp->ctx;
488
489         /*
490          * Don't install a gateway if not asked to.
491          * This option is mainly for VPN users who want their VPN to be the
492          * default route.
493          * Because VPN's generally don't care about route management
494          * beyond their own, a longer term solution would be to remove this
495          * and get the VPN to inject the default route into dhcpcd somehow.
496          */
497         if (((nrt->rt_ifp->active &&
498             !(nrt->rt_ifp->options->options & DHCPCD_GATEWAY)) ||
499             (!nrt->rt_ifp->active && !(ctx->options & DHCPCD_GATEWAY))) &&
500             sa_is_unspecified(&nrt->rt_dest) &&
501             sa_is_unspecified(&nrt->rt_netmask))
502                 return false;
503
504         rt_desc(ort == NULL ? "adding" : "changing", nrt);
505
506         change = kroute = result = false;
507         if (ort == NULL) {
508                 ort = rb_tree_find_node(kroutes, nrt);
509                 if (ort != NULL &&
510                     ((ort->rt_flags & RTF_REJECT &&
511                       nrt->rt_flags & RTF_REJECT) ||
512                      (ort->rt_ifp == nrt->rt_ifp &&
513 #ifdef HAVE_ROUTE_METRIC
514                     ort->rt_metric == nrt->rt_metric &&
515 #endif
516                     sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)))
517                 {
518                         if (ort->rt_mtu == nrt->rt_mtu)
519                                 return true;
520                         change = true;
521                         kroute = true;
522                 }
523         } else if (ort->rt_dflags & RTDF_FAKE &&
524             !(nrt->rt_dflags & RTDF_FAKE) &&
525             ort->rt_ifp == nrt->rt_ifp &&
526 #ifdef HAVE_ROUTE_METRIC
527             ort->rt_metric == nrt->rt_metric &&
528 #endif
529             sa_cmp(&ort->rt_dest, &nrt->rt_dest) == 0 &&
530             rt_cmp_netmask(ort, nrt) == 0 &&
531             sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)
532         {
533                 if (ort->rt_mtu == nrt->rt_mtu)
534                         return true;
535                 change = true;
536         }
537
538 #ifdef RTF_CLONING
539         /* BSD can set routes to be cloning routes.
540          * Cloned routes inherit the parent flags.
541          * As such, we need to delete and re-add the route to flush children
542          * to correct the flags. */
543         if (change && ort != NULL && ort->rt_flags & RTF_CLONING)
544                 change = false;
545 #endif
546
547         if (change) {
548                 if (if_route(RTM_CHANGE, nrt) != -1) {
549                         result = true;
550                         goto out;
551                 }
552                 if (errno != ESRCH)
553                         logerr("if_route (CHG)");
554         }
555
556 #ifdef HAVE_ROUTE_METRIC
557         /* With route metrics, we can safely add the new route before
558          * deleting the old route. */
559         if (if_route(RTM_ADD, nrt) != -1) {
560                 if (ort != NULL) {
561                         if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
562                                 logerr("if_route (DEL)");
563                 }
564                 result = true;
565                 goto out;
566         }
567
568         /* If the kernel claims the route exists we need to rip out the
569          * old one first. */
570         if (errno != EEXIST || ort == NULL)
571                 goto logerr;
572 #endif
573
574         /* No route metrics, we need to delete the old route before
575          * adding the new one. */
576 #ifdef ROUTE_PER_GATEWAY
577         errno = 0;
578 #endif
579         if (ort != NULL) {
580                 if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
581                         logerr("if_route (DEL)");
582                 else
583                         kroute = false;
584         }
585 #ifdef ROUTE_PER_GATEWAY
586         /* The OS allows many routes to the same dest with different gateways.
587          * dhcpcd does not support this yet, so for the time being just keep on
588          * deleting the route until there is an error. */
589         if (ort != NULL && errno == 0) {
590                 for (;;) {
591                         if (if_route(RTM_DELETE, ort) == -1)
592                                 break;
593                 }
594         }
595 #endif
596
597         if (if_route(RTM_ADD, nrt) != -1) {
598                 result = true;
599                 goto out;
600         }
601
602 #ifdef HAVE_ROUTE_METRIC
603 logerr:
604 #endif
605         logerr("if_route (ADD)");
606
607 out:
608         if (kroute) {
609                 rb_tree_remove_node(kroutes, ort);
610                 rt_free(ort);
611         }
612         return result;
613 }
614
615 static bool
616 rt_delete(struct rt *rt)
617 {
618         int retval;
619
620         rt_desc("deleting", rt);
621         retval = if_route(RTM_DELETE, rt) == -1 ? false : true;
622         if (!retval && errno != ENOENT && errno != ESRCH)
623                 logerr(__func__);
624         return retval;
625 }
626
627 static bool
628 rt_cmp(const struct rt *r1, const struct rt *r2)
629 {
630
631         return (r1->rt_ifp == r2->rt_ifp &&
632 #ifdef HAVE_ROUTE_METRIC
633             r1->rt_metric == r2->rt_metric &&
634 #endif
635             sa_cmp(&r1->rt_gateway, &r2->rt_gateway) == 0);
636 }
637
638 static bool
639 rt_doroute(rb_tree_t *kroutes, struct rt *rt)
640 {
641         struct dhcpcd_ctx *ctx;
642         struct rt *or;
643
644         ctx = rt->rt_ifp->ctx;
645         /* Do we already manage it? */
646         or = rb_tree_find_node(&ctx->routes, rt);
647         if (or != NULL) {
648                 if (rt->rt_dflags & RTDF_FAKE)
649                         return true;
650                 if (or->rt_dflags & RTDF_FAKE ||
651                     !rt_cmp(rt, or) ||
652                     (rt->rt_ifa.sa_family != AF_UNSPEC &&
653                     sa_cmp(&or->rt_ifa, &rt->rt_ifa) != 0) ||
654                     or->rt_mtu != rt->rt_mtu)
655                 {
656                         if (!rt_add(kroutes, rt, or))
657                                 return false;
658                 }
659                 rb_tree_remove_node(&ctx->routes, or);
660                 rt_free(or);
661         } else {
662                 if (rt->rt_dflags & RTDF_FAKE) {
663                         or = rb_tree_find_node(kroutes, rt);
664                         if (or == NULL)
665                                 return false;
666                         if (!rt_cmp(rt, or))
667                                 return false;
668                 } else {
669                         if (!rt_add(kroutes, rt, NULL))
670                                 return false;
671                 }
672         }
673
674         return true;
675 }
676
677 void
678 rt_build(struct dhcpcd_ctx *ctx, int af)
679 {
680         rb_tree_t routes, added, kroutes;
681         struct rt *rt, *rtn;
682         unsigned long long o;
683
684         rb_tree_init(&routes, &rt_compare_proto_ops);
685         rb_tree_init(&added, &rt_compare_os_ops);
686         rb_tree_init(&kroutes, &rt_compare_os_ops);
687         if_initrt(ctx, &kroutes, af);
688         ctx->rt_order = 0;
689         ctx->options |= DHCPCD_RTBUILD;
690
691         switch (af) {
692 #ifdef INET
693         case AF_INET:
694                 if (!inet_getroutes(ctx, &routes))
695                         goto getfail;
696                 break;
697 #endif
698 #ifdef INET6
699         case AF_INET6:
700                 if (!inet6_getroutes(ctx, &routes))
701                         goto getfail;
702                 break;
703 #endif
704         }
705
706         RB_TREE_FOREACH_SAFE(rt, &routes, rtn) {
707                 if ((rt->rt_dest.sa_family != af &&
708                     rt->rt_dest.sa_family != AF_UNSPEC) ||
709                     (rt->rt_gateway.sa_family != af &&
710                     rt->rt_gateway.sa_family != AF_UNSPEC))
711                         continue;
712                 /* Is this route already in our table? */
713                 if (rb_tree_find_node(&added, rt) != NULL)
714                         continue;
715                 if (rt_doroute(&kroutes, rt)) {
716                         rb_tree_remove_node(&routes, rt);
717                         if (rb_tree_insert_node(&added, rt) != rt) {
718                                 errno = EEXIST;
719                                 logerr(__func__);
720                                 rt_free(rt);
721                         }
722                 }
723         }
724
725         /* Remove old routes we used to manage. */
726         RB_TREE_FOREACH_REVERSE_SAFE(rt, &ctx->routes, rtn) {
727                 if ((rt->rt_dest.sa_family != af &&
728                     rt->rt_dest.sa_family != AF_UNSPEC) ||
729                     (rt->rt_gateway.sa_family != af &&
730                     rt->rt_gateway.sa_family != AF_UNSPEC))
731                         continue;
732                 rb_tree_remove_node(&ctx->routes, rt);
733                 if (rb_tree_find_node(&added, rt) == NULL) {
734                         o = rt->rt_ifp->options ?
735                             rt->rt_ifp->options->options :
736                             ctx->options;
737                         if ((o &
738                                 (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
739                                 (DHCPCD_EXITING | DHCPCD_PERSISTENT))
740                                 rt_delete(rt);
741                 }
742                 rt_free(rt);
743         }
744
745         /* XXX This needs to be optimised. */
746         while ((rt = RB_TREE_MIN(&added)) != NULL) {
747                 rb_tree_remove_node(&added, rt);
748                 if (rb_tree_insert_node(&ctx->routes, rt) != rt) {
749                         errno = EEXIST;
750                         logerr(__func__);
751                         rt_free(rt);
752                 }
753         }
754
755
756 getfail:
757         rt_headclear(&routes, AF_UNSPEC);
758         rt_headclear(&kroutes, AF_UNSPEC);
759 }