]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Replace route TAILQ macros with rbtree(3) from NetBSD.
authorRoy Marples <roy@marples.name>
Sun, 3 Mar 2019 19:52:57 +0000 (19:52 +0000)
committerRoy Marples <roy@marples.name>
Sun, 3 Mar 2019 19:52:57 +0000 (19:52 +0000)
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 <sharpd@cumulusnetworks.com> 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.

src/dhcp.c
src/dhcp.h
src/dhcpcd.h
src/if-options.c
src/if-options.h
src/ipv4.c
src/ipv4.h
src/ipv6.c
src/ipv6.h
src/route.c
src/route.h

index d0f9a453a1c168c18631c039777d211450ccf3f3..b77eda164317f8e73ec58ec6dab840d4aa3e780a 100644 (file)
@@ -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;
 
index 19da216272940bb5a861c9117bfe2503ae53d65e..245d20a4134d529e495f5b3707b4c63e94659176 100644 (file)
@@ -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 *);
 
index 02b830c0d1b3c3d5aaa4a87a3077b304e6812796..a09f40fba2877a02bbb4b0b257cc7b69b9ef6ce4 100644 (file)
@@ -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;
index 06c47b0593c741039dbb2bfe363d3d1b0e004507..ccd5b5453d547e251607e065692cd9132b08b93e 100644 (file)
@@ -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
index eddfeef9d67611908c5d2fa311ffa32593fa26f1..1b0988f3abe84349c27cc7bb004618ecd1705eb3 100644 (file)
@@ -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;
index 57f70a77b53f1aec49d3338e1ddcb65ec83046be..952b24fee7227d7a44429716b08f67a5a46a69db 100644 (file)
@@ -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
index 496b8ecd23a2d743e8c500d479adef98499a1a3e..1f8c3a301ffd307f37c9119c0825e162e28dbe96 100644 (file)
@@ -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
index d7c73c223c874abd37e7035ea42911871639b64a..f078972c82c23d13a534efc328bc7cf17b5ebda2 100644 (file)
@@ -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;
 
index bc48d42e280a6e5c3f78e4234c50b74b480bab95..4a9d5b5db7b54fece2560ce9b51432ab6c269aa8 100644 (file)
@@ -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 */
index c5f790f90c930947535c963129262fd4760cabbe..666a60972e8a4a7a2a960000462a88b8b3e3074a 100644 (file)
@@ -29,6 +29,7 @@
 #include <ctype.h>
 #include <errno.h>
 #include <stdbool.h>
+#include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #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);
index 6f29813c41ec3a265080be9dfee3a7f7abb8ec18..c6f8a65427c5085c4b1f911bc80cea461e0bd3b1 100644 (file)
@@ -28,6 +28,7 @@
 #ifndef ROUTE_H
 #define ROUTE_H
 
+#include <sys/rbtree.h>
 #include <sys/socket.h>
 #include <net/route.h>
 
@@ -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 *);