core: fix nm_netns_ip_route_ecmp_commit() to return regular single-hop route

nm_netns_ip_route_ecmp_commit() returns the ECMP routes that didn't find
a merge-partner. It expects NML3Cfg to configure such routes in
platform.

Note that these routes have a positive "weight", which was used for
tracking the ECMP information in NML3Cfg and NMNetns.

But in kernel, single-hop routes don't have a weight. All routes in
NMPlatform will have a zero weight. While it somewhat works to call
nm_platform_ip_route_add() with a single-hop route of non-zero weight,
the result will be a different(!) route. This means for example that
nm_platform_ip_route_sync() will search the NMPlatform cache for whether
the routes exist, but in the cache single-hop routes with positive weight
never exist.

The proper solution is that nm_netns_ip_route_ecmp_commit() does not
tell NML3Cfg to add a route, that cannot be added. Instead, it must
first normalize the weight to zero, to have a "regular" single-hop
route.

Fixes: 5b5ce42682 ('nm-netns: track ECMP routes')
This commit is contained in:
Thomas Haller 2023-11-28 15:20:33 +01:00
parent 389ac096e3
commit ca7cbf6fea
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728

View file

@ -916,6 +916,8 @@ nm_netns_ip_route_ecmp_commit(NMNetns *self,
}
if (route->n_nexthops <= 1) {
NMPObject *route_clone;
/* This is a single hop route. Return it to the caller. */
if (!*out_singlehop_routes) {
/* Note that the returned array does not own a reference. This
@ -924,7 +926,30 @@ nm_netns_ip_route_ecmp_commit(NMNetns *self,
*out_singlehop_routes =
g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref);
}
g_ptr_array_add(*out_singlehop_routes, (gpointer) nmp_object_ref(route_obj));
/* We have here a IPv4 single-hop route. For internal tracking purposes,
* this route has a positive "weight" (which was used to mark it as a candidate
* for ECMP merging). Now we want to return this route to NML3Cfg and add it
* as regular single-hop routes.
*
* A single-hop route in kernel always has a "weight" of zero. This route
* cannot be added as-is. Well, if we would, then the result would be
* a different(!) route (with a zero "weight").
*
* Anticipate that and normalize the route now to be a regular single-hop
* route (with weight zero). nm_platform_ip_route_normalize() does that.
* We really want to return a regular route here, not the route with a positive
* weight that exists for internal tracking purposes.
*/
nm_assert(route_obj->ip4_route.weight > 0u);
nm_assert(NMP_OBJECT_GET_TYPE(route_obj) == NMP_OBJECT_TYPE_IP4_ROUTE);
route_clone = nmp_object_clone(route_obj, FALSE);
nm_platform_ip_route_normalize(AF_INET,
NMP_OBJECT_CAST_IP_ROUTE(route_clone),
NMP_IP_ROUTE_NORMALIZE_FLAGS_NONE);
g_ptr_array_add(*out_singlehop_routes, route_clone);
if (changed) {
_LOGT("ecmp-route: single-hop %s",
nmp_object_to_string(route_obj,