From: Roy Marples Date: Sun, 3 Mar 2019 19:52:57 +0000 (+0000) Subject: Replace route TAILQ macros with rbtree(3) from NetBSD. X-Git-Tag: v8.0.0~90 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b2ae620502e57bc9ec9e7c1b5816bc89a0343d6b;p=thirdparty%2Fdhcpcd.git Replace route TAILQ macros with rbtree(3) from NetBSD. This not only reduces the binary size of dhcpcd by ~5k on NetBSD/amd64, but also increases the performance of dhcpcd on systems with large routing tables. There should be more room for improvement as we can now call find functions on the tree instead of walking it ourself if we can generate a suitable key. This has been greatly inspired from a similar patch from Donald Sharp which used the more generic RB_ macros found in the BSD tree(3). Not ready for production use because routes are now sorted upon insertion so we need to ensure rt_compare is 100% correct as it's no longer in the order supplied by DHCP. Lastly, portability gunk needs to be added. --- diff --git a/src/dhcp.c b/src/dhcp.c index d0f9a453..b77eda16 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -402,7 +402,7 @@ decode_rfc3442(char *out, size_t len, const uint8_t *p, size_t pl) } static int -decode_rfc3442_rt(struct rt_head *routes, struct interface *ifp, +decode_rfc3442_rt(rb_tree_t *routes, struct interface *ifp, const uint8_t *data, size_t dl, const struct bootp *bootp) { const uint8_t *p = data; @@ -465,8 +465,7 @@ decode_rfc3442_rt(struct rt_head *routes, struct interface *ifp, sa_in_init(&rt->rt_dest, &dest); sa_in_init(&rt->rt_netmask, &netmask); sa_in_init(&rt->rt_gateway, &gateway); - - TAILQ_INSERT_TAIL(routes, rt, rt_next); + rb_tree_insert_node(routes, rt); n++; } return n; @@ -579,7 +578,7 @@ route_netmask(uint32_t ip_in) * If we have a CSR then we only use that. * Otherwise we add static routes and then routers. */ static int -get_option_routes(struct rt_head *routes, struct interface *ifp, +get_option_routes(rb_tree_t *routes, struct interface *ifp, const struct bootp *bootp, size_t bootp_len) { struct if_options *ifo = ifp->options; @@ -654,8 +653,7 @@ get_option_routes(struct rt_head *routes, struct interface *ifp, sa_in_init(&rt->rt_dest, &dest); sa_in_init(&rt->rt_netmask, &netmask); sa_in_init(&rt->rt_gateway, &gateway); - - TAILQ_INSERT_TAIL(routes, rt, rt_next); + rb_tree_insert_node(routes, rt); n++; } } @@ -677,7 +675,7 @@ get_option_routes(struct rt_head *routes, struct interface *ifp, sa_in_init(&rt->rt_dest, &dest); sa_in_init(&rt->rt_netmask, &netmask); sa_in_init(&rt->rt_gateway, &gateway); - TAILQ_INSERT_TAIL(routes, rt, rt_next); + rb_tree_insert_node(routes, rt); n++; } } @@ -705,7 +703,7 @@ dhcp_get_mtu(const struct interface *ifp) /* Grab our routers from the DHCP message and apply any MTU value * the message contains */ int -dhcp_get_routes(struct rt_head *routes, struct interface *ifp) +dhcp_get_routes(rb_tree_t *routes, struct interface *ifp) { const struct dhcp_state *state; diff --git a/src/dhcp.h b/src/dhcp.h index 19da2162..245d20a4 100644 --- a/src/dhcp.h +++ b/src/dhcp.h @@ -251,7 +251,7 @@ ssize_t decode_rfc3442(char *, size_t, const uint8_t *p, size_t); void dhcp_printoptions(const struct dhcpcd_ctx *, const struct dhcp_opt *, size_t); uint16_t dhcp_get_mtu(const struct interface *); -int dhcp_get_routes(struct rt_head *, struct interface *); +int dhcp_get_routes(rb_tree_t *, struct interface *); ssize_t dhcp_env(char **, const char *, const struct bootp *, size_t, const struct interface *); diff --git a/src/dhcpcd.h b/src/dhcpcd.h index 02b830c0..a09f40fb 100644 --- a/src/dhcpcd.h +++ b/src/dhcpcd.h @@ -137,9 +137,9 @@ struct dhcpcd_ctx { size_t duid_len; struct if_head *ifaces; - struct rt_head routes; /* our routes */ - struct rt_head kroutes; /* all kernel routes */ - struct rt_head froutes; /* free routes for re-use */ + rb_tree_t routes; /* our routes */ + rb_tree_t kroutes; /* all kernel routes */ + rb_tree_t froutes; /* free routes for re-use */ int pf_inet_fd; void *priv; diff --git a/src/if-options.c b/src/if-options.c index 06c47b05..ccd5b545 100644 --- a/src/if-options.c +++ b/src/if-options.c @@ -1110,7 +1110,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, sa_in_init(&rt->rt_dest, &addr); sa_in_init(&rt->rt_netmask, &addr2); sa_in_init(&rt->rt_gateway, &addr3); - TAILQ_INSERT_TAIL(&ifo->routes, rt, rt_next); + rb_tree_insert_node(&ifo->routes, rt); *fp = ' '; } else if (strncmp(arg, "routers=", strlen("routers=")) == 0) { if (parse_addr(&addr, NULL, p) == -1) @@ -1121,7 +1121,7 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo, sa_in_init(&rt->rt_dest, &addr2); sa_in_init(&rt->rt_netmask, &addr2); sa_in_init(&rt->rt_gateway, &addr); - TAILQ_INSERT_TAIL(&ifo->routes, rt, rt_next); + rb_tree_insert_node(&ifo->routes, rt); } else if (strncmp(arg, "interface_mtu=", strlen("interface_mtu=")) == 0 || strncmp(arg, "mtu=", strlen("mtu=")) == 0) @@ -2279,7 +2279,7 @@ default_config(struct dhcpcd_ctx *ctx) ifo->reboot = DEFAULT_REBOOT; ifo->metric = -1; ifo->auth.options |= DHCPCD_AUTH_REQUIRE; - TAILQ_INIT(&ifo->routes); + rb_tree_init(&ifo->routes, &rt_rb_tree_ops); #ifdef AUTH TAILQ_INIT(&ifo->auth.tokens); #endif diff --git a/src/if-options.h b/src/if-options.h index eddfeef9..1b0988f3 100644 --- a/src/if-options.h +++ b/src/if-options.h @@ -181,7 +181,7 @@ struct if_options { struct in_addr req_addr; struct in_addr req_mask; struct in_addr req_brd; - struct rt_head routes; + rb_tree_t routes; struct in6_addr req_addr6; uint8_t req_prefix_len; unsigned int mtu; diff --git a/src/ipv4.c b/src/ipv4.c index 57f70a77..952b24fe 100644 --- a/src/ipv4.c +++ b/src/ipv4.c @@ -247,10 +247,10 @@ ipv4_ifcmp(const struct interface *si, const struct interface *ti) } static int -inet_dhcproutes(struct rt_head *routes, struct interface *ifp) +inet_dhcproutes(rb_tree_t *routes, struct interface *ifp) { const struct dhcp_state *state; - struct rt_head nroutes; + rb_tree_t nroutes; struct rt *rt, *r = NULL; struct in_addr in; uint16_t mtu; @@ -263,7 +263,7 @@ inet_dhcproutes(struct rt_head *routes, struct interface *ifp) /* An address does have to exist. */ assert(state->addr); - TAILQ_INIT(&nroutes); + rb_tree_init(&nroutes, &rt_rb_tree_ops); /* First, add a subnet route. */ if (!(ifp->flags & IFF_POINTOPOINT) && @@ -283,12 +283,12 @@ inet_dhcproutes(struct rt_head *routes, struct interface *ifp) //in.s_addr = INADDR_ANY; //sa_in_init(&rt->rt_gateway, &in); rt->rt_gateway.sa_family = AF_UNSPEC; - TAILQ_INSERT_HEAD(&nroutes, rt, rt_next); + rb_tree_insert_node(&nroutes, rt); } /* If any set routes, grab them, otherwise DHCP routes. */ - if (TAILQ_FIRST(&ifp->options->routes)) { - TAILQ_FOREACH(r, &ifp->options->routes, rt_next) { + if (RB_TREE_MIN(&ifp->options->routes)) { + RB_TREE_FOREACH(r, &ifp->options->routes) { if (sa_is_unspecified(&r->rt_gateway)) break; if ((rt = rt_new0(ifp->ctx)) == NULL) @@ -296,7 +296,7 @@ inet_dhcproutes(struct rt_head *routes, struct interface *ifp) memcpy(rt, r, sizeof(*rt)); rt_setif(rt, ifp); rt->rt_dflags = RTDF_STATIC; - TAILQ_INSERT_TAIL(&nroutes, rt, rt_next); + rb_tree_insert_node(&nroutes, rt); } } else { if (dhcp_get_routes(&nroutes, ifp) == -1) @@ -315,20 +315,21 @@ inet_dhcproutes(struct rt_head *routes, struct interface *ifp) sa_in_init(&rt->rt_netmask, &in); sa_in_init(&rt->rt_gateway, &state->addr->brd); sa_in_init(&rt->rt_ifa, &state->addr->addr); - TAILQ_INSERT_HEAD(routes, rt, rt_next); + rb_tree_insert_node(&nroutes, rt); } /* Copy our address as the source address and set mtu */ mtu = dhcp_get_mtu(ifp); n = 0; - TAILQ_FOREACH(rt, &nroutes, rt_next) { + while ((rt = RB_TREE_MIN(&nroutes)) != NULL) { + rb_tree_remove_node(&nroutes, rt); rt->rt_mtu = mtu; if (!(rt->rt_dflags & RTDF_STATIC)) rt->rt_dflags |= RTDF_DHCP; sa_in_init(&rt->rt_ifa, &state->addr->addr); + rb_tree_insert_node(routes, rt); n++; } - TAILQ_CONCAT(routes, &nroutes, rt_next); return n; } @@ -336,7 +337,7 @@ inet_dhcproutes(struct rt_head *routes, struct interface *ifp) /* We should check to ensure the routers are on the same subnet * OR supply a host route. If not, warn and add a host route. */ static int -inet_routerhostroute(struct rt_head *routes, struct interface *ifp) +inet_routerhostroute(rb_tree_t *routes, struct interface *ifp) { struct rt *rt, *rth; struct sockaddr_in *dest, *netmask, *gateway; @@ -349,7 +350,7 @@ inet_routerhostroute(struct rt_head *routes, struct interface *ifp) if (ifp->flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) return 0; - TAILQ_FOREACH(rt, routes, rt_next) { + RB_TREE_FOREACH(rt, routes) { if (rt->rt_dest.sa_family != AF_INET) continue; if (!sa_is_unspecified(&rt->rt_dest) || @@ -357,13 +358,14 @@ inet_routerhostroute(struct rt_head *routes, struct interface *ifp) continue; gateway = satosin(&rt->rt_gateway); /* Scan for a route to match */ - TAILQ_FOREACH(rth, routes, rt_next) { + RB_TREE_FOREACH(rth, routes) { if (rth == rt) break; /* match host */ if (sa_cmp(&rth->rt_dest, &rt->rt_gateway) == 0) break; /* match subnet */ + /* XXX ADD TO RT_COMARE? XXX */ cp = (const char *)&gateway->sin_addr.s_addr; dest = satosin(&rth->rt_dest); cp2 = (const char *)&dest->sin_addr.s_addr; @@ -418,13 +420,13 @@ inet_routerhostroute(struct rt_head *routes, struct interface *ifp) sa_in_init(&rth->rt_gateway, &in); rth->rt_mtu = dhcp_get_mtu(ifp); sa_in_init(&rth->rt_ifa, &state->addr->addr); - TAILQ_INSERT_BEFORE(rt, rth, rt_next); + rb_tree_insert_node(routes, rth); } return 0; } bool -inet_getroutes(struct dhcpcd_ctx *ctx, struct rt_head *routes) +inet_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes) { struct interface *ifp; #ifdef IPV4LL diff --git a/src/ipv4.h b/src/ipv4.h index 496b8ecd..1f8c3a30 100644 --- a/src/ipv4.h +++ b/src/ipv4.h @@ -116,7 +116,7 @@ int inet_cidrtoaddr(int, struct in_addr *); uint32_t ipv4_getnetmask(uint32_t); int ipv4_hasaddr(const struct interface *); -bool inet_getroutes(struct dhcpcd_ctx *, struct rt_head *); +bool inet_getroutes(struct dhcpcd_ctx *, rb_tree_t *); #define STATE_ADDED 0x01 #define STATE_FAKE 0x02 diff --git a/src/ipv6.c b/src/ipv6.c index d7c73c22..f078972c 100644 --- a/src/ipv6.c +++ b/src/ipv6.c @@ -2208,7 +2208,7 @@ inet6_makerouter(struct ra *rap) IN6_ARE_ADDR_EQUAL(&((rtp)->mask), &in6addr_any)) static int -inet6_staticroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx) +inet6_staticroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx) { struct interface *ifp; struct ipv6_state *state; @@ -2224,7 +2224,7 @@ inet6_staticroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx) { rt = inet6_makeprefix(ifp, NULL, ia); if (rt) - TAILQ_INSERT_TAIL(routes, rt, rt_next); + rb_tree_insert_node(routes, rt); } } } @@ -2232,7 +2232,7 @@ inet6_staticroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx) } static int -inet6_raroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx, int expired, +inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx, int expired, bool *have_default) { struct rt *rt; @@ -2248,14 +2248,14 @@ inet6_raroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx, int expired, rt = inet6_makeprefix(rap->iface, rap, addr); if (rt) { rt->rt_dflags |= RTDF_RA; - TAILQ_INSERT_TAIL(routes, rt, rt_next); + rb_tree_insert_node(routes, rt); } } if (rap->lifetime) { rt = inet6_makerouter(rap); if (rt) { rt->rt_dflags |= RTDF_RA; - TAILQ_INSERT_TAIL(routes, rt, rt_next); + rb_tree_insert_node(routes, rt); if (have_default) *have_default = true; } @@ -2266,7 +2266,7 @@ inet6_raroutes(struct rt_head *routes, struct dhcpcd_ctx *ctx, int expired, #ifdef DHCP6 static int -inet6_dhcproutes(struct rt_head *routes, struct dhcpcd_ctx *ctx, +inet6_dhcproutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx, enum DH6S dstate) { struct interface *ifp; @@ -2279,10 +2279,10 @@ inet6_dhcproutes(struct rt_head *routes, struct dhcpcd_ctx *ctx, if (d6_state && d6_state->state == dstate) { TAILQ_FOREACH(addr, &d6_state->addrs, next) { rt = inet6_makeprefix(ifp, NULL, addr); - if (rt) { - rt->rt_dflags |= RTDF_DHCP; - TAILQ_INSERT_TAIL(routes, rt, rt_next); - } + if (rt == NULL) + continue; + rt->rt_dflags |= RTDF_DHCP; + rb_tree_insert_node(routes, rt); } } } @@ -2291,7 +2291,7 @@ inet6_dhcproutes(struct rt_head *routes, struct dhcpcd_ctx *ctx, #endif bool -inet6_getroutes(struct dhcpcd_ctx *ctx, struct rt_head *routes) +inet6_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes) { bool have_default; diff --git a/src/ipv6.h b/src/ipv6.h index bc48d42e..4a9d5b5d 100644 --- a/src/ipv6.h +++ b/src/ipv6.h @@ -281,7 +281,7 @@ int ipv6_staticdadcompleted(const struct interface *); int ipv6_startstatic(struct interface *); ssize_t ipv6_env(char **, const char *, const struct interface *); void ipv6_ctxfree(struct dhcpcd_ctx *); -bool inet6_getroutes(struct dhcpcd_ctx *, struct rt_head *); +bool inet6_getroutes(struct dhcpcd_ctx *, rb_tree_t *); #endif /* INET6 */ #endif /* INET6_H */ diff --git a/src/route.c b/src/route.c index c5f790f9..666a6097 100644 --- a/src/route.c +++ b/src/route.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,54 @@ #include "route.h" #include "sa.h" +static int +rt_compare(__unused void *context, const void *node1, const void *node2) +{ + const struct rt *rt1 = node1, *rt2 = node2; + bool rt1u, rt2u; + int c; + + /* Default routes come last. */ + rt1u = !(rt1->rt_flags & RTF_HOST) && + sa_is_unspecified(&rt1->rt_dest) && + sa_is_unspecified(&rt1->rt_netmask); + rt2u = !(rt2->rt_flags & RTF_HOST) && + sa_is_unspecified(&rt2->rt_dest) && + sa_is_unspecified(&rt2->rt_netmask); + if (rt1u != rt2u) + return rt1u ? 1 : -1; + + /* Sort by destination and netmask. */ + c = sa_cmp(&rt1->rt_dest, &rt2->rt_dest); + if (c != 0) + return c; + c = sa_cmp(&rt1->rt_netmask, &rt2->rt_netmask); + if (c != 0) + return c; + + /* Finally by interface metric. */ + if (rt1->rt_ifp != NULL && rt2->rt_ifp != NULL) + c = (int)(rt1->rt_ifp->metric - rt2->rt_ifp->metric); + + return c; +} + +const rb_tree_ops_t rt_rb_tree_ops = { + .rbto_compare_nodes = rt_compare, + .rbto_compare_key = rt_compare, + .rbto_node_offset = offsetof(struct rt, rt_tree), + .rbto_context = NULL +}; + +void +rt_init(struct dhcpcd_ctx *ctx) +{ + + rb_tree_init(&ctx->routes, &rt_rb_tree_ops); + rb_tree_init(&ctx->kroutes, &rt_rb_tree_ops); + rb_tree_init(&ctx->froutes, &rt_rb_tree_ops); +} + /* * On some systems, host routes have no need for a netmask. * However DHCP specifies host routes using an all-ones netmask. @@ -59,15 +108,6 @@ rt_cmp_netmask(const struct rt *rt1, const struct rt *rt2) return sa_cmp(&rt1->rt_netmask, &rt2->rt_netmask); } -void -rt_init(struct dhcpcd_ctx *ctx) -{ - - TAILQ_INIT(&ctx->routes); - TAILQ_INIT(&ctx->kroutes); - TAILQ_INIT(&ctx->froutes); -} - static void rt_desc(const char *cmd, const struct rt *rt) { @@ -114,7 +154,7 @@ rt_desc(const char *cmd, const struct rt *rt) } void -rt_headclear0(struct dhcpcd_ctx *ctx, struct rt_head *rts, int af) +rt_headclear0(struct dhcpcd_ctx *ctx, rb_tree_t *rts, int af) { struct rt *rt, *rtn; @@ -123,33 +163,33 @@ rt_headclear0(struct dhcpcd_ctx *ctx, struct rt_head *rts, int af) assert(ctx != NULL); assert(&ctx->froutes != rts); - TAILQ_FOREACH_SAFE(rt, rts, rt_next, rtn) { + RB_TREE_FOREACH_SAFE(rt, rts, rtn) { if (af != AF_UNSPEC && rt->rt_dest.sa_family != af && rt->rt_gateway.sa_family != af) continue; - TAILQ_REMOVE(rts, rt, rt_next); - TAILQ_INSERT_TAIL(&ctx->froutes, rt, rt_next); + rb_tree_remove_node(rts, rt); + rt_free(rt); } } void -rt_headclear(struct rt_head *rts, int af) +rt_headclear(rb_tree_t *rts, int af) { struct rt *rt; - if (rts == NULL || (rt = TAILQ_FIRST(rts)) == NULL) + if (rts == NULL || (rt = RB_TREE_MIN(rts)) == NULL) return; rt_headclear0(rt->rt_ifp->ctx, rts, af); } static void -rt_headfree(struct rt_head *rts) +rt_headfree(rb_tree_t *rts) { struct rt *rt; - while ((rt = TAILQ_FIRST(rts))) { - TAILQ_REMOVE(rts, rt, rt_next); + while ((rt = RB_TREE_MIN(rts)) != NULL) { + rb_tree_remove_node(rts, rt); free(rt); } } @@ -170,8 +210,8 @@ rt_new0(struct dhcpcd_ctx *ctx) struct rt *rt; assert(ctx != NULL); - if ((rt = TAILQ_FIRST(&ctx->froutes)) != NULL) - TAILQ_REMOVE(&ctx->froutes, rt, rt_next); + if ((rt = RB_TREE_MIN(&ctx->froutes)) != NULL) + rb_tree_remove_node(&ctx->froutes, rt); else if ((rt = malloc(sizeof(*rt))) == NULL) { logerr(__func__); return NULL; @@ -207,10 +247,15 @@ rt_new(struct interface *ifp) void rt_free(struct rt *rt) { + struct dhcpcd_ctx *ctx; assert(rt != NULL); + assert(rt->rt_ifp != NULL); assert(rt->rt_ifp->ctx != NULL); - TAILQ_INSERT_TAIL(&rt->rt_ifp->ctx->froutes, rt, rt_next); + + ctx = rt->rt_ifp->ctx; + rt->rt_ifp = NULL; + rb_tree_insert_node(&ctx->froutes, rt); } void @@ -222,28 +267,28 @@ rt_freeif(struct interface *ifp) if (ifp == NULL) return; ctx = ifp->ctx; - TAILQ_FOREACH_SAFE(rt, &ctx->routes, rt_next, rtn) { + RB_TREE_FOREACH_SAFE(rt, &ctx->routes, rtn) { if (rt->rt_ifp == ifp) { - TAILQ_REMOVE(&ctx->routes, rt, rt_next); + rb_tree_remove_node(&ctx->routes, rt); rt_free(rt); } } - TAILQ_FOREACH_SAFE(rt, &ctx->kroutes, rt_next, rtn) { + RB_TREE_FOREACH_SAFE(rt, &ctx->kroutes, rtn) { if (rt->rt_ifp == ifp) { - TAILQ_REMOVE(&ctx->kroutes, rt, rt_next); + rb_tree_remove_node(&ctx->kroutes, rt); rt_free(rt); } } } struct rt * -rt_find(struct rt_head *rts, const struct rt *f) +rt_find(rb_tree_t *rts, const struct rt *f) { struct rt *rt; assert(rts != NULL); assert(f != NULL); - TAILQ_FOREACH(rt, rts, rt_next) { + RB_TREE_FOREACH(rt, rts) { if (sa_cmp(&rt->rt_dest, &f->rt_dest) == 0 && #ifdef HAVE_ROUTE_METRIC (f->rt_ifp == NULL || @@ -264,7 +309,7 @@ rt_kfree(struct rt *rt) assert(rt != NULL); ctx = rt->rt_ifp->ctx; if ((f = rt_find(&ctx->kroutes, rt)) != NULL) { - TAILQ_REMOVE(&ctx->kroutes, f, rt_next); + rb_tree_remove_node(&ctx->kroutes, f); rt_free(f); } } @@ -284,11 +329,11 @@ rt_recvrt(int cmd, const struct rt *rt) switch(cmd) { case RTM_DELETE: if (f != NULL) { - TAILQ_REMOVE(&ctx->kroutes, f, rt_next); + rb_tree_remove_node(&ctx->kroutes, f); rt_free(f); } if ((f = rt_find(&ctx->routes, rt)) != NULL) { - TAILQ_REMOVE(&ctx->routes, f, rt_next); + rb_tree_remove_node(&ctx->routes, f); rt_desc("deleted", f); rt_free(f); } @@ -299,7 +344,7 @@ rt_recvrt(int cmd, const struct rt *rt) if ((f = rt_new(rt->rt_ifp)) == NULL) break; memcpy(f, rt, sizeof(*f)); - TAILQ_INSERT_TAIL(&ctx->kroutes, f, rt_next); + rb_tree_insert_node(&ctx->kroutes, f); break; } @@ -478,7 +523,7 @@ rt_doroute(struct rt *rt) if (!rt_add(rt, or)) return false; } - TAILQ_REMOVE(&ctx->routes, or, rt_next); + rb_tree_remove_node(&ctx->routes, or); rt_free(or); } else { if (rt->rt_dflags & RTDF_FAKE) { @@ -498,7 +543,7 @@ rt_doroute(struct rt *rt) void rt_build(struct dhcpcd_ctx *ctx, int af) { - struct rt_head routes, added; + rb_tree_t routes, added; struct rt *rt, *rtn; unsigned long long o; @@ -506,8 +551,8 @@ rt_build(struct dhcpcd_ctx *ctx, int af) * our routes are managed correctly. */ if_sortinterfaces(ctx); - TAILQ_INIT(&routes); - TAILQ_INIT(&added); + rb_tree_init(&routes, &rt_rb_tree_ops); + rb_tree_init(&added, &rt_rb_tree_ops); switch (af) { #ifdef INET @@ -524,7 +569,7 @@ rt_build(struct dhcpcd_ctx *ctx, int af) #endif } - TAILQ_FOREACH_SAFE(rt, &routes, rt_next, rtn) { + RB_TREE_FOREACH_SAFE(rt, &routes, rtn) { if (rt->rt_dest.sa_family != af && rt->rt_gateway.sa_family != af) continue; @@ -532,18 +577,18 @@ rt_build(struct dhcpcd_ctx *ctx, int af) if ((rt_find(&added, rt)) != NULL) continue; if (rt_doroute(rt)) { - TAILQ_REMOVE(&routes, rt, rt_next); - TAILQ_INSERT_TAIL(&added, rt, rt_next); + rb_tree_remove_node(&routes, rt); + rb_tree_insert_node(&added, rt); } } /* Remove old routes we used to manage. */ - TAILQ_FOREACH_SAFE(rt, &ctx->routes, rt_next, rtn) { - if (rt->rt_dest.sa_family != af && - rt->rt_gateway.sa_family != af) - continue; - TAILQ_REMOVE(&ctx->routes, rt, rt_next); - if (rt_find(&added, rt) == NULL) { + while ((rt = RB_TREE_MAX(&ctx->routes)) != NULL) { + rb_tree_remove_node(&ctx->routes, rt); + if (rt->rt_dest.sa_family == af && + rt->rt_gateway.sa_family == af && + rt_find(&added, rt) == NULL) + { o = rt->rt_ifp->options ? rt->rt_ifp->options->options : ctx->options; @@ -552,11 +597,14 @@ rt_build(struct dhcpcd_ctx *ctx, int af) (DHCPCD_EXITING | DHCPCD_PERSISTENT)) rt_delete(rt); } - TAILQ_INSERT_TAIL(&ctx->froutes, rt, rt_next); + rt_free(rt); } - rt_headclear(&ctx->routes, af); - TAILQ_CONCAT(&ctx->routes, &added, rt_next); + /* XXX This can be optimised if */ + while ((rt = RB_TREE_MIN(&added)) != NULL) { + rb_tree_remove_node(&added, rt); + rb_tree_insert_node(&ctx->routes, rt); + } getfail: rt_headclear(&routes, AF_UNSPEC); diff --git a/src/route.h b/src/route.h index 6f29813c..c6f8a654 100644 --- a/src/route.h +++ b/src/route.h @@ -28,6 +28,7 @@ #ifndef ROUTE_H #define ROUTE_H +#include #include #include @@ -56,7 +57,6 @@ #endif struct rt { - TAILQ_ENTRY(rt) rt_next; union sa_ss rt_ss_dest; #define rt_dest rt_ss_dest.sa union sa_ss rt_ss_netmask; @@ -78,17 +78,19 @@ struct rt { #define RTDF_RA 0x08 /* Router Advertisement */ #define RTDF_DHCP 0x10 /* DHCP route */ #define RTDF_STATIC 0x20 /* Configured in dhcpcd */ + rb_node_t rt_tree; }; -TAILQ_HEAD(rt_head, rt); + +extern const rb_tree_ops_t rt_rb_tree_ops; void rt_init(struct dhcpcd_ctx *); void rt_dispose(struct dhcpcd_ctx *); -struct rt * rt_find(struct rt_head *, const struct rt *); +struct rt * rt_find(rb_tree_t *, const struct rt *); void rt_free(struct rt *); void rt_freeif(struct interface *); -void rt_headclear0(struct dhcpcd_ctx *, struct rt_head *, int); -void rt_headclear(struct rt_head *, int); -void rt_headfreeif(struct rt_head *); +void rt_headclear0(struct dhcpcd_ctx *, rb_tree_t *, int); +void rt_headclear(rb_tree_t *, int); +void rt_headfreeif(rb_tree_t *); struct rt * rt_new0(struct dhcpcd_ctx *); void rt_setif(struct rt *, struct interface *); struct rt * rt_new(struct interface *);