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);
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);
if (r != 0)
return r;
+ if (!hash_all_parameters)
+ return 0;
+
r = CMP(a->weight, b->weight);
if (r != 0)
return r;
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,
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);
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;
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;
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;