platform: fix udev/kernel interface race conditions

A network interface is only exposed if it's recognized by both netlink
cache and udev.
This commit is contained in:
Pavel Šimerda
2013-07-26 17:03:39 +02:00
parent 423307f6bc
commit aca9b9b570
7 changed files with 117 additions and 69 deletions

View File

@@ -754,13 +754,6 @@ announce_object (NMPlatform *platform, const struct nl_object *object, ObjectSta
ObjectType object_type = object_type_from_nl_object (object);
const char *sig = signal_by_type_and_status[object_type][status];
if (object_type == LINK && status == ADDED) {
/* We have to wait until udev has registered the device; we'll
* emit NM_PLATFORM_LINK_ADDED from udev_device_added().
*/
return;
}
switch (object_type) {
case LINK:
{
@@ -768,6 +761,22 @@ announce_object (NMPlatform *platform, const struct nl_object *object, ObjectSta
link_init (platform, &device, (struct rtnl_link *) object);
/* Skip devices not yet discovered by udev. They will be announced
* by udev_device_added(). This doesn't apply to removed devices, as
* those come either from udev_device_removed(), event_notification()
* or link_delete() which block the announcment themselves when
* appropriate.
*/
switch (status) {
case ADDED:
case CHANGED:
if (!device.driver)
return;
break;
default:
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
@@ -785,7 +794,7 @@ announce_object (NMPlatform *platform, const struct nl_object *object, ObjectSta
default:
break;
}
g_signal_emit_by_name (platform, sig, device.ifindex, &device);
}
return;
@@ -922,7 +931,6 @@ delete_object (NMPlatform *platform, struct nl_object *obj)
}
nl_cache_remove (cached_object);
announce_object (platform, cached_object, REMOVED);
return TRUE;
@@ -984,6 +992,16 @@ event_notification (struct nl_msg *msg, gpointer user_data)
return NL_OK;
nl_cache_remove (cached_object);
/* Don't announced removed interfaces that are not recognized by
* udev. They were either not yet discovered or they have been
* already removed and announced.
*/
if (event == RTM_DELLINK) {
int ifindex = rtnl_link_get_ifindex ((struct rtnl_link *) object);
if (!g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (ifindex)))
return NL_OK;
}
announce_object (platform, cached_object, REMOVED);
return NL_OK;
@@ -1164,8 +1182,10 @@ link_get (NMPlatform *platform, int ifindex)
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
struct rtnl_link *rtnllink = rtnl_link_get (priv->link_cache, ifindex);
if (!rtnllink)
if (!rtnllink || !g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (ifindex))) {
platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
return NULL;
}
return rtnllink;
}
@@ -1198,6 +1218,13 @@ link_change (NMPlatform *platform, int ifindex, struct rtnl_link *change)
static gboolean
link_delete (NMPlatform *platform, int ifindex)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
if (!g_hash_table_lookup (priv->udev_devices, GINT_TO_POINTER (ifindex))) {
platform->error = NM_PLATFORM_ERROR_NOT_FOUND;
return FALSE;
}
return delete_object (platform, build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_NONE));
}
@@ -2252,7 +2279,6 @@ udev_device_added (NMPlatform *platform,
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
auto_nl_object struct rtnl_link *rtnllink = NULL;
const char *ifname, *devtype;
NMPlatformLink link;
int ifindex;
ifname = g_udev_device_get_name (udev_device);
@@ -2286,17 +2312,17 @@ udev_device_added (NMPlatform *platform,
return;
}
rtnllink = link_get (platform, ifindex);
g_hash_table_insert (priv->udev_devices, GINT_TO_POINTER (ifindex),
g_object_ref (udev_device));
/* Don't announce devices that have not yet been discovered via Netlink. */
rtnllink = rtnl_link_get (priv->link_cache, ifindex);
if (!rtnllink) {
debug ("%s: not found in link cache, ignoring...", ifname);
return;
}
g_hash_table_insert (priv->udev_devices, GINT_TO_POINTER (ifindex),
g_object_ref (udev_device));
link_init (platform, &link, rtnllink);
g_signal_emit_by_name (platform, NM_PLATFORM_LINK_ADDED, ifindex, &link);
announce_object (platform, (struct nl_object *) rtnllink, ADDED);
}
static void
@@ -2304,7 +2330,7 @@ udev_device_removed (NMPlatform *platform,
GUdevDevice *udev_device)
{
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
int ifindex;
int ifindex = 0;
if (g_udev_device_get_sysfs_attr (udev_device, "ifindex")) {
ifindex = g_udev_device_get_sysfs_attr_as_int (udev_device, "ifindex");
@@ -2324,6 +2350,14 @@ udev_device_removed (NMPlatform *platform,
}
}
}
/* Announce device removal if it's still in the Netlink cache. */
if (ifindex) {
struct rtnl_link *device = rtnl_link_get (priv->link_cache, ifindex);
if (device)
announce_object (platform, (struct nl_object *) device, REMOVED);
}
}
static void