From c1a66936f343122e5991a25f07dd1d74a05ff68a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 10 Feb 2012 13:25:39 -0600 Subject: [PATCH] core: don't create virtual interfaces we already have Make sure we don't already have an NMDevice for this interface before creating it, and also when creating the interface, make a new NMDevice for it immediately to prevent a race between telling the kernel to create the interface via netlink, and when udev later tells us about it. In between there we could be triggered to try creating the interface again. --- src/nm-device.c | 8 ++- src/nm-manager.c | 129 ++++++++++++++++++++++++++++++++++++++++------- src/nm-system.c | 16 +++--- src/nm-system.h | 2 +- 4 files changed, 124 insertions(+), 31 deletions(-) diff --git a/src/nm-device.c b/src/nm-device.c index 3174b7c57..7f089f826 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -3563,7 +3563,11 @@ set_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_UDI: - /* construct-only */ + /* Only virtual interfaces can set UDI post-construction */ + if (priv->initialized) + g_return_if_fail (nm_system_get_iface_type (priv->ifindex, NULL) != NM_IFACE_TYPE_UNSPEC); + + g_free (priv->udi); priv->udi = g_strdup (g_value_get_string (value)); break; case PROP_IFACE: @@ -3746,7 +3750,7 @@ nm_device_class_init (NMDeviceClass *klass) "UDI", "Unique Device Identifier", NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_IFACE, diff --git a/src/nm-manager.c b/src/nm-manager.c index 13ff41f70..10394fe46 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -888,6 +888,27 @@ get_active_connections (NMManager *manager, NMConnection *filter) /* Settings stuff via NMSettings */ /*******************************************************************/ +/** + * get_virtual_iface_name: + * @self: the #NMManager + * @connection: the #NMConnection representing a virtual interface + * + * Given @connection, returns the interface name that the connection + * would represent. If the interface name is not given by the connection, + * this may require constructing it based on information in the connection + * and existing network interfaces. + * + * Returns: the expected interface name (caller takes ownership), or %NULL + */ +static char * +get_virtual_iface_name (NMManager *self, NMConnection *connection) +{ + if (nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME)) + return g_strdup (nm_connection_get_virtual_iface_name (connection)); + + return NULL; +} + static gboolean connection_needs_virtual_device (NMConnection *connection) { @@ -897,36 +918,98 @@ connection_needs_virtual_device (NMConnection *connection) return FALSE; } -static gboolean -system_update_virtual_device (NMConnection *connection) +static char * +get_virtual_iface_placeholder_udi (void) { - if (nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME)) { - NMSettingBond *s_bond; + static guint32 id = 0; - s_bond = nm_connection_get_setting_bond (connection); - g_assert (s_bond); + return g_strdup_printf ("/virtual/device/placeholder/%d", id++); +} - return nm_system_add_bonding_master (s_bond); +/** + * system_create_virtual_device: + * @self: the #NMManager + * @connection: the connection which might require a virtual device + * + * If @connection requires a virtual device and one does not yet exist for it, + * creates that device. + * + * Returns: the #NMDevice if successfully created, NULL if not + */ +static NMDevice * +system_create_virtual_device (NMManager *self, NMConnection *connection) +{ + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + GSList *iter; + char *iface = NULL, *udi; + NMDevice *device = NULL; + int master_ifindex = -1; + const char *driver = NULL; + + iface = get_virtual_iface_name (self, connection); + if (!iface) { + nm_log_warn (LOGD_DEVICE, "(%s) failed to determine virtual interface name", + nm_connection_get_id (connection)); + return NULL; } - return TRUE; + /* Make sure we didn't create a device for this connection already */ + for (iter = priv->devices; iter; iter = g_slist_next (iter)) { + NMDevice *candidate = iter->data; + GError *error = NULL; + + if ( g_strcmp0 (nm_device_get_iface (candidate), iface) == 0 + || nm_device_check_connection_compatible (candidate, connection, &error)) { + g_clear_error (&error); + goto out; + } + g_clear_error (&error); + } + + if (nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME)) { + if (!nm_system_add_bonding_master (iface)) { + nm_log_warn (LOGD_DEVICE, "(%s): failed to add bonding master interface for '%s'", + iface, nm_connection_get_id (connection)); + goto out; + } + driver = "bonding"; + } + + if (driver) { + udi = get_virtual_iface_placeholder_udi (); + device = nm_device_ethernet_new (udi, iface, driver); + g_free (udi); + if (device) + add_device (self, device); + else + nm_log_warn (LOGD_DEVICE, "(%s) failed to add virtual interface device", iface); + } + +out: + g_free (iface); + return device; } static void -system_create_virtual_devices (NMSettings *settings) +system_create_virtual_devices (NMManager *self) { + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GSList *iter, *connections; - nm_log_info (LOGD_CORE, "Creating virtual devices"); + nm_log_dbg (LOGD_CORE, "creating virtual devices..."); - connections = nm_settings_get_connections (settings); + connections = nm_settings_get_connections (priv->settings); for (iter = connections; iter; iter = g_slist_next (iter)) { - NMConnection *connection = NM_CONNECTION (iter->data); + NMConnection *connection = iter->data; + NMSettingConnection *s_con = nm_connection_get_setting_connection (connection); - if (connection_needs_virtual_device (connection)) - system_update_virtual_device (connection); + g_assert (s_con); + if (connection_needs_virtual_device (connection)) { + /* We only create a virtual interface if the connection can autoconnect */ + if (nm_setting_connection_get_autoconnect (s_con)) + system_create_virtual_device (self, connection); + } } - g_slist_free (connections); } @@ -938,7 +1021,7 @@ connection_added (NMSettings *settings, bluez_manager_resync_devices (manager); if (connection_needs_virtual_device (NM_CONNECTION (connection))) - system_update_virtual_device (NM_CONNECTION (connection)); + system_create_virtual_device (manager, NM_CONNECTION (connection)); } static void @@ -1540,6 +1623,11 @@ add_device (NMManager *self, NMDevice *device) nm_settings_device_added (priv->settings, device); g_signal_emit (self, signals[DEVICE_ADDED], 0, device); + /* New devices might be master interfaces for virtual interfaces; so we may + * need to create new virtual interfaces now. + */ + system_create_virtual_devices (self); + /* If the device has a connection it can assume, do that now */ if (existing && managed && nm_device_is_available (device)) { const char *ac_path; @@ -1937,8 +2025,13 @@ udev_device_added_cb (NMUdevManager *udev_mgr, GError *error = NULL; ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX"); - if (find_device_by_ifindex (self, ifindex)) + device = find_device_by_ifindex (self, ifindex); + if (device) { + /* If it's a virtual device we may need to update its UDI */ + if (nm_system_get_iface_type (ifindex, iface) != NM_IFACE_TYPE_UNSPEC) + g_object_set (G_OBJECT (device), NM_DEVICE_UDI, sysfs_path, NULL); return; + } /* Try registered device factories */ for (iter = priv->factories; iter; iter = g_slist_next (iter)) { @@ -3046,7 +3139,7 @@ nm_manager_start (NMManager *self) * Connections added before the manager is started do not emit * connection-added signals thus devices have to be created manually. */ - system_create_virtual_devices (priv->settings); + system_create_virtual_devices (self); } static gboolean diff --git a/src/nm-system.c b/src/nm-system.c index 04fc954a9..8202d821e 100644 --- a/src/nm-system.c +++ b/src/nm-system.c @@ -1281,34 +1281,30 @@ nm_system_apply_bonding_config (NMSettingBond *s_bond) /** * nm_system_add_bonding_master: - * @setting: bonding setting + * @iface: the interface name for the new bond master * * Adds a virtual bonding device if it does not exist yet. * * Returns: %TRUE on success, %FALSE on failure */ gboolean -nm_system_add_bonding_master (NMSettingBond *setting) +nm_system_add_bonding_master (const char *iface) { struct nl_sock *sock; - const char *name; int err; + g_return_val_if_fail (iface != NULL, FALSE); + sock = nm_netlink_get_default_handle (); - name = nm_setting_bond_get_interface_name (setting); - g_assert (name); /* Existing bonding devices with matching name will be reused */ - err = rtnl_link_bond_add (sock, name, NULL); + err = rtnl_link_bond_add (sock, iface, NULL); if (err < 0) { nm_log_err (LOGD_DEVICE, "(%s): error %d returned from " "rtnl_link_bond_add(): %s", - name, err, nl_geterror (err)); + iface, err, nl_geterror (err)); return FALSE; } - - nm_system_apply_bonding_config (setting); - return TRUE; } diff --git a/src/nm-system.h b/src/nm-system.h index 9ee2b0625..3f3ae63b1 100644 --- a/src/nm-system.h +++ b/src/nm-system.h @@ -90,7 +90,7 @@ gboolean nm_system_iface_set_mtu (int ifindex, guint32 mtu); gboolean nm_system_iface_set_mac (int ifindex, const struct ether_addr *mac); gboolean nm_system_apply_bonding_config (NMSettingBond *s_bond); -gboolean nm_system_add_bonding_master (NMSettingBond *setting); +gboolean nm_system_add_bonding_master (const char *iface); gboolean nm_system_iface_enslave (NMDevice *slave, NMDevice *master); gboolean nm_system_iface_release (NMDevice *slave, NMDevice *master);