diff options
Diffstat (limited to 'src/devices/nm-device.c')
| -rw-r--r-- | src/devices/nm-device.c | 141 |
1 files changed, 140 insertions, 1 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 9cd2271620..84374887ff 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -213,6 +213,7 @@ typedef struct { gpointer act_source6_func; gulong secrets_updated_id; gulong secrets_failed_id; + NMConnection * current_connection; /* Link stuff */ guint link_connected_id; @@ -1604,6 +1605,131 @@ nm_device_can_assume_connections (NMDevice *device) } static void +test_reconfigure (NMDevice *self, NMConnection *connection, + GHashTable *diff) +{ + GHashTable *conn_diff, *ip4_diff, *ip6_diff; + + conn_diff = g_hash_table_lookup (diff, NM_SETTING_CONNECTION_SETTING_NAME); + ip4_diff = g_hash_table_lookup (diff, NM_SETTING_IP4_CONFIG_SETTING_NAME); + ip6_diff = g_hash_table_lookup (diff, NM_SETTING_IP6_CONFIG_SETTING_NAME); + + /* These only affect matching and have already been taken into account. */ + if (conn_diff) + g_hash_table_remove (conn_diff, NM_SETTING_CONNECTION_INTERFACE_NAME); + if (ip4_diff) + g_hash_table_remove (ip4_diff, NM_SETTING_IP4_CONFIG_MAY_FAIL); + if (ip6_diff) + g_hash_table_remove (ip6_diff, NM_SETTING_IP6_CONFIG_MAY_FAIL); + + /* FIXME: remove reconfigurable ip4/ip6 properties */ +} + +static gboolean +nm_device_can_reconfigure (NMDevice *self, NMConnection *connection, + GHashTable *diff) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self); + GHashTableIter iter; + gpointer key, value; + + /* If the changes would make the connection incompatible, then we + * can always "reconfigure" the device (by deactivating the + * connection). + */ + if (!nm_device_check_connection_compatible (self, connection, NULL)) + return TRUE; + if ( (!priv->ip4_config && nm_device_ip_config_should_fail (self, FALSE)) + || (!priv->ip6_config && nm_device_ip_config_should_fail (self, TRUE))) + return TRUE; + + klass->test_reconfigure (self, connection, diff); + + /* test_reconfigure() will have removed any properties it can + * reconfigure; clear out any now-empty settings diffs. + */ + g_hash_table_iter_init (&iter, diff); + while (g_hash_table_iter_next (&iter, &key, &value)) { + if (g_hash_table_size (value) == 0) + g_hash_table_iter_remove (&iter); + } + + /* If the diff is now empty, that means all of the changed + * properties are reconfigurable. + */ + return g_hash_table_size (diff) == 0; +} + +static gboolean +reconfigure (NMDevice *device, NMConnection *connection, + NMDeviceStateReason *reason) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device); + + if (!nm_device_check_connection_compatible (device, connection, NULL)) { + nm_log_info ("(%s): connection '%s' is no longer compatible with device", + nm_device_get_iface (self), nm_connection_get_id (connection)); + *reason = NM_DEVICE_STATE_REASON_CONNECTION_REMOVED; + return FALSE; + } + + if (!priv->ip4_config && nm_device_ip_config_should_fail (device, FALSE)) { + nm_log_info ("(%s): connection '%s' now requires IPv4 connectivity", + nm_device_get_iface (self), nm_connection_get_id (connection)); + *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; + return FALSE; + } + + if (!priv->ip6_config && nm_device_ip_config_should_fail (device, TRUE)) { + nm_log_info ("(%s): connection '%s' now requires IPv6 connectivity", + nm_device_get_iface (self), nm_connection_get_id (connection)); + *reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE; + return FALSE; + } + + /* FIXME: reconfigure IP4 and IP6 properties, by re-merging the + * settings properties with the DHCP/SLAAC/AIPD/etc-provided + * configs. + */ + + return TRUE; +} + +static void +connection_updated (NMSettingsConnection *settings_connection, gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMConnection *connection = NM_CONNECTION (settings_connection); + NMDeviceStateReason reason; + GHashTable *diff = NULL; + + if (!nm_connection_diff (priv->current_connection, connection, + NM_SETTING_COMPARE_FLAG_FUZZY, + &diff)) { + if (nm_device_can_reconfigure (self, connection, diff)) { + if (!NM_DEVICE_GET_CLASS (self)->reconfigure (self, connection, &reason)) { + nm_device_queue_state (self, + NM_DEVICE_STATE_DISCONNECTED, + reason); + } + } else { + nm_log_info ("(%s): connection '%s' changed, but device cannot be updated for those changes while active", + nm_device_get_iface (self), nm_connection_get_id (connection)); + } + + g_hash_table_destroy (diff); + } else { + nm_log_dbg ("(%s): connection '%s' changed, but the changes do not affect the device", + nm_device_get_iface (self), nm_connection_get_id (connection)); + } + + g_clear_object (&priv->current_connection); + priv->current_connection = nm_connection_duplicate (connection); +} + +static void dnsmasq_state_changed_cb (NMDnsMasqManager *manager, guint32 status, gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); @@ -3881,7 +4007,8 @@ nm_device_activate_ip6_state_in_wait (NMDevice *self) static void clear_act_request (NMDevice *self) { - NMDevicePrivate * priv; + NMDevicePrivate *priv; + NMConnection *connection; g_return_if_fail (self != NULL); @@ -3902,6 +4029,10 @@ clear_act_request (NMDevice *self) priv->secrets_failed_id = 0; } + connection = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (priv->act_request)); + g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_updated), self); + g_clear_object (&priv->current_connection); + nm_active_connection_set_default (NM_ACTIVE_CONNECTION (priv->act_request), FALSE); g_object_unref (priv->act_request); @@ -4213,6 +4344,10 @@ nm_device_activate (NMDevice *self, NMActRequest *req) priv->act_request = g_object_ref (req); g_object_notify (G_OBJECT (self), NM_DEVICE_ACTIVE_CONNECTION); + g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED, + G_CALLBACK (connection_updated), self); + priv->current_connection = g_object_ref (connection); + if (nm_active_connection_get_assumed (NM_ACTIVE_CONNECTION (req))) { /* If it's an assumed connection, let the device subclass short-circuit * the normal connection process and just copy its IP configs from the @@ -4827,6 +4962,8 @@ dispose (GObject *object) g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ip_changed), self); g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self); + g_clear_object (&priv->current_connection); + out: G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); } @@ -5132,6 +5269,8 @@ nm_device_class_init (NMDeviceClass *klass) klass->carrier_changed = carrier_changed; klass->can_interrupt_activation = can_interrupt_activation; klass->get_hw_address_length = get_hw_address_length; + klass->test_reconfigure = test_reconfigure; + klass->reconfigure = reconfigure; /* Properties */ g_object_class_install_property |
