diff --git a/src/network/networkd-route-nexthop.c b/src/network/networkd-route-nexthop.c index 43ff790f04..f58cdb3695 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 0a524400cf..f06a2c49ff 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 48b8af2f06..adc920427b 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); - 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->priority, 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->dst_prefixlen, state); + siphash24_compress_typesafe(route->priority, 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); - route_metric_hash_func(&route->metric, state); - siphash24_compress_typesafe(route->nexthop_id, 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); 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,33 +185,7 @@ static int route_compare_func(const Route *a, const Route *b) { if (r != 0) return r; - r = CMP(a->src_prefixlen, b->src_prefixlen); - if (r != 0) - return r; - - r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family)); - if (r != 0) - return r; - - r = CMP(a->nexthop.family, b->nexthop.family); - 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->nexthop.weight, b->nexthop.weight); - if (r != 0) - return r; - } - - r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family)); - if (r != 0) - return r; - - r = CMP(a->tos, b->tos); + r = CMP(a->dst_prefixlen, b->dst_prefixlen); if (r != 0) return r; @@ -200,7 +193,7 @@ static int route_compare_func(const Route *a, const Route *b) { if (r != 0) return r; - r = CMP(a->table, b->table); + r = CMP(a->tos, b->tos); if (r != 0) return r; @@ -216,15 +209,47 @@ static int route_compare_func(const Route *a, const Route *b) { if (r != 0) return r; - r = route_metric_compare_func(&a->metric, &b->metric); + r = CMP(a->flags & ~RTNH_COMPARE_MASK, b->flags & ~RTNH_COMPARE_MASK); if (r != 0) return r; - r = CMP(a->nexthop_id, b->nexthop_id); + r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family)); if (r != 0) return r; - return 0; + 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 = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family)); + if (r != 0) + return r; + + r = CMP(a->dst_prefixlen, b->dst_prefixlen); + if (r != 0) + return r; + + r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family)); + if (r != 0) + return r; + + r = CMP(a->src_prefixlen, b->src_prefixlen); + if (r != 0) + return r; + + r = CMP(a->priority, b->priority); + if (r != 0) + return r; + + return route_nexthops_compare_func(a, b); + default: /* treat any other address family as AF_UNSPEC */ return 0;