diff options
author | Pavel Šimerda <psimerda@redhat.com> | 2013-08-02 14:25:07 +0200 |
---|---|---|
committer | Pavel Šimerda <psimerda@redhat.com> | 2013-08-02 23:04:46 +0200 |
commit | c1bbacae4c64e0bffea33a031709093aaf3dd3a0 (patch) | |
tree | 840ec43288e9fd5daac84ebf3ece88b65ad5f338 | |
parent | d85ae7438899f5d40386862ff47ad413be4a9117 (diff) | |
download | NetworkManager-c1bbacae4c64e0bffea33a031709093aaf3dd3a0.tar.gz |
platform: re-check potentially removed objects
-rw-r--r-- | src/platform/nm-linux-platform.c | 76 |
1 files changed, 55 insertions, 21 deletions
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 0f5cef385c..2a61d1f19c 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -81,6 +81,18 @@ nm_linux_platform_setup (void) /* libnl library workarounds and additions */ /* Automatic deallocation of local variables */ +#define auto_nl_cache __attribute__((cleanup(put_nl_cache))) +static void +put_nl_cache (void *ptr) +{ + struct nl_cache **cache = ptr; + + if (cache && *cache) { + nl_cache_free (*cache); + *cache = NULL; + } +} + #define auto_nl_object __attribute__((cleanup(put_nl_object))) static void put_nl_object (void *ptr) @@ -724,18 +736,13 @@ choose_cache (NMPlatform *platform, struct nl_object *object) } } -static void -remove_if_ifindex (struct nl_object *object, gpointer user_data) +static gboolean +object_has_ifindex (struct nl_object *object, int ifindex) { - int ifindex = *(int *) user_data; - switch (object_type_from_nl_object (object)) { case IP4_ADDRESS: case IP6_ADDRESS: - if (ifindex != rtnl_addr_get_ifindex ((struct rtnl_addr *) object)) - break; - nl_cache_remove (object); - break; + return ifindex == rtnl_addr_get_ifindex ((struct rtnl_addr *) object); case IP4_ROUTE: case IP6_ROUTE: { @@ -743,15 +750,29 @@ remove_if_ifindex (struct nl_object *object, gpointer user_data) struct rtnl_nexthop *nexthop; if (rtnl_route_get_nnexthops (rtnlroute) != 1) - break; + return FALSE; nexthop = rtnl_route_nexthop_n (rtnlroute, 0); - if (ifindex != rtnl_route_nh_get_ifindex (nexthop)) - break; - nl_cache_remove (object); + + return ifindex == rtnl_route_nh_get_ifindex (nexthop); } - break; default: - break; + g_assert_not_reached (); + } +} + +static gboolean refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed); + +static void +check_cache_items (NMPlatform *platform, struct nl_cache *cache, int ifindex) +{ + auto_nl_cache struct nl_cache *cloned_cache = nl_cache_clone (cache); + struct nl_object *object; + + for (object = nl_cache_get_first (cloned_cache); object; object = nl_cache_get_next (object)) { + debug ("cache %p object %p", cloned_cache, object); + g_assert (nl_object_get_cache (object) == cloned_cache); + if (object_has_ifindex (object, ifindex)) + refresh_object (platform, object, TRUE); } } @@ -785,19 +806,20 @@ announce_object (NMPlatform *platform, const struct nl_object *object, ObjectSta break; } - /* In some cases, the link action is followed by address and/or - * route action. Kernel silently removes routes when interface - * goes !IFF_UP and we also need to handle addresses and routes - * for removed network interfaces. + /* Link deletion or setting down is sometimes accompanied by address + * and/or route deletion. + * + * More precisely, kernel removes routes when interface goes !IFF_UP and + * removes both addresses and routes when interface is removed. */ switch (status) { case CHANGED: if (!device.connected) - nl_cache_foreach (priv->route_cache, remove_if_ifindex, &device.ifindex); + check_cache_items (platform, priv->route_cache, device.ifindex); break; case REMOVED: - nl_cache_foreach (priv->address_cache, remove_if_ifindex, &device.ifindex); - nl_cache_foreach (priv->route_cache, remove_if_ifindex, &device.ifindex); + check_cache_items (platform, priv->address_cache, device.ifindex); + check_cache_items (platform, priv->route_cache, device.ifindex); break; default: break; @@ -811,6 +833,18 @@ announce_object (NMPlatform *platform, const struct nl_object *object, ObjectSta NMPlatformIP4Address address; init_ip4_address (&address, (struct rtnl_addr *) object); + + /* Address deletion is sometimes accompanied by route deletion. We need to + * check all routes belonging to the same interface. + */ + switch (status) { + case REMOVED: + check_cache_items (platform, priv->route_cache, address.ifindex); + break; + default: + break; + } + g_signal_emit_by_name (platform, sig, address.ifindex, &address); } return; |