We can't just make the order up as that is prone to error.
So apply ordering as from the source.
sa_in_init(&rt->rt_dest, &dest);
sa_in_init(&rt->rt_netmask, &netmask);
sa_in_init(&rt->rt_gateway, &gateway);
- if (rb_tree_insert_node(routes, rt) != rt)
- rt_free(rt);
- else
+ if (rt_proto_add(routes, rt))
n++;
}
return n;
#ifdef RT_FREE_ROUTE_TABLE
rb_tree_t froutes; /* free routes for re-use */
#endif
+ size_t rt_order; /* route order storage */
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);
- if (rb_tree_insert_node(&ifo->routes, rt) != rt)
- rt_free(rt);
- else
+ if (rt_proto_add_ctx(&ifo->routes, rt, ctx))
add_environ(&ifo->config, arg, 0);
} 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);
- if (rb_tree_insert_node(&ifo->routes, rt) != rt)
- rt_free(rt);
- else
+ if (rt_proto_add_ctx(&ifo->routes, rt, ctx))
add_environ(&ifo->config, arg, 0);
} else if (strncmp(arg, "interface_mtu=",
strlen("interface_mtu=")) == 0 ||
ifo->script = UNCONST(default_script);
ifo->metric = -1;
ifo->auth.options |= DHCPCD_AUTH_REQUIRE;
- rb_tree_init(&ifo->routes, &rt_compare_list_ops);
+ rb_tree_init(&ifo->routes, &rt_compare_proto_ops);
#ifdef AUTH
TAILQ_INIT(&ifo->auth.tokens);
#endif
buf = NULL;
buflen = 0;
+ /* Reset route order */
+ ctx->rt_order = 0;
+
/* Parse our embedded options file */
if (ifname == NULL && !(ctx->options & DHCPCD_PRINT_PIDFILE)) {
/* Space for initial estimates */
}
static int
-inet_dhcproutes(rb_tree_t *routes, struct interface *ifp)
+inet_dhcproutes(rb_tree_t *routes, struct interface *ifp, bool *have_default)
{
const struct dhcp_state *state;
rb_tree_t nroutes;
/* An address does have to exist. */
assert(state->addr);
- rb_tree_init(&nroutes, &rt_compare_list_ops);
+ rb_tree_init(&nroutes, &rt_compare_proto_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;
- if (rb_tree_insert_node(&nroutes, rt) != rt)
- rt_free(rt);
+ rt_proto_add(&nroutes, rt);
}
/* If any set routes, grab them, otherwise DHCP routes. */
memcpy(rt, r, sizeof(*rt));
rt_setif(rt, ifp);
rt->rt_dflags = RTDF_STATIC;
- if (rb_tree_insert_node(&nroutes, rt) != rt)
- rt_free(rt);
+ rt_proto_add(&nroutes, rt);
}
} else {
if (dhcp_get_routes(&nroutes, ifp) == -1)
return -1;
}
- /* If configured, Install a gateway to the desintion
+ /* If configured, install a gateway to the desintion
* for P2P interfaces. */
if (ifp->flags & IFF_POINTOPOINT &&
has_option_mask(ifp->options->dstmask, DHO_ROUTER))
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);
- if (rb_tree_insert_node(&nroutes, rt) != rt)
- rt_free(rt);
+ rt_proto_add(&nroutes, rt);
}
/* Copy our address as the source address and set mtu */
if (!(rt->rt_dflags & RTDF_STATIC))
rt->rt_dflags |= RTDF_DHCP;
sa_in_init(&rt->rt_ifa, &state->addr->addr);
- if (rb_tree_insert_node(routes, rt) != rt)
+ if (rb_tree_insert_node(routes, rt) != rt) {
rt_free(rt);
- else
- n++;
+ continue;
+ }
+ if (rt_is_default(rt))
+ *have_default = true;
+ n++;
}
return n;
sa_in_init(&rth->rt_gateway, &in);
rth->rt_mtu = dhcp_get_mtu(ifp);
sa_in_init(&rth->rt_ifa, &state->addr->addr);
- if (rb_tree_insert_node(routes, rth) != rth)
- rt_free(rth);
+ rt_proto_add(routes, rt);
}
return 0;
}
bool
-inet_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes, rb_tree_t *kroutes)
+inet_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes)
{
struct interface *ifp;
#ifdef IPV4LL
- struct rt def;
- bool have_default;
+ bool have_default = false;
#endif
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (!ifp->active)
continue;
- if (inet_dhcproutes(routes, ifp) == -1)
+ if (inet_dhcproutes(routes, ifp, &have_default) == -1)
return false;
#ifdef IPV4LL
if (ipv4ll_subnetroute(routes, ifp) == -1)
#ifdef IPV4LL
/* If there is no default route, see if we can use an IPv4LL one. */
- memset(&def, 0, sizeof(def));
- def.rt_dest.sa_family = AF_INET;
- def.rt_netmask.sa_family = AF_INET;
- have_default = (rb_tree_find_node(routes, &def) != NULL);
- if (!have_default)
- have_default = (rb_tree_find_node(kroutes, &def) != NULL);
-
- if (!have_default) {
- TAILQ_FOREACH(ifp, ctx->ifaces, next) {
- if (ifp->active &&
- ipv4ll_defaultroute(routes, ifp) == 1)
- break;
- }
+ if (have_default)
+ return true;
+
+ TAILQ_FOREACH(ifp, ctx->ifaces, next) {
+ if (ifp->active &&
+ ipv4ll_defaultroute(routes, ifp) == 1)
+ break;
}
-#else
- UNUSED(kroutes);
#endif
return true;
uint32_t ipv4_getnetmask(uint32_t);
int ipv4_hasaddr(const struct interface *);
-bool inet_getroutes(struct dhcpcd_ctx *, rb_tree_t *, rb_tree_t *);
+bool inet_getroutes(struct dhcpcd_ctx *, rb_tree_t *);
#define STATE_ADDED 0x01
#define STATE_FAKE 0x02
in.s_addr = INADDR_ANY;
sa_in_init(&rt->rt_gateway, &in);
sa_in_init(&rt->rt_ifa, &state->addr->addr);
- if (rb_tree_insert_node(routes, rt) != rt) {
- rt_free(rt);
- return 0;
- }
- return 1;
+ return rt_proto_add(routes, rt) ? 1 : 0;
}
int
sa_in_init(&rt->rt_netmask, &in);
sa_in_init(&rt->rt_gateway, &in);
sa_in_init(&rt->rt_ifa, &state->addr->addr);
- if (rb_tree_insert_node(routes, rt) != rt) {
- rt_free(rt);
- return 0;
- }
- return 1;
+ return rt_proto_add(routes, rt) ? 1 : 0;
}
ssize_t
(IPV6_AF_ADDED | IPV6_AF_STATIC))
{
rt = inet6_makeprefix(ifp, NULL, ia);
- if (rt && rb_tree_insert_node(routes, rt) != rt)
- rt_free(rt);
+ if (rt)
+ rt_proto_add(routes, rt);
}
}
}
rt = inet6_makeprefix(rap->iface, rap, addr);
if (rt) {
rt->rt_dflags |= RTDF_RA;
- if (rb_tree_insert_node(routes, rt) != rt)
- rt_free(rt);
+ rt_proto_add(routes, rt);
}
}
if (rap->lifetime) {
rt = inet6_makerouter(rap);
if (rt) {
rt->rt_dflags |= RTDF_RA;
- if (rb_tree_insert_node(routes, rt) != rt)
- rt_free(rt);
- else if (have_default)
+ if (rt_proto_add(routes, rt) && have_default)
*have_default = true;
}
}
if (rt == NULL)
continue;
rt->rt_dflags |= RTDF_DHCP;
- if (rb_tree_insert_node(routes, rt) != rt)
- rt_free(rt);
+ rt_proto_add(routes, rt);
}
}
}
#ifdef DHCP6
/* We have no way of knowing if prefixes added by DHCP are reachable
* or not, so we have to assume they are.
- * Add bound before delegated so we can prefer interfaces better */
+ * Add bound before delegated so we can prefer interfaces better. */
if (inet6_dhcproutes(routes, ctx, DH6S_BOUND) == -1)
return false;
if (inet6_dhcproutes(routes, ctx, DH6S_DELEGATED) == -1)
memset(dstp, 0, (size_t)(addre - dstp));
}
+int
+rt_cmp_dest(const struct rt *rt1, const struct rt *rt2)
+{
+ union sa_ss ma1 = { .sa.sa_family = AF_UNSPEC };
+ union sa_ss ma2 = { .sa.sa_family = AF_UNSPEC };
+
+ rt_maskedaddr(&ma1.sa, &rt1->rt_dest, &rt1->rt_netmask);
+ rt_maskedaddr(&ma2.sa, &rt2->rt_dest, &rt2->rt_netmask);
+ return sa_cmp(&ma1.sa, &ma2.sa);
+}
+
/*
* 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);
}
-static bool rt_compare_os;
-
static int
-rt_compare(void *context, const void *node1, const void *node2)
+rt_compare_os(__unused void *context, const void *node1, const void *node2)
{
const struct rt *rt1 = node1, *rt2 = node2;
- bool rt1u, rt2u;
- union sa_ss ma1 = { .sa.sa_family = AF_UNSPEC };
- union sa_ss ma2 = { .sa.sa_family = AF_UNSPEC };
int c;
- struct interface *ifp1, *ifp2;
-
- /* 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 masked destination. */
- rt_maskedaddr(&ma1.sa, &rt1->rt_dest, &rt1->rt_netmask);
- rt_maskedaddr(&ma2.sa, &rt2->rt_dest, &rt2->rt_netmask);
- c = sa_cmp(&ma1.sa, &ma2.sa);
+ c = rt_cmp_dest(rt1, rt2);
if (c != 0)
return c;
-#ifndef HAVE_ROUTE_METRIC
- if (context == &rt_compare_os)
- return 0;
-#else
- UNUSED(context);
+#ifdef HAVE_ROUTE_METRIC
+ c = (int)(rt1->rt_ifp->metric - rt2->rt_ifp->metric);
#endif
+ return c;
+}
- /* All other checks are by interface. */
- if (rt1->rt_ifp == NULL || rt2->rt_ifp == NULL)
- return 0;
+static int
+rt_compare_proto(__unused void *context, const void *node1, const void *node2)
+{
+ const struct rt *rt1 = node1, *rt2 = node2;
+ int c;
+ struct interface *ifp1, *ifp2;
+
+ assert(rt1->rt_ifp != NULL);
+ assert(rt2->rt_ifp != NULL);
ifp1 = rt1->rt_ifp;
ifp2 = rt2->rt_ifp;
- /* Prefer interfaces with a carrier */
+ /* Prefer interfaces with a carrier. */
c = ifp1->carrier - ifp2->carrier;
if (c != 0)
return -c;
- /* Lower metric interfaces come first */
- return (int)(ifp1->metric - ifp2->metric);
+ /* Lower metric interfaces come first. */
+ c = (int)(ifp1->metric - ifp2->metric);
+ if (c != 0)
+ return c;
+
+ /* Finally the order in which the route was given to us. */
+ if (rt1->rt_order > rt2->rt_order)
+ return 1;
+ if (rt1->rt_order < rt2->rt_order)
+ return -1;
+ return 0;
}
static const rb_tree_ops_t rt_compare_os_ops = {
- .rbto_compare_nodes = rt_compare,
- .rbto_compare_key = rt_compare,
+ .rbto_compare_nodes = rt_compare_os,
+ .rbto_compare_key = rt_compare_os,
.rbto_node_offset = offsetof(struct rt, rt_tree),
- .rbto_context = &rt_compare_os
+ .rbto_context = NULL
};
-const rb_tree_ops_t rt_compare_list_ops = {
- .rbto_compare_nodes = rt_compare,
- .rbto_compare_key = rt_compare,
+const rb_tree_ops_t rt_compare_proto_ops = {
+ .rbto_compare_nodes = rt_compare_proto,
+ .rbto_compare_key = rt_compare_proto,
.rbto_node_offset = offsetof(struct rt, rt_tree),
.rbto_context = NULL
};
#endif
}
+bool
+rt_is_default(const struct rt *rt)
+{
+
+ return sa_is_unspecified(&rt->rt_dest) &&
+ sa_is_unspecified(&rt->rt_netmask);
+}
+
static void
rt_desc(const char *cmd, const struct rt *rt)
{
else
loginfox("%s: %s host route to %s via %s",
ifname, cmd, dest, gateway);
- } else if (sa_is_unspecified(&rt->rt_dest) &&
- sa_is_unspecified(&rt->rt_netmask))
- {
+ } else if (rt_is_default(rt)) {
if (gateway_unspec)
loginfox("%s: %s default route",
ifname, cmd);
return rt;
}
+struct rt *
+rt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx)
+{
+
+ rt->rt_order = ctx->rt_order++;
+ if (rb_tree_insert_node(tree, rt) == rt)
+ return rt;
+
+ rt_free(rt);
+ return NULL;
+}
+
+struct rt *
+rt_proto_add(rb_tree_t *tree, struct rt *rt)
+{
+
+ assert (rt->rt_ifp != NULL);
+ return rt_proto_add_ctx(tree, rt, rt->rt_ifp->ctx);
+}
+
void
rt_free(struct rt *rt)
{
struct rt *rt, *rtn;
unsigned long long o;
- rb_tree_init(&routes, &rt_compare_list_ops);
+ rb_tree_init(&routes, &rt_compare_proto_ops);
rb_tree_init(&added, &rt_compare_os_ops);
rb_tree_init(&kroutes, &rt_compare_os_ops);
if_initrt(ctx, &kroutes, af);
+ ctx->rt_order = 0;
switch (af) {
#ifdef INET
case AF_INET:
- if (!inet_getroutes(ctx, &routes, &kroutes))
+ if (!inet_getroutes(ctx, &routes))
goto getfail;
break;
#endif
#define RTDF_RA 0x08 /* Router Advertisement */
#define RTDF_DHCP 0x10 /* DHCP route */
#define RTDF_STATIC 0x20 /* Configured in dhcpcd */
+ size_t rt_order;
rb_node_t rt_tree;
};
-extern const rb_tree_ops_t rt_compare_list_ops;
+extern const rb_tree_ops_t rt_compare_proto_ops;
void rt_init(struct dhcpcd_ctx *);
void rt_dispose(struct dhcpcd_ctx *);
void rt_free(struct rt *);
void rt_freeif(struct interface *);
+bool rt_is_default(const struct rt *);
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 *);
+struct rt * rt_proto_add_ctx(rb_tree_t *, struct rt *, struct dhcpcd_ctx *);
+struct rt * rt_proto_add(rb_tree_t *, struct rt *);
+int rt_cmp_dest(const struct rt *, const struct rt *);
void rt_recvrt(int, const struct rt *, pid_t);
void rt_build(struct dhcpcd_ctx *, int);