changeset 4522:e2582c7c7e1d draft

route: Use order from message / config We can't just make the order up as that is prone to error. So apply ordering as from the source.
author Roy Marples <roy@marples.name>
date Fri, 07 Jun 2019 16:37:00 +0100
parents 9a0884e8ab97
children 605fe3b1f9ae
files src/dhcp.c src/dhcpcd.h src/if-options.c src/ipv4.c src/ipv4.h src/ipv4ll.c src/ipv6.c src/route.c src/route.h
diffstat 9 files changed, 125 insertions(+), 110 deletions(-) [+]
line wrap: on
line diff
--- a/src/dhcp.c	Sun May 05 19:04:02 2019 +0100
+++ b/src/dhcp.c	Fri Jun 07 16:37:00 2019 +0100
@@ -468,9 +468,7 @@
 		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;
--- a/src/dhcpcd.h	Sun May 05 19:04:02 2019 +0100
+++ b/src/dhcpcd.h	Fri Jun 07 16:37:00 2019 +0100
@@ -140,6 +140,7 @@
 #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;
--- a/src/if-options.c	Sun May 05 19:04:02 2019 +0100
+++ b/src/if-options.c	Fri Jun 07 16:37:00 2019 +0100
@@ -1129,9 +1129,7 @@
 			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)
@@ -1142,9 +1140,7 @@
 			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 ||
@@ -2282,7 +2278,7 @@
 	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
@@ -2340,6 +2336,9 @@
 	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 */
--- a/src/ipv4.c	Sun May 05 19:04:02 2019 +0100
+++ b/src/ipv4.c	Fri Jun 07 16:37:00 2019 +0100
@@ -247,7 +247,7 @@
 }
 
 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;
@@ -263,7 +263,7 @@
 	/* 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) &&
@@ -283,8 +283,7 @@
 		//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. */
@@ -297,15 +296,14 @@
 			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))
@@ -317,8 +315,7 @@
 		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 */
@@ -330,10 +327,13 @@
 		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;
@@ -425,25 +425,23 @@
 		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)
@@ -455,22 +453,14 @@
 
 #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)
+		return true;
 
-	if (!have_default) {
-		TAILQ_FOREACH(ifp, ctx->ifaces, next) {
-			if (ifp->active &&
-			    ipv4ll_defaultroute(routes, ifp) == 1)
-				break;
-		}
+	TAILQ_FOREACH(ifp, ctx->ifaces, next) {
+		if (ifp->active &&
+		    ipv4ll_defaultroute(routes, ifp) == 1)
+			break;
 	}
-#else
-	UNUSED(kroutes);
 #endif
 
 	return true;
--- a/src/ipv4.h	Sun May 05 19:04:02 2019 +0100
+++ b/src/ipv4.h	Fri Jun 07 16:37:00 2019 +0100
@@ -118,7 +118,7 @@
 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
--- a/src/ipv4ll.c	Sun May 05 19:04:02 2019 +0100
+++ b/src/ipv4ll.c	Fri Jun 07 16:37:00 2019 +0100
@@ -110,11 +110,7 @@
 	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
@@ -137,11 +133,7 @@
 	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
--- a/src/ipv6.c	Sun May 05 19:04:02 2019 +0100
+++ b/src/ipv6.c	Fri Jun 07 16:37:00 2019 +0100
@@ -2226,8 +2226,8 @@
 			    (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);
 			}
 		}
 	}
@@ -2251,17 +2251,14 @@
 			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;
 			}
 		}
@@ -2287,8 +2284,7 @@
 				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);
 			}
 		}
 	}
@@ -2313,7 +2309,7 @@
 #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)
--- a/src/route.c	Sun May 05 19:04:02 2019 +0100
+++ b/src/route.c	Fri Jun 07 16:37:00 2019 +0100
@@ -94,6 +94,17 @@
 		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.
@@ -109,67 +120,63 @@
 	return sa_cmp(&rt1->rt_netmask, &rt2->rt_netmask);
 }
 
-static bool rt_compare_os;
+static int
+rt_compare_os(__unused void *context, const void *node1, const void *node2)
+{
+	const struct rt *rt1 = node1, *rt2 = node2;
+	int c;
+
+	/* Sort by masked destination. */
+	c = rt_cmp_dest(rt1, rt2);
+	if (c != 0)
+		return c;
+
+#ifdef HAVE_ROUTE_METRIC
+	c = (int)(rt1->rt_ifp->metric - rt2->rt_ifp->metric);
+#endif
+	return c;
+}
 
 static int
-rt_compare(void *context, const void *node1, const void *node2)
+rt_compare_proto(__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);
-	if (c != 0)
-		return c;
-
-#ifndef HAVE_ROUTE_METRIC
-	if (context == &rt_compare_os)
-		return 0;
-#else
-	UNUSED(context);
-#endif
-
-	/* All other checks are by interface. */
-	if (rt1->rt_ifp == NULL || rt2->rt_ifp == NULL)
-		return 0;
+	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
 };
@@ -200,6 +207,14 @@
 #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)
 {
@@ -224,9 +239,7 @@
 		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);
@@ -351,6 +364,26 @@
 	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)
 {
@@ -629,15 +662,16 @@
 	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
--- a/src/route.h	Sun May 05 19:04:02 2019 +0100
+++ b/src/route.h	Fri Jun 07 16:37:00 2019 +0100
@@ -91,21 +91,26 @@
 #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);