changeset 4518:e523b7420583 draft

route: Fix OS route comparison This at least works on a multi-homed NetBSD.
author Roy Marples <roy@marples.name>
date Sun, 05 May 2019 12:47:54 +0100
parents ae3b0ea6c309
children ff4eb295ed3c
files src/route.c
diffstat 1 files changed, 39 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/src/route.c	Sun May 05 12:46:32 2019 +0100
+++ b/src/route.c	Sun May 05 12:47:54 2019 +0100
@@ -68,6 +68,32 @@
 static size_t mroutes;
 #endif
 
+static void
+rt_maskedaddr(struct sockaddr *dst,
+	const struct sockaddr *addr, const struct sockaddr *netmask)
+{
+	const char *addrp = addr->sa_data, *netmaskp = netmask->sa_data;
+	char *dstp = dst->sa_data;
+	const char *addre = (char *)dst + sa_len(addr);
+	const char *netmaske = (char *)dst + MIN(sa_len(addr), sa_len(netmask));
+
+	dst->sa_family = addr->sa_family;
+#ifdef HAVE_SA_LEN
+	dst->sa_len = addr->sa_len;
+#endif
+
+	if (sa_is_unspecified(netmask)) {
+		if (addre > dstp)
+			memcpy(dstp, addrp, (size_t)(addre - dstp));
+		return;
+	}
+
+	while (dstp < netmaske)
+		*dstp++ = *addrp++ & *netmaskp++;
+	if (dstp < addre)
+		memset(dstp, 0, (size_t)(addre - dstp));
+}
+
 /*
  * On some systems, host routes have no need for a netmask.
  * However DHCP specifies host routes using an all-ones netmask.
@@ -90,6 +116,8 @@
 {
 	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;
 
@@ -103,13 +131,19 @@
 	if (rt1u != rt2u)
 		return rt1u ? 1 : -1;
 
-	/* Sort by destination and netmask. */
-	c = sa_cmp(&rt1->rt_dest, &rt2->rt_dest);
+	/* 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;
-	c = rt_cmp_netmask(rt1, rt2);
-	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)
@@ -122,12 +156,6 @@
 	if (c != 0)
 		return -c;
 
-#ifndef HAVE_ROUTE_METRIC
-	if (context == &rt_compare_os)
-		return 0;
-#else
-	UNUSED(context);
-#endif
 	/* Lower metric interfaces come first */
 	return (int)(ifp1->metric - ifp2->metric);
 }