platform: fix lookup of routes and deletion of IPv4 routes
When doing a lookup for an libnl route, the cache comparison function
for routes takes into account 'family', 'tos', 'table', 'dst', and 'prio'.
In NetworkManager we don't use all of these properties for a route, so
at several places when doing a cache lookup we don't have all identifying
properties. Usually we only have 'family' and 'dst' ('table' is
implicit 0, because NM does currently not care about any other tables).
The problem is that NM sees routes with different 'tos', 'prio', but it
cannot look them up in the cache. Add a hack to search the cache
fuzzy.
This is similar to the hack for link, where the identifying properties
are 'family' and 'ifindex', but we only have 'ifindex' at hand. However,
contrary to this hack, we coerce the 'family' to AF_UNSPEC for every link cache
operation. This is not viable in this case, because we internally need
the 'tos' field.
We need the 'tos' field because when deleting an IPv4 route, the 'tos' field must
match. See fib_table_delete(). This was already partially fixed by commit
f0daf90298
, but before the lookup to the
cached object would fail for any non-zero 'tos'.
Signed-off-by: Thomas Haller <thaller@redhat.com>
This commit is contained in:
@@ -1501,7 +1501,7 @@ add_object (NMPlatform *platform, struct nl_object *obj)
|
||||
|
||||
/* Decreases the reference count if @obj for convenience */
|
||||
static gboolean
|
||||
delete_object (NMPlatform *platform, struct nl_object *obj)
|
||||
delete_object (NMPlatform *platform, struct nl_object *obj, gboolean do_refresh_object)
|
||||
{
|
||||
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
|
||||
auto_nl_object struct nl_object *obj_cleanup = obj;
|
||||
@@ -1547,7 +1547,8 @@ delete_object (NMPlatform *platform, struct nl_object *obj)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
refresh_object (platform, object, TRUE, NM_PLATFORM_REASON_INTERNAL);
|
||||
if (do_refresh_object)
|
||||
refresh_object (platform, object, TRUE, NM_PLATFORM_REASON_INTERNAL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -1984,7 +1985,7 @@ link_delete (NMPlatform *platform, int ifindex)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return delete_object (platform, build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_NONE));
|
||||
return delete_object (platform, build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_NONE), TRUE);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -3174,13 +3175,13 @@ ip6_address_add (NMPlatform *platform,
|
||||
static gboolean
|
||||
ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen)
|
||||
{
|
||||
return delete_object (platform, build_rtnl_addr (AF_INET, ifindex, &addr, NULL, plen, 0, 0, 0, NULL));
|
||||
return delete_object (platform, build_rtnl_addr (AF_INET, ifindex, &addr, NULL, plen, 0, 0, 0, NULL), TRUE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen)
|
||||
{
|
||||
return delete_object (platform, build_rtnl_addr (AF_INET6, ifindex, &addr, NULL, plen, 0, 0, 0, NULL));
|
||||
return delete_object (platform, build_rtnl_addr (AF_INET6, ifindex, &addr, NULL, plen, 0, 0, 0, NULL), TRUE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -3338,6 +3339,55 @@ ip6_route_add (NMPlatform *platform, int ifindex, struct in6_addr network, int p
|
||||
return add_object (platform, build_rtnl_route (AF_INET6, ifindex, &network, plen, &gateway, metric, mss));
|
||||
}
|
||||
|
||||
static struct rtnl_route *
|
||||
route_search_cache (struct nl_cache *cache, int family, int ifindex, const void *network, int plen, int metric)
|
||||
{
|
||||
guint32 network_clean[4], dst_clean[4];
|
||||
struct nl_object *object;
|
||||
|
||||
clear_host_address (family, network, plen, network_clean);
|
||||
|
||||
for (object = nl_cache_get_first (cache); object; object = nl_cache_get_next (object)) {
|
||||
struct nl_addr *dst;
|
||||
struct rtnl_route *rtnlroute = (struct rtnl_route *) object;
|
||||
|
||||
if (!_route_match (rtnlroute, family, ifindex))
|
||||
continue;
|
||||
|
||||
if (metric && metric != rtnl_route_get_priority (rtnlroute))
|
||||
continue;
|
||||
|
||||
dst = rtnl_route_get_dst (rtnlroute);
|
||||
if ( !dst
|
||||
|| nl_addr_get_family (dst) != family
|
||||
|| nl_addr_get_prefixlen (dst) != plen)
|
||||
continue;
|
||||
|
||||
clear_host_address (family, nl_addr_get_binary_addr (dst), plen, dst_clean);
|
||||
if (memcmp (dst_clean, network_clean,
|
||||
family == AF_INET ? sizeof (guint32) : sizeof (struct in6_addr)) != 0)
|
||||
continue;
|
||||
|
||||
rtnl_route_get (rtnlroute);
|
||||
return rtnlroute;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
refresh_route (NMPlatform *platform, int family, int ifindex, const void *network, int plen, int metric)
|
||||
{
|
||||
struct nl_cache *cache;
|
||||
auto_nl_object struct rtnl_route *cached_object = NULL;
|
||||
|
||||
cache = choose_cache_by_type (platform, family == AF_INET ? OBJECT_TYPE_IP4_ROUTE : OBJECT_TYPE_IP6_ROUTE);
|
||||
cached_object = route_search_cache (cache, family, ifindex, network, plen, metric);
|
||||
|
||||
if (cached_object)
|
||||
return refresh_object (platform, (struct nl_object *) cached_object, TRUE, NM_PLATFORM_REASON_INTERNAL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, int metric)
|
||||
{
|
||||
@@ -3345,12 +3395,17 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen
|
||||
struct rtnl_route *cached_object;
|
||||
struct nl_object *route = build_rtnl_route (AF_INET, ifindex, &network, plen, &gateway, metric, 0);
|
||||
uint8_t scope = RT_SCOPE_NOWHERE;
|
||||
struct nl_cache *cache;
|
||||
|
||||
g_return_val_if_fail (route, FALSE);
|
||||
|
||||
cache = choose_cache_by_type (platform, OBJECT_TYPE_IP4_ROUTE);
|
||||
|
||||
/* when deleting an IPv4 route, several fields of the provided route must match.
|
||||
* Lookup in the cache so that we hopefully get the right values. */
|
||||
cached_object = (struct rtnl_route *) nm_nl_cache_search (choose_cache_by_type (platform, OBJECT_TYPE_IP4_ROUTE), route);
|
||||
cached_object = (struct rtnl_route *) nl_cache_search (cache, route);
|
||||
if (!cached_object)
|
||||
cached_object = route_search_cache (cache, AF_INET, ifindex, &network, plen, metric);
|
||||
|
||||
if (!_nl_has_capability (1 /* NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE */)) {
|
||||
/* When searching for a matching IPv4 route to delete, the kernel
|
||||
@@ -3393,7 +3448,7 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen
|
||||
*/
|
||||
|
||||
rtnl_route_put (cached_object);
|
||||
return delete_object (platform, route);
|
||||
return delete_object (platform, route, FALSE) && refresh_route (platform, AF_INET, ifindex, &network, plen, metric);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -3401,17 +3456,20 @@ ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, in
|
||||
{
|
||||
struct in6_addr gateway = IN6ADDR_ANY_INIT;
|
||||
|
||||
return delete_object (platform, build_rtnl_route (AF_INET6, ifindex, &network, plen, &gateway, metric, 0));
|
||||
return delete_object (platform, build_rtnl_route (AF_INET6, ifindex, &network, plen, &gateway, metric, 0), FALSE) &&
|
||||
refresh_route (platform, AF_INET6, ifindex, &network, plen, metric);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ip_route_exists (NMPlatform *platform, int family, int ifindex, gpointer network, int plen, int metric)
|
||||
{
|
||||
auto_nl_object struct nl_object *object = build_rtnl_route (
|
||||
family, ifindex, network, plen, NULL, metric, 0);
|
||||
auto_nl_object struct nl_object *cached_object = nl_cache_search (
|
||||
choose_cache (platform, object), object);
|
||||
auto_nl_object struct nl_object *object = build_rtnl_route (family, ifindex, network,
|
||||
plen, NULL, metric, 0);
|
||||
struct nl_cache *cache = choose_cache (platform, object);
|
||||
auto_nl_object struct nl_object *cached_object = nl_cache_search (cache, object);
|
||||
|
||||
if (!cached_object)
|
||||
cached_object = (struct nl_object *) route_search_cache (cache, family, ifindex, network, plen, metric);
|
||||
return !!cached_object;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user