From: Yu Watanabe Date: Sat, 13 Jan 2024 04:16:10 +0000 (+0900) Subject: network/route: find/distinguish routes in the same way that the kernel uses X-Git-Tag: v256-rc1~1139 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=47420573a778e83f2bddd536cf185cd5829e8ba9;p=thirdparty%2Fsystemd.git network/route: find/distinguish routes in the same way that the kernel uses The kernel uses different logics to find or distinguish IPv4 and IPv6 routes. Let's follow the same way that the kernel uses. See comments in the code for more details. --- diff --git a/src/network/networkd-route-nexthop.c b/src/network/networkd-route-nexthop.c index 43ff790f04a..f58cdb36955 100644 --- a/src/network/networkd-route-nexthop.c +++ b/src/network/networkd-route-nexthop.c @@ -35,7 +35,7 @@ void route_nexthops_done(Route *route) { ordered_set_free(route->nexthops); } -static void route_nexthop_hash_func(const RouteNextHop *nh, struct siphash *state) { +static void route_nexthop_hash_func_full(const RouteNextHop *nh, struct siphash *state, bool hash_all_parameters) { assert(nh); assert(state); @@ -46,13 +46,15 @@ static void route_nexthop_hash_func(const RouteNextHop *nh, struct siphash *stat return; in_addr_hash_func(&nh->gw, nh->family, state); + if (!hash_all_parameters) + return; siphash24_compress_typesafe(nh->weight, state); siphash24_compress_typesafe(nh->ifindex, state); if (nh->ifindex == 0) siphash24_compress_string(nh->ifname, state); /* For Network or Request object. */ } -static int route_nexthop_compare_func(const RouteNextHop *a, const RouteNextHop *b) { +static int route_nexthop_compare_func_full(const RouteNextHop *a, const RouteNextHop *b, bool hash_all_parameters) { int r; assert(a); @@ -69,6 +71,9 @@ static int route_nexthop_compare_func(const RouteNextHop *a, const RouteNextHop if (r != 0) return r; + if (!hash_all_parameters) + return 0; + r = CMP(a->weight, b->weight); if (r != 0) return r; @@ -86,6 +91,14 @@ static int route_nexthop_compare_func(const RouteNextHop *a, const RouteNextHop return 0; } +static void route_nexthop_hash_func(const RouteNextHop *nh, struct siphash *state) { + route_nexthop_hash_func_full(nh, state, /* hash_all_parameters = */ true); +} + +static int route_nexthop_compare_func(const RouteNextHop *a, const RouteNextHop *b) { + return route_nexthop_compare_func_full(a, b, /* hash_all_parameters = */ true); +} + DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( route_nexthop_hash_ops, RouteNextHop, @@ -93,6 +106,70 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( route_nexthop_compare_func, route_nexthop_free); +static size_t route_n_nexthops(const Route *route) { + assert(route); + + if (route->nexthop_id != 0 || route_type_is_reject(route)) + return 0; + + if (ordered_set_isempty(route->nexthops)) + return 1; + + return ordered_set_size(route->nexthops); +} + +void route_nexthops_hash_func(const Route *route, struct siphash *state) { + assert(route); + + size_t nhs = route_n_nexthops(route); + siphash24_compress_typesafe(nhs, state); + + switch (nhs) { + case 0: + siphash24_compress_typesafe(route->nexthop_id, state); + return; + + case 1: + route_nexthop_hash_func_full(&route->nexthop, state, /* hash_all_parameters = */ false); + return; + + default: { + RouteNextHop *nh; + ORDERED_SET_FOREACH(nh, route->nexthops) + route_nexthop_hash_func(nh, state); + }} +} + +int route_nexthops_compare_func(const Route *a, const Route *b) { + int r; + + assert(a); + assert(b); + + size_t a_nhs = route_n_nexthops(a); + size_t b_nhs = route_n_nexthops(b); + r = CMP(a_nhs, b_nhs); + if (r != 0) + return r; + + switch (a_nhs) { + case 0: + return CMP(a->nexthop_id, b->nexthop_id); + + case 1: + return route_nexthop_compare_func_full(&a->nexthop, &b->nexthop, /* hash_all_parameters = */ false); + + default: { + RouteNextHop *nh; + ORDERED_SET_FOREACH(nh, a->nexthops) { + r = CMP(nh, (RouteNextHop*) ordered_set_get(a->nexthops, nh)); + if (r != 0) + return r; + } + return 0; + }} +} + int route_nexthop_get_link(Manager *manager, Link *link, const RouteNextHop *nh, Link **ret) { assert(manager); assert(nh); diff --git a/src/network/networkd-route-nexthop.h b/src/network/networkd-route-nexthop.h index 0a524400cf4..f06a2c49ff1 100644 --- a/src/network/networkd-route-nexthop.h +++ b/src/network/networkd-route-nexthop.h @@ -9,6 +9,7 @@ #include "conf-parser.h" #include "in-addr-util.h" #include "macro.h" +#include "siphash24.h" typedef struct Link Link; typedef struct Manager Manager; @@ -28,6 +29,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(RouteNextHop*, route_nexthop_free); void route_nexthops_done(Route *route); +void route_nexthops_hash_func(const Route *route, struct siphash *state); +int route_nexthops_compare_func(const Route *a, const Route *b); + int route_nexthop_get_link(Manager *manager, Link *link, const RouteNextHop *nh, Link **ret); int route_nexthops_is_ready_to_configure(const Route *route, Link *link); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 48b8af2f060..adc920427be 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -117,31 +117,51 @@ static void route_hash_func(const Route *route, struct siphash *state) { switch (route->family) { case AF_INET: - case AF_INET6: - siphash24_compress_typesafe(route->dst_prefixlen, state); + /* First, the table, destination prefix, priority, and tos (dscp), are used to find routes. + * See fib_table_insert(), fib_find_node(), and fib_find_alias() in net/ipv4/fib_trie.c of the kernel. */ + siphash24_compress_typesafe(route->table, state); in_addr_hash_func(&route->dst, route->family, state); - - siphash24_compress_typesafe(route->src_prefixlen, state); - in_addr_hash_func(&route->src, route->family, state); - - siphash24_compress_typesafe(route->nexthop.family, state); - if (IN_SET(route->nexthop.family, AF_INET, AF_INET6)) { - in_addr_hash_func(&route->nexthop.gw, route->nexthop.family, state); - siphash24_compress_typesafe(route->nexthop.weight, state); - } - - in_addr_hash_func(&route->prefsrc, route->family, state); - - siphash24_compress_typesafe(route->tos, state); + siphash24_compress_typesafe(route->dst_prefixlen, state); siphash24_compress_typesafe(route->priority, state); - siphash24_compress_typesafe(route->table, state); + siphash24_compress_typesafe(route->tos, state); + + /* Then, protocol, scope, type, flags, prefsrc, metrics (RTAX_* attributes), and nexthops (gateways) + * are used to find routes. See fib_find_info() in net/ipv4/fib_semantics.c of the kernel. */ siphash24_compress_typesafe(route->protocol, state); siphash24_compress_typesafe(route->scope, state); siphash24_compress_typesafe(route->type, state); + unsigned flags = route->flags & ~RTNH_COMPARE_MASK; + siphash24_compress_typesafe(flags, state); + in_addr_hash_func(&route->prefsrc, route->family, state); + + /* nexthops (id, number of nexthops, nexthop) */ + route_nexthops_hash_func(route, state); + + /* metrics */ route_metric_hash_func(&route->metric, state); - siphash24_compress_typesafe(route->nexthop_id, state); + break; + case AF_INET6: + /* First, table and destination prefix are used for classifying routes. + * See fib6_add() and fib6_add_1() in net/ipv6/ip6_fib.c of the kernel. */ + siphash24_compress_typesafe(route->table, state); + in_addr_hash_func(&route->dst, route->family, state); + siphash24_compress_typesafe(route->dst_prefixlen, state); + + /* Then, source prefix is used. See fib6_add(). */ + in_addr_hash_func(&route->src, route->family, state); + siphash24_compress_typesafe(route->src_prefixlen, state); + + /* See fib6_add_rt2node(). */ + siphash24_compress_typesafe(route->priority, state); + + /* See rt6_duplicate_nexthop() in include/net/ip6_route.h of the kernel. + * Here, we hash nexthop in a similar way as the one for IPv4. */ + route_nexthops_hash_func(route, state); + + /* Unlike IPv4 routes, metrics are not taken into account. */ break; + default: /* treat any other address family as AF_UNSPEC */ break; @@ -157,8 +177,7 @@ static int route_compare_func(const Route *a, const Route *b) { switch (a->family) { case AF_INET: - case AF_INET6: - r = CMP(a->dst_prefixlen, b->dst_prefixlen); + r = CMP(a->table, b->table); if (r != 0) return r; @@ -166,65 +185,71 @@ static int route_compare_func(const Route *a, const Route *b) { if (r != 0) return r; - r = CMP(a->src_prefixlen, b->src_prefixlen); + r = CMP(a->dst_prefixlen, b->dst_prefixlen); if (r != 0) return r; - r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family)); + r = CMP(a->priority, b->priority); if (r != 0) return r; - r = CMP(a->nexthop.family, b->nexthop.family); + r = CMP(a->tos, b->tos); if (r != 0) return r; - if (IN_SET(a->nexthop.family, AF_INET, AF_INET6)) { - r = memcmp(&a->nexthop.gw, &b->nexthop.gw, FAMILY_ADDRESS_SIZE(a->family)); - if (r != 0) - return r; + r = CMP(a->protocol, b->protocol); + if (r != 0) + return r; - r = CMP(a->nexthop.weight, b->nexthop.weight); - if (r != 0) - return r; - } + r = CMP(a->scope, b->scope); + if (r != 0) + return r; - r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family)); + r = CMP(a->type, b->type); if (r != 0) return r; - r = CMP(a->tos, b->tos); + r = CMP(a->flags & ~RTNH_COMPARE_MASK, b->flags & ~RTNH_COMPARE_MASK); if (r != 0) return r; - r = CMP(a->priority, b->priority); + r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family)); + if (r != 0) + return r; + + r = route_nexthops_compare_func(a, b); if (r != 0) return r; + return route_metric_compare_func(&a->metric, &b->metric); + + case AF_INET6: r = CMP(a->table, b->table); if (r != 0) return r; - r = CMP(a->protocol, b->protocol); + r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family)); if (r != 0) return r; - r = CMP(a->scope, b->scope); + r = CMP(a->dst_prefixlen, b->dst_prefixlen); if (r != 0) return r; - r = CMP(a->type, b->type); + r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family)); if (r != 0) return r; - r = route_metric_compare_func(&a->metric, &b->metric); + r = CMP(a->src_prefixlen, b->src_prefixlen); if (r != 0) return r; - r = CMP(a->nexthop_id, b->nexthop_id); + r = CMP(a->priority, b->priority); if (r != 0) return r; - return 0; + return route_nexthops_compare_func(a, b); + default: /* treat any other address family as AF_UNSPEC */ return 0;