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);