diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 2708dad89e..6f8caeb40b 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -421,42 +421,56 @@ enslave_slave (NMDevice *device, success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)); nm_device_bring_up (slave, TRUE, &no_firmware); - } - if (success) { + if (!success) + return FALSE; + nm_log_info (LOGD_BOND, "(%s): enslaved bond slave %s", iface, slave_iface); - g_object_notify (G_OBJECT (device), "slaves"); - } + } else + nm_log_info (LOGD_BOND, "(%s): bond slave %s was enslaved", iface, slave_iface); - return success; + g_object_notify (G_OBJECT (device), NM_DEVICE_BOND_SLAVES); + return TRUE; } static gboolean -release_slave (NMDevice *device, NMDevice *slave) +release_slave (NMDevice *device, + NMDevice *slave, + gboolean configure) { - gboolean success, no_firmware = FALSE; + gboolean success = TRUE, no_firmware = FALSE; - success = nm_platform_link_release (nm_device_get_ip_ifindex (device), - nm_device_get_ip_ifindex (slave)); + if (configure) { + success = nm_platform_link_release (nm_device_get_ip_ifindex (device), + nm_device_get_ip_ifindex (slave)); - if (success) { - nm_log_info (LOGD_BOND, "(%s): released bond slave %s", - nm_device_get_ip_iface (device), - nm_device_get_ip_iface (slave)); + if (success) { + nm_log_info (LOGD_BOND, "(%s): released bond slave %s", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave)); + } else { + nm_log_warn (LOGD_BOND, "(%s): failed to release bond slave %s", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave)); + } } else { - nm_log_warn (LOGD_BOND, "(%s): failed to release bond slave %s", + nm_log_info (LOGD_BOND, "(%s): bond slave %s was released", nm_device_get_ip_iface (device), nm_device_get_ip_iface (slave)); } - g_object_notify (G_OBJECT (device), "slaves"); - /* Kernel bonding code "closes" the slave when releasing it, (which clears - * IFF_UP), so we must bring it back up here to ensure carrier changes and - * other state is noticed by the now-released slave. - */ - if (!nm_device_bring_up (slave, TRUE, &no_firmware)) { - nm_log_warn (LOGD_BOND, "(%s): released bond slave could not be brought up.", - nm_device_get_iface (slave)); + if (success) + g_object_notify (G_OBJECT (device), NM_DEVICE_BOND_SLAVES); + + if (configure) { + /* Kernel bonding code "closes" the slave when releasing it, (which clears + * IFF_UP), so we must bring it back up here to ensure carrier changes and + * other state is noticed by the now-released slave. + */ + if (!nm_device_bring_up (slave, TRUE, &no_firmware)) { + nm_log_warn (LOGD_BOND, "(%s): released bond slave could not be brought up.", + nm_device_get_iface (slave)); + } } return success; diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index ad2dc7c8bb..5c99c0e998 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -391,30 +391,43 @@ enslave_slave (NMDevice *device, return FALSE; commit_slave_options (slave, nm_connection_get_setting_bridge_port (connection)); + + nm_log_info (LOGD_BRIDGE, "(%s): attached bridge port %s", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave)); + } else { + nm_log_info (LOGD_BRIDGE, "(%s): bridge port %s was attached", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave)); } - nm_log_info (LOGD_BRIDGE, "(%s): attached bridge port %s", - nm_device_get_ip_iface (device), - nm_device_get_ip_iface (slave)); g_object_notify (G_OBJECT (device), NM_DEVICE_BRIDGE_SLAVES); return TRUE; } static gboolean -release_slave (NMDevice *device, NMDevice *slave) +release_slave (NMDevice *device, + NMDevice *slave, + gboolean configure) { - gboolean success; + gboolean success = TRUE; - success = nm_platform_link_release (nm_device_get_ip_ifindex (device), - nm_device_get_ip_ifindex (slave)); + if (configure) { + success = nm_platform_link_release (nm_device_get_ip_ifindex (device), + nm_device_get_ip_ifindex (slave)); - if (success) { - nm_log_info (LOGD_BRIDGE, "(%s): detached bridge port %s", - nm_device_get_ip_iface (device), - nm_device_get_ip_iface (slave)); + if (success) { + nm_log_info (LOGD_BRIDGE, "(%s): detached bridge port %s", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave)); + } else { + nm_log_warn (LOGD_BRIDGE, "(%s): failed to detach bridge port %s", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave)); + } } else { - nm_log_warn (LOGD_BRIDGE, "(%s): failed to detach bridge port %s", + nm_log_info (LOGD_BRIDGE, "(%s): bridge port %s was detached", nm_device_get_ip_iface (device), nm_device_get_ip_iface (slave)); } diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index 8dae02e0bd..24bb0b3362 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -97,5 +97,6 @@ void nm_device_master_check_slave_physical_port (NMDevice *dev, NMDevice *slave, void nm_device_set_carrier (NMDevice *device, gboolean carrier); void nm_device_emit_recheck_auto_activate (NMDevice *device); +void nm_device_queue_recheck_assume (NMDevice *device); #endif /* NM_DEVICE_PRIVATE_H */ diff --git a/src/devices/nm-device-team.c b/src/devices/nm-device-team.c index e672f6e1a3..63c2673fd9 100644 --- a/src/devices/nm-device-team.c +++ b/src/devices/nm-device-team.c @@ -691,42 +691,57 @@ enslave_slave (NMDevice *device, success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)); nm_device_bring_up (slave, TRUE, &no_firmware); - } - if (success) { + if (!success) + return FALSE; + nm_log_info (LOGD_TEAM, "(%s): enslaved team port %s", iface, slave_iface); - g_object_notify (G_OBJECT (device), "slaves"); - } + } else + nm_log_info (LOGD_TEAM, "(%s): team port %s was enslaved", iface, slave_iface); - return success; + g_object_notify (G_OBJECT (device), NM_DEVICE_TEAM_SLAVES); + + return TRUE; } static gboolean -release_slave (NMDevice *device, NMDevice *slave) +release_slave (NMDevice *device, + NMDevice *slave, + gboolean configure) { - gboolean success, no_firmware = FALSE; + gboolean success = TRUE, no_firmware = FALSE; - success = nm_platform_link_release (nm_device_get_ip_ifindex (device), - nm_device_get_ip_ifindex (slave)); + if (configure) { + success = nm_platform_link_release (nm_device_get_ip_ifindex (device), + nm_device_get_ip_ifindex (slave)); - if (success) { - nm_log_info (LOGD_TEAM, "(%s): released team port %s", - nm_device_get_ip_iface (device), - nm_device_get_ip_iface (slave)); + if (success) { + nm_log_info (LOGD_TEAM, "(%s): released team port %s", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave)); + } else { + nm_log_warn (LOGD_TEAM, "(%s): failed to release team port %s", + nm_device_get_ip_iface (device), + nm_device_get_ip_iface (slave)); + } } else { - nm_log_warn (LOGD_TEAM, "(%s): failed to release team port %s", + nm_log_info (LOGD_TEAM, "(%s): team port %s was released", nm_device_get_ip_iface (device), nm_device_get_ip_iface (slave)); } - g_object_notify (G_OBJECT (device), "slaves"); - /* Kernel team code "closes" the port when releasing it, (which clears - * IFF_UP), so we must bring it back up here to ensure carrier changes and - * other state is noticed by the now-released port. - */ - if (!nm_device_bring_up (slave, TRUE, &no_firmware)) { - nm_log_warn (LOGD_TEAM, "(%s): released team port could not be brought up.", - nm_device_get_iface (slave)); + if (success) + g_object_notify (G_OBJECT (device), NM_DEVICE_TEAM_SLAVES); + + if (configure) { + /* Kernel team code "closes" the port when releasing it, (which clears + * IFF_UP), so we must bring it back up here to ensure carrier changes and + * other state is noticed by the now-released port. + */ + if (!nm_device_bring_up (slave, TRUE, &no_firmware)) { + nm_log_warn (LOGD_TEAM, "(%s): released team port could not be brought up.", + nm_device_get_iface (slave)); + } } return success; diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index c4fd15a12a..2b576add4a 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -105,6 +105,7 @@ enum { IP6_CONFIG_CHANGED, REMOVED, RECHECK_AUTO_ACTIVATE, + RECHECK_ASSUME, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -237,6 +238,7 @@ typedef struct { gpointer act_source_func; guint act_source6_id; gpointer act_source6_func; + guint recheck_assume_id; /* Link stuff */ guint link_connected_id; @@ -855,6 +857,17 @@ nm_device_get_type_desc (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->type_desc; } +static gboolean +nm_device_uses_generated_connection (NMDevice *self) +{ + NMConnection *connection; + + connection = nm_device_get_connection (self); + if (!connection) + return FALSE; + return nm_settings_connection_get_nm_generated (NM_SETTINGS_CONNECTION (connection)); +} + static SlaveInfo * find_slave_info (NMDevice *self, NMDevice *slave) { @@ -883,7 +896,7 @@ free_slave_info (SlaveInfo *info) * nm_device_enslave_slave: * @dev: the master device * @slave: the slave device to enslave - * @connection: the slave device's connection + * @connection: (allow-none): the slave device's connection * * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team, * etc) then this function enslaves @slave. @@ -896,20 +909,27 @@ nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connectio { SlaveInfo *info; gboolean success = FALSE; + gboolean configure; g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (slave != NULL, FALSE); - g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); g_return_val_if_fail (NM_DEVICE_GET_CLASS (dev)->enslave_slave != NULL, FALSE); info = find_slave_info (dev, slave); if (!info) return FALSE; - g_warn_if_fail (info->enslaved == FALSE); - success = NM_DEVICE_GET_CLASS (dev)->enslave_slave (dev, slave, connection, info->configure); + if (info->enslaved) + success = TRUE; + else { + configure = (info->configure && connection != NULL); + if (configure) + g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); + + success = NM_DEVICE_GET_CLASS (dev)->enslave_slave (dev, slave, connection, configure); + info->enslaved = success; + } - info->enslaved = success; nm_device_slave_notify_enslave (info->slave, success); /* Ensure the device's hardware address is up-to-date; it often changes @@ -936,15 +956,17 @@ nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connectio * nm_device_release_one_slave: * @dev: the master device * @slave: the slave device to release + * @configure: whether @dev needs to actually release @slave * * If @dev is capable of enslaving other devices (ie it's a bridge, bond, team, - * etc) then this function releases the previously enslaved @slave. + * etc) then this function releases the previously enslaved @slave and/or + * updates the state of @dev and @slave to reflect its release. * * Returns: %TRUE on success, %FALSE on failure, if this device cannot enslave * other devices, or if @slave was never enslaved. */ static gboolean -nm_device_release_one_slave (NMDevice *dev, NMDevice *slave) +nm_device_release_one_slave (NMDevice *dev, NMDevice *slave, gboolean configure) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); SlaveInfo *info; @@ -960,13 +982,15 @@ nm_device_release_one_slave (NMDevice *dev, NMDevice *slave) priv->slaves = g_slist_remove (priv->slaves, info); if (info->enslaved) { - success = NM_DEVICE_GET_CLASS (dev)->release_slave (dev, slave); + success = NM_DEVICE_GET_CLASS (dev)->release_slave (dev, slave, configure); /* The release_slave() implementation logs success/failure (in the * correct device-specific log domain), so we don't have to do anything. */ } - if (priv->state == NM_DEVICE_STATE_FAILED) + if (!configure) + reason = NM_DEVICE_STATE_REASON_NONE; + else if (priv->state == NM_DEVICE_STATE_FAILED) reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED; else reason = priv->state_reason; @@ -1195,6 +1219,29 @@ device_link_changed (NMDevice *device, NMPlatformLink *info) nm_device_emit_recheck_auto_activate (device); } + /* Update slave status for external changes */ + if (info->master && !priv->enslaved) { + NMDevice *master; + + master = nm_manager_get_device_by_ifindex (nm_manager_get (), info->master); + if (master && NM_DEVICE_GET_CLASS (master)->enslave_slave) { + g_clear_object (&priv->master); + priv->master = g_object_ref (master); + nm_device_master_add_slave (master, device, FALSE); + nm_device_enslave_slave (master, device, NULL); + } else if (master) { + nm_log_info (LOGD_DEVICE, "(%s): enslaved to non-master-type device %s; ignoring", + nm_device_get_iface (device), + nm_device_get_iface (master)); + } else { + nm_log_warn (LOGD_DEVICE, "(%s): enslaved to unknown device %d %s", + nm_device_get_iface (device), + info->master, + nm_platform_link_get_name (info->master)); + } + } else if (priv->enslaved && !info->master) + nm_device_release_one_slave (priv->master, device, FALSE); + if (klass->link_changed) klass->link_changed (device, info); @@ -1317,8 +1364,6 @@ slave_state_changed (NMDevice *slave, slave_new_state, state_to_string (slave_new_state)); - g_assert (priv->state > NM_DEVICE_STATE_DISCONNECTED); - /* Don't try to enslave slaves until the master is ready */ if (priv->state < NM_DEVICE_STATE_CONFIG) return; @@ -1334,7 +1379,7 @@ slave_state_changed (NMDevice *slave, } if (release) { - nm_device_release_one_slave (self, slave); + nm_device_release_one_slave (self, slave, TRUE); /* Bridge/bond/team interfaces are left up until manually deactivated */ if (priv->slaves == NULL && priv->state == NM_DEVICE_STATE_ACTIVATED) { nm_log_dbg (LOGD_DEVICE, "(%s): last slave removed; remaining activated", @@ -1363,9 +1408,11 @@ nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure) g_return_val_if_fail (dev != NULL, FALSE); g_return_val_if_fail (slave != NULL, FALSE); - g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); g_return_val_if_fail (NM_DEVICE_GET_CLASS (dev)->enslave_slave != NULL, FALSE); + if (configure) + g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE); + if (!find_slave_info (dev, slave)) { info = g_malloc0 (sizeof (SlaveInfo)); info->slave = g_object_ref (slave); @@ -1480,10 +1527,14 @@ nm_device_master_release_slaves (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + /* Don't release the slaves if this connection doesn't belong to NM. */ + if (nm_device_uses_generated_connection (self)) + return; + while (priv->slaves) { SlaveInfo *info = priv->slaves->data; - nm_device_release_one_slave (self, info->slave); + nm_device_release_one_slave (self, info->slave, TRUE); } } @@ -1522,31 +1573,42 @@ nm_device_slave_notify_enslave (NMDevice *dev, gboolean success) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev); NMConnection *connection = nm_device_get_connection (dev); + gboolean activating = (priv->state == NM_DEVICE_STATE_IP_CONFIG); g_assert (priv->master); - g_warn_if_fail (priv->enslaved == FALSE); - g_warn_if_fail (priv->state == NM_DEVICE_STATE_IP_CONFIG); - if (success) { - nm_log_info (LOGD_DEVICE, - "Activation (%s) connection '%s' enslaved, continuing activation", - nm_device_get_iface (dev), - nm_connection_get_id (connection)); + if (!priv->enslaved) { + if (success) { + if (activating) { + nm_log_info (LOGD_DEVICE, + "Activation (%s) connection '%s' enslaved, continuing activation", + nm_device_get_iface (dev), + nm_connection_get_id (connection)); + } else { + nm_log_info (LOGD_DEVICE, + "(%s): enslaved to %s", + nm_device_get_iface (dev), + nm_device_get_iface (priv->master)); + } - priv->enslaved = TRUE; - g_object_notify (G_OBJECT (dev), NM_DEVICE_MASTER); - } else { - nm_log_warn (LOGD_DEVICE, - "Activation (%s) connection '%s' could not be enslaved", - nm_device_get_iface (dev), - nm_connection_get_id (connection)); + priv->enslaved = TRUE; + g_object_notify (G_OBJECT (dev), NM_DEVICE_MASTER); + } else if (activating) { + nm_log_warn (LOGD_DEVICE, + "Activation (%s) connection '%s' could not be enslaved", + nm_device_get_iface (dev), + nm_connection_get_id (connection)); + } } - priv->ip4_state = IP_DONE; - priv->ip6_state = IP_DONE; - nm_device_queue_state (dev, - success ? NM_DEVICE_STATE_SECONDARIES : NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_NONE); + if (activating) { + priv->ip4_state = IP_DONE; + priv->ip6_state = IP_DONE; + nm_device_queue_state (dev, + success ? NM_DEVICE_STATE_SECONDARIES : NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NONE); + } else + nm_device_queue_recheck_assume (dev); } /** @@ -1564,7 +1626,8 @@ nm_device_slave_notify_release (NMDevice *dev, NMDeviceStateReason reason) NMDeviceState new_state; const char *master_status; - if ( priv->state > NM_DEVICE_STATE_DISCONNECTED + if ( reason != NM_DEVICE_STATE_REASON_NONE + && priv->state > NM_DEVICE_STATE_DISCONNECTED && priv->state <= NM_DEVICE_STATE_ACTIVATED) { if (reason == NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED) { new_state = NM_DEVICE_STATE_FAILED; @@ -1584,6 +1647,11 @@ nm_device_slave_notify_release (NMDevice *dev, NMDeviceStateReason reason) master_status); nm_device_queue_state (dev, new_state, reason); + } else { + nm_log_info (LOGD_DEVICE, + "(%s): released from master %s", + nm_device_get_iface (dev), + nm_device_get_iface (priv->master)); } if (priv->enslaved) { @@ -1786,6 +1854,19 @@ nm_device_generate_connection (NMDevice *device) return NULL; } + if (ifindex) + master_ifindex = nm_platform_link_get_master (ifindex); + if (master_ifindex) { + NMDevice *master; + + master = nm_manager_get_device_by_ifindex (nm_manager_get (), master_ifindex); + if (!master || !nm_device_get_act_request (master)) { + nm_log_dbg (LOGD_DEVICE, "(%s): cannot generate connection for slave before its master (%s)", + ifname, nm_platform_link_get_name (master_ifindex)); + return NULL; + } + } + connection = nm_connection_new (); s_con = nm_setting_connection_new (); uuid = nm_utils_uuid_generate (); @@ -1803,9 +1884,7 @@ nm_device_generate_connection (NMDevice *device) nm_connection_add_setting (connection, s_con); /* If the device is a slave, update various slave settings */ - if (ifindex) - master_ifindex = nm_platform_link_get_master (ifindex); - if (master_ifindex > 0) { + if (master_ifindex) { const char *master_iface = nm_platform_link_get_name (master_ifindex); const char *slave_type = NULL; gboolean success = FALSE; @@ -1837,19 +1916,11 @@ nm_device_generate_connection (NMDevice *device) NULL); } else { /* Only regular and master devices get IP configuration; slaves do not */ - s_ip4 = nm_setting_ip4_config_new (); + s_ip4 = nm_ip4_config_create_setting (priv->ip4_config); nm_connection_add_setting (connection, s_ip4); - if (priv->ip4_config) - nm_ip4_config_update_setting (priv->ip4_config, (NMSettingIP4Config *) s_ip4); - else - g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NULL); - s_ip6 = nm_setting_ip6_config_new (); + s_ip6 = nm_ip6_config_create_setting (priv->ip6_config); nm_connection_add_setting (connection, s_ip6); - if (priv->ip6_config) - nm_ip6_config_update_setting (priv->ip6_config, (NMSettingIP6Config *) s_ip6); - else - g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL); } klass->update_connection (device, connection); @@ -1870,9 +1941,8 @@ nm_device_generate_connection (NMDevice *device) ip6_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); if ( g_strcmp0 (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0 && g_strcmp0 (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0 - && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con)) - && !nm_platform_link_supports_slaves (priv->ifindex)) { - nm_log_dbg (LOGD_DEVICE, "(%s): ignoring generated connection (no IP, not master, no slaves)", ifname); + && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con))) { + nm_log_dbg (LOGD_DEVICE, "(%s): ignoring generated connection (no IP and not slave)", ifname); g_object_unref (connection); connection = NULL; } @@ -2013,6 +2083,26 @@ nm_device_can_assume_connections (NMDevice *device) return !!NM_DEVICE_GET_CLASS (device)->update_connection; } +static gboolean +nm_device_emit_recheck_assume (gpointer self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + priv->recheck_assume_id = 0; + if (!nm_device_get_act_request (self) && (priv->ip4_config || priv->ip6_config)) + g_signal_emit (self, signals[RECHECK_ASSUME], 0); + return G_SOURCE_REMOVE; +} + +void +nm_device_queue_recheck_assume (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + if (nm_device_can_assume_connections (self) && !priv->recheck_assume_id) + priv->recheck_assume_id = g_idle_add (nm_device_emit_recheck_assume, self); +} + void nm_device_emit_recheck_auto_activate (NMDevice *self) { @@ -2293,9 +2383,13 @@ nm_device_activate_stage2_device_config (gpointer user_data) /* If we have slaves that aren't yet enslaved, do that now */ for (iter = priv->slaves; iter; iter = g_slist_next (iter)) { SlaveInfo *info = iter->data; + NMDeviceState slave_state = nm_device_get_state (info->slave); - if (nm_device_get_state (info->slave) == NM_DEVICE_STATE_IP_CONFIG) + if (slave_state == NM_DEVICE_STATE_IP_CONFIG) nm_device_enslave_slave (self, info->slave, nm_device_get_connection (info->slave)); + else if ( nm_device_uses_generated_connection (self) + && slave_state <= NM_DEVICE_STATE_DISCONNECTED) + nm_device_queue_recheck_assume (info->slave); } nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) successful.", iface); @@ -5229,6 +5323,19 @@ nm_device_set_ip4_config (NMDevice *self, if (old_config != priv->ip4_config && old_config) g_object_unref (old_config); + + if (nm_device_uses_generated_connection (self)) { + NMConnection *connection = nm_device_get_connection (self); + NMSetting *s_ip4; + + g_object_freeze_notify (G_OBJECT (connection)); + nm_connection_remove_setting (connection, NM_TYPE_SETTING_IP4_CONFIG); + s_ip4 = nm_ip4_config_create_setting (priv->ip4_config); + nm_connection_add_setting (connection, s_ip4); + g_object_thaw_notify (G_OBJECT (connection)); + } + + nm_device_queue_recheck_assume (self); } if (reason) @@ -5320,6 +5427,19 @@ nm_device_set_ip6_config (NMDevice *self, if (old_config != priv->ip6_config && old_config) g_object_unref (old_config); + + if (nm_device_uses_generated_connection (self)) { + NMConnection *connection = nm_device_get_connection (self); + NMSetting *s_ip6; + + g_object_freeze_notify (G_OBJECT (connection)); + nm_connection_remove_setting (connection, NM_TYPE_SETTING_IP6_CONFIG); + s_ip6 = nm_ip6_config_create_setting (priv->ip6_config); + nm_connection_add_setting (connection, s_ip6); + g_object_thaw_notify (G_OBJECT (connection)); + } + + nm_device_queue_recheck_assume (self); } if (reason) @@ -5764,6 +5884,11 @@ dispose (GObject *object) g_clear_pointer (&priv->ip6_saved_properties, g_hash_table_unref); + if (priv->recheck_assume_id) { + g_source_remove (priv->recheck_assume_id); + priv->recheck_assume_id = 0; + } + link_disconnect_action_cancel (self); if (priv->con_provider) { @@ -6412,6 +6537,13 @@ nm_device_class_init (NMDeviceClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + signals[RECHECK_ASSUME] = + g_signal_new (NM_DEVICE_RECHECK_ASSUME, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), G_TYPE_FROM_CLASS (klass), &dbus_glib_nm_device_object_info); diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 9193264c97..1e2f39a31e 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -76,6 +76,7 @@ #define NM_DEVICE_IP6_CONFIG_CHANGED "ip6-config-changed" #define NM_DEVICE_REMOVED "removed" #define NM_DEVICE_RECHECK_AUTO_ACTIVATE "recheck-auto-activate" +#define NM_DEVICE_RECHECK_ASSUME "recheck-assume" G_BEGIN_DECLS @@ -197,7 +198,8 @@ typedef struct { gboolean configure); gboolean (* release_slave) (NMDevice *self, - NMDevice *slave); + NMDevice *slave, + gboolean configure); gboolean (* have_any_ready_slaves) (NMDevice *self, const GSList *slaves); diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 12aac78519..4d149f25ab 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -379,16 +379,23 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIP4Config *setting) g_object_thaw_notify (G_OBJECT (config)); } -void -nm_ip4_config_update_setting (const NMIP4Config *config, NMSettingIP4Config *setting) +NMSetting * +nm_ip4_config_create_setting (const NMIP4Config *config) { + NMSettingIP4Config *s_ip4; guint32 gateway; guint naddresses, nroutes, nnameservers, nsearches; const char *method = NULL; int i; - if (!config) - return; + s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ()); + + if (!config) { + g_object_set (s_ip4, + NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, + NULL); + return NM_SETTING (s_ip4); + } gateway = nm_ip4_config_get_gateway (config); naddresses = nm_ip4_config_get_num_addresses (config); @@ -422,17 +429,16 @@ nm_ip4_config_update_setting (const NMIP4Config *config, NMSettingIP4Config *set nm_ip4_address_set_gateway (s_addr, gateway); if (*address->label) - NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_add_address_with_label (setting, s_addr, address->label)); + NM_UTIL_PRIVATE_CALL (nm_setting_ip4_config_add_address_with_label (s_ip4, s_addr, address->label)); else - nm_setting_ip4_config_add_address (setting, s_addr); + nm_setting_ip4_config_add_address (s_ip4, s_addr); nm_ip4_address_unref (s_addr); } - /* Only use 'disabled' if the method wasn't previously set */ - if (!method && !nm_setting_ip4_config_get_method (setting)) + /* Use 'disabled' if the method wasn't previously set */ + if (!method) method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED; - if (method) - g_object_set (setting, NM_SETTING_IP4_CONFIG_METHOD, method, NULL); + g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, method, NULL); /* Routes */ for (i = 0; i < nroutes; i++) { @@ -449,7 +455,7 @@ nm_ip4_config_update_setting (const NMIP4Config *config, NMSettingIP4Config *set nm_ip4_route_set_next_hop (s_route, route->gateway); nm_ip4_route_set_metric (s_route, route->metric); - nm_setting_ip4_config_add_route (setting, s_route); + nm_setting_ip4_config_add_route (s_ip4, s_route); nm_ip4_route_unref (s_route); } @@ -457,13 +463,15 @@ nm_ip4_config_update_setting (const NMIP4Config *config, NMSettingIP4Config *set for (i = 0; i < nnameservers; i++) { guint32 nameserver = nm_ip4_config_get_nameserver (config, i); - nm_setting_ip4_config_add_dns (setting, nameserver); + nm_setting_ip4_config_add_dns (s_ip4, nameserver); } for (i = 0; i < nsearches; i++) { const char *search = nm_ip4_config_get_search (config, i); - nm_setting_ip4_config_add_dns_search (setting, search); + nm_setting_ip4_config_add_dns_search (s_ip4, search); } + + return NM_SETTING (s_ip4); } /******************************************************************/ diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index d57cd529b7..dfe84770a9 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -62,7 +62,7 @@ const char * nm_ip4_config_get_dbus_path (const NMIP4Config *config); NMIP4Config *nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf); gboolean nm_ip4_config_commit (const NMIP4Config *config, int ifindex, int priority); void nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIP4Config *setting); -void nm_ip4_config_update_setting (const NMIP4Config *config, NMSettingIP4Config *setting); +NMSetting *nm_ip4_config_create_setting (const NMIP4Config *config); /* Utility functions */ void nm_ip4_config_merge (NMIP4Config *dst, const NMIP4Config *src); diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index fd791a5f90..bbb0b1065f 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -481,16 +481,23 @@ nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIP6Config *setting) g_object_thaw_notify (G_OBJECT (config)); } -void -nm_ip6_config_update_setting (const NMIP6Config *config, NMSettingIP6Config *setting) +NMSetting * +nm_ip6_config_create_setting (const NMIP6Config *config) { + NMSettingIP6Config *s_ip6; const struct in6_addr *gateway; guint naddresses, nroutes, nnameservers, nsearches; const char *method = NULL; int i; - if (!config) - return; + s_ip6 = NM_SETTING_IP6_CONFIG (nm_setting_ip6_config_new ()); + + if (!config) { + g_object_set (s_ip6, + NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NULL); + return NM_SETTING (s_ip6); + } gateway = nm_ip6_config_get_gateway (config); naddresses = nm_ip6_config_get_num_addresses (config); @@ -527,15 +534,14 @@ nm_ip6_config_update_setting (const NMIP6Config *config, NMSettingIP6Config *set if (gateway) nm_ip6_address_set_gateway (s_addr, gateway); - nm_setting_ip6_config_add_address (setting, s_addr); + nm_setting_ip6_config_add_address (s_ip6, s_addr); nm_ip6_address_unref (s_addr); } - /* Only use 'ignore' if the method wasn't previously set */ - if (!method && !nm_setting_ip6_config_get_method (setting)) + /* Use 'ignore' if the method wasn't previously set */ + if (!method) method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE; - if (method) - g_object_set (setting, NM_SETTING_IP6_CONFIG_METHOD, method, NULL); + g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, method, NULL); /* Routes */ for (i = 0; i < nroutes; i++) { @@ -557,7 +563,7 @@ nm_ip6_config_update_setting (const NMIP6Config *config, NMSettingIP6Config *set nm_ip6_route_set_next_hop (s_route, &route->gateway); nm_ip6_route_set_metric (s_route, route->metric); - nm_setting_ip6_config_add_route (setting, s_route); + nm_setting_ip6_config_add_route (s_ip6, s_route); nm_ip6_route_unref (s_route); } @@ -565,13 +571,15 @@ nm_ip6_config_update_setting (const NMIP6Config *config, NMSettingIP6Config *set for (i = 0; i < nnameservers; i++) { const struct in6_addr *nameserver = nm_ip6_config_get_nameserver (config, i); - nm_setting_ip6_config_add_dns (setting, nameserver); + nm_setting_ip6_config_add_dns (s_ip6, nameserver); } for (i = 0; i < nsearches; i++) { const char *search = nm_ip6_config_get_search (config, i); - nm_setting_ip6_config_add_dns_search (setting, search); + nm_setting_ip6_config_add_dns_search (s_ip6, search); } + + return NM_SETTING (s_ip6); } /******************************************************************/ diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index 32a3b21fd0..07ca0e6d7f 100644 --- a/src/nm-ip6-config.h +++ b/src/nm-ip6-config.h @@ -61,7 +61,7 @@ const char * nm_ip6_config_get_dbus_path (const NMIP6Config *config); NMIP6Config *nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary); gboolean nm_ip6_config_commit (const NMIP6Config *config, int ifindex, int priority); void nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIP6Config *setting); -void nm_ip6_config_update_setting (const NMIP6Config *config, NMSettingIP6Config *setting); +NMSetting *nm_ip6_config_create_setting (const NMIP6Config *config); /* Utility functions */ void nm_ip6_config_merge (NMIP6Config *dst, const NMIP6Config *src); diff --git a/src/nm-manager.c b/src/nm-manager.c index 0f2556487b..681de94ac3 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1572,7 +1572,9 @@ get_existing_connection (NMManager *manager, NMDevice *device) nm_connection_get_id (connection)); added = nm_settings_add_connection (priv->settings, connection, FALSE, &error); - if (!added) { + if (added) + nm_settings_connection_set_nm_generated (added); + else { nm_log_warn (LOGD_SETTINGS, "(%s) Couldn't save generated connection '%s': %s", nm_device_get_iface (device), nm_connection_get_id (connection), @@ -1584,6 +1586,89 @@ get_existing_connection (NMManager *manager, NMDevice *device) return added ? NM_CONNECTION (added) : NULL; } +static gboolean +assume_connection (NMManager *self, NMDevice *device, NMConnection *connection) +{ + NMActiveConnection *active, *master_ac; + NMAuthSubject *subject; + GError *error = NULL; + + nm_log_dbg (LOGD_DEVICE, "(%s): will attempt to assume connection", + nm_device_get_iface (device)); + + /* Move device to DISCONNECTED to activate the connection */ + if (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE) { + nm_device_state_changed (device, + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + } + g_return_if_fail (nm_device_get_state (device) >= NM_DEVICE_STATE_DISCONNECTED); + + subject = nm_auth_subject_new_internal (); + active = _new_active_connection (self, connection, NULL, device, subject, &error); + g_object_unref (subject); + + if (!active) { + nm_log_warn (LOGD_DEVICE, "assumed connection %s failed to activate: (%d) %s", + nm_connection_get_path (connection), + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_error_free (error); + return FALSE; + } + + /* If the device is a slave or VLAN, find the master ActiveConnection */ + master_ac = NULL; + if (find_master (self, connection, device, NULL, NULL, &master_ac, NULL) && master_ac) + nm_active_connection_set_master (active, master_ac); + + nm_active_connection_set_assumed (active, TRUE); + nm_active_connection_export (active); + active_connection_add (self, active); + nm_device_queue_activation (device, NM_ACT_REQUEST (active)); + g_object_unref (active); + + return TRUE; +} + +static void +recheck_assume_connection (NMDevice *device, gpointer user_data) +{ + NMManager *self = user_data; + NMConnection *connection; + gboolean was_unmanaged = FALSE; + + if (manager_sleeping (self)) + return; + if (nm_device_get_unmanaged_flag (device, NM_UNMANAGED_USER)) + return; + + connection = get_existing_connection (self, device); + if (!connection) { + nm_log_dbg (LOGD_DEVICE, "(%s): can't assume; no connection", + nm_device_get_iface (device)); + return; + } + + if (nm_device_get_state (device) == NM_DEVICE_STATE_UNMANAGED) { + was_unmanaged = TRUE; + nm_device_state_changed (device, + NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); + } + + if (!assume_connection (self, device, connection)) { + if (was_unmanaged) { + nm_device_state_changed (device, + NM_DEVICE_STATE_UNAVAILABLE, + NM_DEVICE_STATE_REASON_CONFIG_FAILED); + nm_device_state_changed (device, + NM_DEVICE_STATE_UNMANAGED, + NM_DEVICE_STATE_REASON_CONFIG_FAILED); + } + } +} + /** * add_device: * @self: the #NMManager @@ -1706,42 +1791,14 @@ add_device (NMManager *self, NMDevice *device, gboolean generate_con) */ system_create_virtual_devices (self); - /* If the device has a connection it can assume, do that now */ - if (connection) { - NMActiveConnection *active; - NMAuthSubject *subject; - GError *error = NULL; - - nm_log_dbg (LOGD_DEVICE, "(%s): will attempt to assume connection", - nm_device_get_iface (device)); - - /* Move device to DISCONNECTED to activate the connection */ - nm_device_state_changed (device, - NM_DEVICE_STATE_DISCONNECTED, - NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); - - subject = nm_auth_subject_new_internal (); - active = _new_active_connection (self, connection, NULL, device, subject, &error); - if (active) { - NMActiveConnection *master_ac = NULL; - - /* If the device is a slave or VLAN, find the master ActiveConnection */ - if (find_master (self, connection, device, NULL, NULL, &master_ac, NULL) && master_ac) - nm_active_connection_set_master (active, master_ac); - - nm_active_connection_set_assumed (active, TRUE); - nm_active_connection_export (active); - active_connection_add (self, active); - nm_device_queue_activation (device, NM_ACT_REQUEST (active)); - g_object_unref (active); - } else { - nm_log_warn (LOGD_DEVICE, "assumed connection %s failed to activate: (%d) %s", - nm_connection_get_path (connection), - error ? error->code : -1, - error && error->message ? error->message : "(unknown)"); - g_error_free (error); - } - g_object_unref (subject); + /* If the device has a connection it can assume, do that now. If it's a + * device that we might ever want to assume a connection on, then set that up. + */ + if (connection) + assume_connection (self, device, connection); + if (generate_con) { + g_signal_connect (device, NM_DEVICE_RECHECK_ASSUME, + G_CALLBACK (recheck_assume_connection), self); } } diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 6598990862..63b6a09b19 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -100,6 +100,11 @@ typedef struct { */ gboolean unsaved; + /* TRUE if the connection was generated by NetworkManager and has + * not been saved or modified by the user. + */ + gboolean nm_generated; + guint updated_idle_id; GSList *pending_auths; /* List of pending authentication requests */ @@ -402,6 +407,9 @@ set_unsaved (NMSettingsConnection *self, gboolean now_unsaved) if (priv->unsaved != now_unsaved) { priv->unsaved = now_unsaved; + if (!priv->unsaved) + priv->nm_generated = FALSE; + g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_UNSAVED); } } @@ -456,6 +464,8 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self, if (nm_connection_replace_settings_from_connection (NM_CONNECTION (self), new_connection, error)) { + priv->nm_generated = FALSE; + /* Cache the just-updated system secrets in case something calls * nm_connection_clear_secrets() and clears them. */ @@ -1976,6 +1986,39 @@ nm_settings_connection_can_autoconnect (NMSettingsConnection *connection) return TRUE; } +/** + * nm_settings_connection_get_nm_generated: + * @connection: an #NMSettingsConnection + * + * Gets the "nm-generated" flag on @connection. + * + * A connection is "nm-generated" if it was generated by + * nm_device_generate_connection() and then assumed by #NMManager, and + * it has not been modified or saved by the user since then. In other + * words, an "nm-generated" connection reflects state that is entirely + * external to NetworkManager. + */ +gboolean +nm_settings_connection_get_nm_generated (NMSettingsConnection *connection) +{ + return NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->nm_generated; +} + +/** + * nm_settings_connection_set_nm_generated: + * @connection: an #NMSettingsConnection + * + * Sets the "nm-generated" flag on @connection; see + * nm_settings_connection_get_nm_generated(). + */ +void +nm_settings_connection_set_nm_generated (NMSettingsConnection *connection) +{ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection); + + priv->nm_generated = TRUE; +} + /**************************************************************/ static void diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h index 5a84349746..0c2ca849b4 100644 --- a/src/settings/nm-settings-connection.h +++ b/src/settings/nm-settings-connection.h @@ -162,6 +162,9 @@ void nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection gboolean nm_settings_connection_can_autoconnect (NMSettingsConnection *connection); +void nm_settings_connection_set_nm_generated (NMSettingsConnection *connection); +gboolean nm_settings_connection_get_nm_generated (NMSettingsConnection *connection); + G_END_DECLS #endif /* NM_SETTINGS_CONNECTION_H */