devices: improve master/slave handling of assumed connections

https://bugzilla.gnome.org/show_bug.cgi?id=729843
https://bugzilla.redhat.com/show_bug.cgi?id=1066706
This commit is contained in:
Dan Winship 2014-06-06 10:17:32 -04:00
commit d3a51b6e4a
13 changed files with 468 additions and 172 deletions

View file

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

View file

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

View file

@ -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 */

View file

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

View file

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

View file

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

View file

@ -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);
}
/******************************************************************/

View file

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

View file

@ -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);
}
/******************************************************************/

View file

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

View file

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

View file

@ -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

View file

@ -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 */