From a312dc6d1d24cd393a3a3dd728964babdd6ac5e7 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 28 May 2014 18:46:12 +0200 Subject: [PATCH] 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 f0daf90298d1bd9cafac7b9c02dc905327e0b85a, but before the lookup to the cached object would fail for any non-zero 'tos'. Signed-off-by: Thomas Haller --- src/platform/nm-linux-platform.c | 82 +++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 12 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index f3464fcbc..7b91f1fbc 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -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; }