diff options
author | Thomas Haller <thaller@redhat.com> | 2014-05-19 15:26:00 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2014-05-19 15:26:00 +0200 |
commit | e3221e6a1243abd59dfa6952f8ed599fbe2bf111 (patch) | |
tree | 2ba2d4c981b787c5da0ca5b2de4b0e17a155b083 | |
parent | c2e99b181f42dd4673990edd7347e84b007c164d (diff) | |
parent | 019bf7512d0c8ece3e9051f609f10ecc1df7223a (diff) |
platform: merge branch 'th/bgo726273_platform_delete_ip4_route_workaround'
https://bugzilla.gnome.org/show_bug.cgi?id=726273
Signed-off-by: Thomas Haller <thaller@redhat.com>
-rw-r--r-- | src/platform/nm-linux-platform.c | 100 |
1 files changed, 79 insertions, 21 deletions
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 44a119ce05..2ecd92bcb7 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -23,6 +23,7 @@ #include <unistd.h> #include <sys/socket.h> #include <fcntl.h> +#include <dlfcn.h> #include <netinet/icmp6.h> #include <netinet/in.h> #include <linux/ip.h> @@ -58,6 +59,15 @@ #define warning(...) nm_log_warn (LOGD_PLATFORM, __VA_ARGS__) #define error(...) nm_log_err (LOGD_PLATFORM, __VA_ARGS__) + +struct libnl_vtable +{ + void *handle; + + int (*f_nl_has_capability) (int capability); +}; + + typedef struct { struct nl_sock *nlh; struct nl_sock *nlh_event; @@ -89,6 +99,43 @@ nm_linux_platform_setup (void) /******************************************************************/ +static int +_nl_f_nl_has_capability (int capability) +{ + return FALSE; +} + +static struct libnl_vtable * +_nl_get_vtable () +{ + static struct libnl_vtable vtable; + + if (G_UNLIKELY (!vtable.f_nl_has_capability)) { + void *handle; + + handle = dlopen ("libnl-3.so", RTLD_LAZY | RTLD_NOLOAD); + if (handle) { + vtable.handle = handle; + vtable.f_nl_has_capability = dlsym (handle, "nl_has_capability"); + } + + if (!vtable.f_nl_has_capability) + vtable.f_nl_has_capability = &_nl_f_nl_has_capability; + + g_return_val_if_fail (vtable.handle, &vtable); + } + + return &vtable; +} + +static gboolean +_nl_has_capability (int capability) +{ + return (_nl_get_vtable ()->f_nl_has_capability) (capability); +} + +/******************************************************************/ + /* libnl library workarounds and additions */ /* Automatic deallocation of local variables */ @@ -1447,17 +1494,13 @@ delete_object (NMPlatform *platform, struct nl_object *obj) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); auto_nl_object struct nl_object *obj_cleanup = obj; - auto_nl_object struct nl_object *cached_object = NULL; - struct nl_object *object; + struct nl_object *object = obj; int object_type; int nle; object_type = object_type_from_nl_object (obj); g_return_val_if_fail (object_type != OBJECT_TYPE_UNKNOWN, FALSE); - cached_object = nm_nl_cache_search (choose_cache_by_type (platform, object_type), obj); - object = cached_object ? cached_object : obj; - switch (object_type) { case OBJECT_TYPE_LINK: nle = rtnl_link_delete (priv->nlh, (struct rtnl_link *) object); @@ -3280,25 +3323,40 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen { in_addr_t gateway = 0; struct nl_object *route = build_rtnl_route (AF_INET, ifindex, &network, plen, &gateway, metric, 0); + uint8_t scope = RT_SCOPE_NOWHERE; g_return_val_if_fail (route, FALSE); - /* When searching for a matching IPv4 route to delete, the kernel - * searches for a matching scope, unless the RTM_DELROUTE message - * specifies RT_SCOPE_NOWHERE (see fib_table_delete()). - * - * However, if we set the scope of @rtnlroute to RT_SCOPE_NOWHERE (or - * leave it unset), rtnl_route_build_msg() will reset the scope to - * rtnl_route_guess_scope() -- which might be the wrong scope. - * - * As a workaround, we set the scope to RT_SCOPE_UNIVERSE, so libnl - * will not overwrite it. But this only works if we guess correctly. - * - * As a better workaround, we don't use @rtnlroute as argument for - * rtnl_route_delete(), but we look into our cache, if we already have - * this route ready. - **/ - rtnl_route_set_scope ((struct rtnl_route *) route, RT_SCOPE_UNIVERSE); + if (!_nl_has_capability (1 /* NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE */)) { + /* When searching for a matching IPv4 route to delete, the kernel + * searches for a matching scope, unless the RTM_DELROUTE message + * specifies RT_SCOPE_NOWHERE (see fib_table_delete()). + * + * However, if we set the scope of @rtnlroute to RT_SCOPE_NOWHERE (or + * leave it unset), rtnl_route_build_msg() will reset the scope to + * rtnl_route_guess_scope() -- which probably guesses wrong. + * + * As a workaround, we look at the cached route and use that scope. + * + * Newer versions of libnl, no longer reset the scope if explicitly set to RT_SCOPE_NOWHERE. + * So, this workaround is only needed unless we have NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE. + **/ + struct nl_object *cached_object; + + cached_object = nm_nl_cache_search (choose_cache_by_type (platform, OBJECT_TYPE_IP4_ROUTE), route); + if (cached_object) { + scope = rtnl_route_get_scope ((struct rtnl_route *) cached_object); + nl_object_put (cached_object); + } + + if (scope == RT_SCOPE_NOWHERE) { + /* If we would set the scope to RT_SCOPE_NOWHERE, libnl would guess the scope. + * But probably it will guess 'link' because we don't set the next hop + * of the route we are about to delete. A better guess is 'global'. */ + scope = RT_SCOPE_UNIVERSE; + } + } + rtnl_route_set_scope ((struct rtnl_route *) route, scope); return delete_object (platform, route); } |