}
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;
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;
* 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;
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++;
}
}
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++;
}
}
/* 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;
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 *);
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;
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)
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)
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
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;
}
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;
/* 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) &&
//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)
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)
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;
}
/* 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;
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) ||
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;
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
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
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;
{
rt = inet6_makeprefix(ifp, NULL, ia);
if (rt)
- TAILQ_INSERT_TAIL(routes, rt, rt_next);
+ rb_tree_insert_node(routes, rt);
}
}
}
}
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;
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;
}
#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;
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);
}
}
}
#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;
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 */
#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.
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)
{
}
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;
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);
}
}
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;
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
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 ||
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);
}
}
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);
}
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;
}
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) {
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;
* 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
#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;
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;
(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);
#ifndef ROUTE_H
#define ROUTE_H
+#include <sys/rbtree.h>
#include <sys/socket.h>
#include <net/route.h>
#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;
#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 *);