From: Roy Marples Date: Sun, 5 May 2019 11:47:54 +0000 (+0100) Subject: route: Fix OS route comparison X-Git-Tag: v8.0.0~41 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=88c7982663689262b5cb8afa40e0342a376fb00b;p=thirdparty%2Fdhcpcd.git route: Fix OS route comparison This at least works on a multi-homed NetBSD. --- diff --git a/src/route.c b/src/route.c index cf526865..bcf47727 100644 --- a/src/route.c +++ b/src/route.c @@ -68,6 +68,32 @@ static size_t froutes; 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 @@ rt_compare(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; @@ -103,14 +131,20 @@ rt_compare(void *context, const void *node1, const void *node2) 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 = rt_cmp_netmask(rt1, rt2); + /* 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; @@ -122,12 +156,6 @@ rt_compare(void *context, const void *node1, const void *node2) 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); }