1 /* SPDX-License-Identifier: BSD-2-Clause */
3 * dhcpcd - route management
4 * Copyright (c) 2006-2019 Roy Marples <roy@marples.name>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
42 #include "if-options.h"
50 /* Needed for NetBSD-6, 7 and 8. */
51 #ifndef RB_TREE_FOREACH_SAFE
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)
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); \
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); \
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;
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;
84 dst->sa_len = addr->sa_len;
87 if (sa_is_unspecified(netmask)) {
89 memcpy(dstp, addrp, (size_t)(addre - dstp));
93 while (dstp < netmaske)
94 *dstp++ = *addrp++ & *netmaskp++;
96 memset(dstp, 0, (size_t)(addre - dstp));
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
117 rt_cmp_netmask(const struct rt *rt1, const struct rt *rt2)
120 if (rt1->rt_flags & RTF_HOST && rt2->rt_flags & RTF_HOST)
122 return sa_cmp(&rt1->rt_netmask, &rt2->rt_netmask);
126 rt_compare_os(__unused void *context, const void *node1, const void *node2)
128 const struct rt *rt1 = node1, *rt2 = node2;
131 /* Sort by masked destination. */
132 c = rt_cmp_dest(rt1, rt2);
136 #ifdef HAVE_ROUTE_METRIC
137 c = (int)(rt1->rt_ifp->metric - rt2->rt_ifp->metric);
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)
149 if (rt1->rt_order < rt2->rt_order)
155 rt_compare_proto(void *context, const void *node1, const void *node2)
157 const struct rt *rt1 = node1, *rt2 = node2;
159 struct interface *ifp1, *ifp2;
161 assert(rt1->rt_ifp != NULL);
162 assert(rt2->rt_ifp != NULL);
166 /* Prefer interfaces with a carrier. */
167 c = ifp1->carrier - ifp2->carrier;
171 /* Lower metric interfaces come first. */
172 c = (int)(ifp1->metric - ifp2->metric);
176 /* Finally the order in which the route was given to us. */
177 return rt_compare_list(context, rt1, rt2);
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),
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),
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),
201 #ifdef RT_FREE_ROUTE_TABLE
203 rt_compare_free(__unused void *context, const void *node1, const void *node2)
206 return node1 == node2 ? 0 : node1 < node2 ? -1 : 1;
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),
218 rt_init(struct dhcpcd_ctx *ctx)
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);
228 rt_is_default(const struct rt *rt)
231 return sa_is_unspecified(&rt->rt_dest) &&
232 sa_is_unspecified(&rt->rt_netmask);
236 rt_desc(const char *cmd, const struct rt *rt)
238 char dest[INET_MAX_ADDRSTRLEN], gateway[INET_MAX_ADDRSTRLEN];
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;
252 if (rt->rt_flags & RTF_HOST) {
254 loginfox("%s: %s host route to %s",
257 loginfox("%s: %s host route to %s via %s",
258 ifname, cmd, dest, gateway);
259 } else if (rt_is_default(rt)) {
261 loginfox("%s: %s default route",
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",
269 rt->rt_flags & RTF_REJECT ? " reject" : "",
272 loginfox("%s: %s%s route to %s/%d via %s",
274 rt->rt_flags & RTF_REJECT ? " reject" : "",
275 dest, prefix, gateway);
279 rt_headclear0(struct dhcpcd_ctx *ctx, rb_tree_t *rts, int af)
286 #ifdef RT_FREE_ROUTE_TABLE
287 assert(&ctx->froutes != rts);
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)
295 rb_tree_remove_node(rts, rt);
301 rt_headclear(rb_tree_t *rts, int af)
305 if (rts == NULL || (rt = RB_TREE_MIN(rts)) == NULL)
307 rt_headclear0(rt->rt_ifp->ctx, rts, af);
311 rt_headfree(rb_tree_t *rts)
315 while ((rt = RB_TREE_MIN(rts)) != NULL) {
316 rb_tree_remove_node(rts, rt);
322 rt_dispose(struct dhcpcd_ctx *ctx)
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);
338 rt_new0(struct dhcpcd_ctx *ctx)
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
352 if ((rt = malloc(sizeof(*rt))) == NULL) {
356 memset(rt, 0, sizeof(*rt));
361 rt_setif(struct rt *rt, struct interface *ifp)
367 #ifdef HAVE_ROUTE_METRIC
368 rt->rt_metric = ifp->metric;
373 rt_new(struct interface *ifp)
378 if ((rt = rt_new0(ifp->ctx)) == NULL)
385 rt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx)
388 rt->rt_order = ctx->rt_order++;
389 if (rb_tree_insert_node(tree, rt) == rt)
397 rt_proto_add(rb_tree_t *tree, struct rt *rt)
400 assert (rt->rt_ifp != NULL);
401 return rt_proto_add_ctx(tree, rt, rt->rt_ifp->ctx);
405 rt_free(struct rt *rt)
407 #ifdef RT_FREE_ROUTE_TABLE
408 struct dhcpcd_ctx *ctx;
411 if (rt->rt_ifp == NULL) {
416 ctx = rt->rt_ifp->ctx;
417 rb_tree_insert_node(&ctx->froutes, rt);
418 #ifdef RT_FREE_ROUTE_TABLE_STATS
421 if (croutes > mroutes)
430 rt_freeif(struct interface *ifp)
432 struct dhcpcd_ctx *ctx;
438 RB_TREE_FOREACH_SAFE(rt, &ctx->routes, rtn) {
439 if (rt->rt_ifp == ifp) {
440 rb_tree_remove_node(&ctx->routes, rt);
446 /* If something other than dhcpcd removes a route,
447 * we need to remove it from our internal table. */
449 rt_recvrt(int cmd, const struct rt *rt, pid_t pid)
451 struct dhcpcd_ctx *ctx;
455 assert(rt->rt_ifp != NULL);
456 assert(rt->rt_ifp->ctx != NULL);
458 ctx = rt->rt_ifp->ctx;
462 f = rb_tree_find_node(&ctx->routes, rt);
466 rb_tree_remove_node(&ctx->routes, f);
467 snprintf(buf, sizeof(buf), "pid %d deleted", pid);
474 #if defined(IPV4LL) && defined(HAVE_ROUTE_METRIC)
475 if (rt->rt_dest.sa_family == AF_INET)
476 ipv4ll_recvrt(cmd, rt);
481 rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
483 struct dhcpcd_ctx *ctx;
484 bool change, kroute, result;
487 ctx = nrt->rt_ifp->ctx;
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
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.
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))
504 rt_desc(ort == NULL ? "adding" : "changing", nrt);
506 change = kroute = result = false;
508 ort = rb_tree_find_node(kroutes, nrt);
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 &&
516 sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)))
518 if (ort->rt_mtu == nrt->rt_mtu)
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 &&
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)
533 if (ort->rt_mtu == nrt->rt_mtu)
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)
548 if (if_route(RTM_CHANGE, nrt) != -1) {
553 logerr("if_route (CHG)");
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) {
561 if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
562 logerr("if_route (DEL)");
568 /* If the kernel claims the route exists we need to rip out the
570 if (errno != EEXIST || ort == NULL)
574 /* No route metrics, we need to delete the old route before
575 * adding the new one. */
576 #ifdef ROUTE_PER_GATEWAY
580 if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
581 logerr("if_route (DEL)");
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) {
591 if (if_route(RTM_DELETE, ort) == -1)
597 if (if_route(RTM_ADD, nrt) != -1) {
602 #ifdef HAVE_ROUTE_METRIC
605 logerr("if_route (ADD)");
609 rb_tree_remove_node(kroutes, ort);
616 rt_delete(struct rt *rt)
620 rt_desc("deleting", rt);
621 retval = if_route(RTM_DELETE, rt) == -1 ? false : true;
622 if (!retval && errno != ENOENT && errno != ESRCH)
628 rt_cmp(const struct rt *r1, const struct rt *r2)
631 return (r1->rt_ifp == r2->rt_ifp &&
632 #ifdef HAVE_ROUTE_METRIC
633 r1->rt_metric == r2->rt_metric &&
635 sa_cmp(&r1->rt_gateway, &r2->rt_gateway) == 0);
639 rt_doroute(rb_tree_t *kroutes, struct rt *rt)
641 struct dhcpcd_ctx *ctx;
644 ctx = rt->rt_ifp->ctx;
645 /* Do we already manage it? */
646 or = rb_tree_find_node(&ctx->routes, rt);
648 if (rt->rt_dflags & RTDF_FAKE)
650 if (or->rt_dflags & RTDF_FAKE ||
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)
656 if (!rt_add(kroutes, rt, or))
659 rb_tree_remove_node(&ctx->routes, or);
662 if (rt->rt_dflags & RTDF_FAKE) {
663 or = rb_tree_find_node(kroutes, rt);
669 if (!rt_add(kroutes, rt, NULL))
678 rt_build(struct dhcpcd_ctx *ctx, int af)
680 rb_tree_t routes, added, kroutes;
682 unsigned long long o;
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);
689 ctx->options |= DHCPCD_RTBUILD;
694 if (!inet_getroutes(ctx, &routes))
700 if (!inet6_getroutes(ctx, &routes))
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))
712 /* Is this route already in our table? */
713 if (rb_tree_find_node(&added, rt) != NULL)
715 if (rt_doroute(&kroutes, rt)) {
716 rb_tree_remove_node(&routes, rt);
717 if (rb_tree_insert_node(&added, rt) != rt) {
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))
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 :
738 (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
739 (DHCPCD_EXITING | DHCPCD_PERSISTENT))
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) {
757 rt_headclear(&routes, AF_UNSPEC);
758 rt_headclear(&kroutes, AF_UNSPEC);