From 78bb1c01b7e00275c27f20fbbac5ba507102c2fe Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 5 Aug 2009 18:03:09 -0400 Subject: [PATCH] core: implement connection assumption Mark activation requests that contain connections to be assumed, and use that to short-circuit various parts of the activation process by not touching various device attributes, since they are already set up. Also ensure the device is not deactivated when it initially becomes managed, because that would kill the connection we are about to assume. --- include/NetworkManager.h | 3 ++ introspection/nm-device.xml | 5 +++ src/nm-activation-request.c | 12 +++++ src/nm-activation-request.h | 3 ++ src/nm-device.c | 87 +++++++++++++++++++++++++++---------- src/nm-manager.c | 56 +++++++++++++++++++----- 6 files changed, 132 insertions(+), 34 deletions(-) diff --git a/include/NetworkManager.h b/include/NetworkManager.h index fcef15bc7f..f755a4e4ca 100644 --- a/include/NetworkManager.h +++ b/include/NetworkManager.h @@ -357,6 +357,9 @@ typedef enum { /* Carrier/link changed */ NM_DEVICE_STATE_REASON_CARRIER, + /* The device's existing connection was assumed */ + NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED, + /* Unused */ NM_DEVICE_STATE_REASON_LAST = 0xFFFF } NMDeviceStateReason; diff --git a/introspection/nm-device.xml b/introspection/nm-device.xml index 952f1cf1ac..c743f831b2 100644 --- a/introspection/nm-device.xml +++ b/introspection/nm-device.xml @@ -370,6 +370,11 @@ The device's carrier/link changed. + + + + The device's existing connection was assumed. + diff --git a/src/nm-activation-request.c b/src/nm-activation-request.c index c919c5e710..45851d9938 100644 --- a/src/nm-activation-request.c +++ b/src/nm-activation-request.c @@ -76,6 +76,8 @@ typedef struct { GSList *share_rules; char *ac_path; + + gboolean assumed; } NMActRequestPrivate; enum { @@ -136,6 +138,7 @@ NMActRequest * nm_act_request_new (NMConnection *connection, const char *specific_object, gboolean user_requested, + gboolean assumed, gpointer *device) { GObject *object; @@ -160,6 +163,7 @@ nm_act_request_new (NMConnection *connection, NM_ACT_REQUEST (object)); priv->user_requested = user_requested; + priv->assumed = assumed; return NM_ACT_REQUEST (object); } @@ -643,3 +647,11 @@ nm_act_request_get_device (NMActRequest *req) return G_OBJECT (NM_ACT_REQUEST_GET_PRIVATE (req)->device); } +gboolean +nm_act_request_get_assumed (NMActRequest *req) +{ + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE); + + return NM_ACT_REQUEST_GET_PRIVATE (req)->assumed; +} + diff --git a/src/nm-activation-request.h b/src/nm-activation-request.h index 18760925f2..a3c0d97446 100644 --- a/src/nm-activation-request.h +++ b/src/nm-activation-request.h @@ -59,6 +59,7 @@ GType nm_act_request_get_type (void); NMActRequest *nm_act_request_new (NMConnection *connection, const char *specific_object, gboolean user_requested, + gboolean assumed, gpointer *device); /* An NMDevice */ NMConnection *nm_act_request_get_connection (NMActRequest *req); @@ -85,6 +86,8 @@ void nm_act_request_add_share_rule (NMActRequest *req, GObject * nm_act_request_get_device (NMActRequest *req); +gboolean nm_act_request_get_assumed (NMActRequest *req); + gboolean nm_act_request_get_secrets (NMActRequest *req, const char *setting_name, gboolean request_new, diff --git a/src/nm-device.c b/src/nm-device.c index 53e18f21ce..18a85160c7 100644 --- a/src/nm-device.c +++ b/src/nm-device.c @@ -140,8 +140,14 @@ static void nm_device_take_down (NMDevice *dev, gboolean wait, NMDeviceStateReas static gboolean nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware); static gboolean nm_device_is_up (NMDevice *self); -static gboolean nm_device_set_ip4_config (NMDevice *dev, NMIP4Config *config, NMDeviceStateReason *reason); -static gboolean nm_device_set_ip6_config (NMDevice *dev, NMIP6Config *config, NMDeviceStateReason *reason); +static gboolean nm_device_set_ip4_config (NMDevice *dev, + NMIP4Config *config, + gboolean assumed, + NMDeviceStateReason *reason); +static gboolean nm_device_set_ip6_config (NMDevice *dev, + NMIP6Config *config, + gboolean assumed, + NMDeviceStateReason *reason); static void device_interface_init (NMDeviceInterface *device_interface_class) @@ -774,7 +780,7 @@ handle_autoip_change (NMDevice *self, NMDeviceStateReason *reason) g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP4_CONFIG, config); - if (!nm_device_set_ip4_config (self, config, reason)) { + if (!nm_device_set_ip4_config (self, config, FALSE, reason)) { nm_warning ("(%s): failed to update IP4 config in response to autoip event.", nm_device_get_iface (self)); return FALSE; @@ -1724,19 +1730,20 @@ static gboolean nm_device_activate_stage5_ip_config_commit (gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMIP4Config *ip4_config = NULL; NMIP6Config *ip6_config = NULL; const char *iface, *method = NULL; NMConnection *connection; NMSettingIP4Config *s_ip4; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; + gboolean assumed; ip4_config = g_object_get_data (G_OBJECT (nm_device_get_act_request (self)), NM_ACT_REQUEST_IP4_CONFIG); g_assert (ip4_config); ip6_config = g_object_get_data (G_OBJECT (nm_device_get_act_request (self)), NM_ACT_REQUEST_IP6_CONFIG); - /* FIXME g_assert (ip6_config); */ /* Clear the activation source ID now that this stage has run */ activation_source_clear (self, FALSE, 0); @@ -1745,12 +1752,14 @@ nm_device_activate_stage5_ip_config_commit (gpointer user_data) nm_info ("Activation (%s) Stage 5 of 5 (IP Configure Commit) started...", iface); - if (!nm_device_set_ip4_config (self, ip4_config, &reason)) { + assumed = nm_act_request_get_assumed (priv->act_request); + + if (!nm_device_set_ip4_config (self, ip4_config, assumed, &reason)) { nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); goto out; } - if (!nm_device_set_ip6_config (self, ip6_config, &reason)) { + if (ip6_config && !nm_device_set_ip6_config (self, ip6_config, assumed, &reason)) { nm_info ("Activation (%s) Stage 5 of 5 (IP Configure Commit) IPv6 failed", iface); } @@ -1776,7 +1785,8 @@ out: /* Balance IP config creation; device takes ownership in set_ip*_config() */ g_object_unref (ip4_config); - g_object_unref (ip6_config); + if (ip6_config) + g_object_unref (ip6_config); return FALSE; } @@ -1920,8 +1930,8 @@ nm_device_deactivate (NMDeviceInterface *device, NMDeviceStateReason reason) nm_device_deactivate_quickly (self); /* Clean up nameservers and addresses */ - nm_device_set_ip4_config (self, NULL, &ignored); - nm_device_set_ip6_config (self, NULL, &ignored); + nm_device_set_ip4_config (self, NULL, FALSE, &ignored); + nm_device_set_ip6_config (self, NULL, FALSE, &ignored); /* Take out any entries in the routing table and any IP address the device had. */ nm_system_device_flush_routes (self); @@ -2022,13 +2032,22 @@ nm_device_activate (NMDeviceInterface *device, G_CALLBACK (connection_secrets_failed_cb), device); - /* HACK: update the state a bit early to avoid a race between the - * scheduled stage1 handler and nm_policy_device_change_check() thinking - * that the activation request isn't deferred because the deferred bit - * gets cleared a bit too early, when the connection becomes valid. - */ - nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); - nm_device_activate_schedule_stage1_device_prepare (self); + if (!nm_act_request_get_assumed (req)) { + /* HACK: update the state a bit early to avoid a race between the + * scheduled stage1 handler and nm_policy_device_change_check() thinking + * that the activation request isn't deferred because the deferred bit + * gets cleared a bit too early, when the connection becomes valid. + */ + nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); + nm_device_activate_schedule_stage1_device_prepare (self); + } else { + /* If it's an assumed connection, let the device subclass short-circuit + * the normal connection process and just copy its IP configs from the + * interface. + */ + nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); + nm_device_activate_schedule_stage3_ip_config_start (self); + } return TRUE; } @@ -2093,6 +2112,7 @@ handle_dhcp_lease_change (NMDevice *device) NMActRequest *req; NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; const char *ip_iface; + gboolean assumed; if (!nm_device_get_use_dhcp (device)) { nm_warning ("got DHCP rebind for device that wasn't using DHCP."); @@ -2118,7 +2138,8 @@ handle_dhcp_lease_change (NMDevice *device) g_object_set_data (G_OBJECT (req), NM_ACT_REQUEST_IP4_CONFIG, config); - if (nm_device_set_ip4_config (device, config, &reason)) { + assumed = nm_act_request_get_assumed (req); + if (nm_device_set_ip4_config (device, config, assumed, &reason)) { nm_dhcp4_config_reset (priv->dhcp4_config); nm_dhcp_manager_foreach_dhcp4_option (priv->dhcp_manager, ip_iface, @@ -2276,6 +2297,7 @@ nm_device_get_ip4_config (NMDevice *self) static gboolean nm_device_set_ip4_config (NMDevice *self, NMIP4Config *new_config, + gboolean assumed, NMDeviceStateReason *reason) { NMDevicePrivate *priv; @@ -2311,8 +2333,13 @@ nm_device_set_ip4_config (NMDevice *self, if (new_config) { priv->ip4_config = g_object_ref (new_config); - success = nm_system_apply_ip4_config (ip_iface, new_config, nm_device_get_priority (self), diff); - if (success) { + /* Don't touch the device's actual IP config if the connection is + * assumed when NM starts. + */ + if (!assumed) + success = nm_system_apply_ip4_config (ip_iface, new_config, nm_device_get_priority (self), diff); + + if (success || assumed) { /* Export over D-Bus */ if (!nm_ip4_config_get_dbus_path (new_config)) nm_ip4_config_export (new_config); @@ -2373,6 +2400,7 @@ nm_device_update_ip4_address (NMDevice *self) static gboolean nm_device_set_ip6_config (NMDevice *self, NMIP6Config *new_config, + gboolean assumed, NMDeviceStateReason *reason) { NMDevicePrivate *priv; @@ -2408,8 +2436,13 @@ nm_device_set_ip6_config (NMDevice *self, if (new_config) { priv->ip6_config = g_object_ref (new_config); - success = nm_system_apply_ip6_config (ip_iface, new_config, nm_device_get_priority (self), diff); - if (success) { + /* Don't touch the device's actual IP config if the connection is + * assumed when NM starts. + */ + if (!assumed) + success = nm_system_apply_ip6_config (ip_iface, new_config, nm_device_get_priority (self), diff); + + if (success || assumed) { /* Export over D-Bus */ if (!nm_ip6_config_get_dbus_path (new_config)) nm_ip6_config_export (new_config); @@ -2587,7 +2620,7 @@ dispose (GObject *object) NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE; nm_device_take_down (self, FALSE, NM_DEVICE_STATE_REASON_REMOVED); - nm_device_set_ip4_config (self, NULL, &ignored); + nm_device_set_ip4_config (self, NULL, FALSE, &ignored); } clear_act_request (self); @@ -2880,8 +2913,14 @@ nm_device_state_changed (NMDevice *device, if (!nm_device_bring_up (device, TRUE, &no_firmware) && no_firmware) nm_warning ("%s: firmware may be missing.", nm_device_get_iface (device)); } - /* Fall through, so when the device needs to be deactivated due to - * eg carrier changes we actually deactivate it */ + /* Ensure the device gets deactivated in response to stuff like + * carrier changes or rfkill. But don't deactivate devices that are + * about to assume a connection since that defeats the purpose of + * assuming the device's existing connection. + */ + if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) + nm_device_interface_deactivate (NM_DEVICE_INTERFACE (device), reason); + break; case NM_DEVICE_STATE_DISCONNECTED: if (old_state != NM_DEVICE_STATE_UNAVAILABLE) nm_device_interface_deactivate (NM_DEVICE_INTERFACE (device), reason); diff --git a/src/nm-manager.c b/src/nm-manager.c index 5ad0ff58cf..dc9da810e4 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -113,6 +113,14 @@ static void add_device (NMManager *self, NMDevice *device); static void hostname_provider_init (NMHostnameProvider *provider_class); +static const char *internal_activate_device (NMManager *manager, + NMDevice *device, + NMConnection *connection, + const char *specific_object, + gboolean user_requested, + gboolean assumed, + GError **error); + #define SSD_POKE_INTERVAL 120 #define ORIGDEV_TAG "originating-device" @@ -1165,7 +1173,6 @@ add_device (NMManager *self, NMDevice *device) char *path; static guint32 devcount = 0; const GSList *unmanaged_specs; - GSList *connections = NULL; NMConnection *existing; GHashTableIter iter; gpointer value; @@ -1208,20 +1215,47 @@ add_device (NMManager *self, NMDevice *device) /* Check if we should assume the device's active connection by matching its * config with an existing system connection. */ - g_hash_table_iter_init (&iter, priv->system_connections); - while (g_hash_table_iter_next (&iter, NULL, &value)) - connections = g_slist_append (connections, value); - existing = nm_device_interface_connection_match_config (NM_DEVICE_INTERFACE (device), - (const GSList *) connections); - g_slist_free (connections); + if (nm_device_interface_can_assume_connection (NM_DEVICE_INTERFACE (device))) { + GSList *connections = NULL; + + g_hash_table_iter_init (&iter, priv->system_connections); + while (g_hash_table_iter_next (&iter, NULL, &value)) + connections = g_slist_append (connections, value); + existing = nm_device_interface_connection_match_config (NM_DEVICE_INTERFACE (device), + (const GSList *) connections); + g_slist_free (connections); + } /* Start the device if it's supposed to be managed */ unmanaged_specs = nm_sysconfig_settings_get_unmanaged_specs (priv->sys_settings); - if (!priv->sleeping && !nm_device_interface_spec_match_list (NM_DEVICE_INTERFACE (device), unmanaged_specs)) - nm_device_set_managed (device, TRUE, NM_DEVICE_STATE_REASON_NOW_MANAGED); + if ( !priv->sleeping + && !nm_device_interface_spec_match_list (NM_DEVICE_INTERFACE (device), unmanaged_specs)) { + nm_device_set_managed (device, + TRUE, + existing ? NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED : + NM_DEVICE_STATE_REASON_NOW_MANAGED); + } nm_sysconfig_settings_device_added (priv->sys_settings, device); g_signal_emit (self, signals[DEVICE_ADDED], 0, device); + + /* If the device has a connection it can assume, do that now */ + if (existing) { + const char *ac_path; + GError *error = NULL; + + ac_path = internal_activate_device (self, device, existing, NULL, FALSE, TRUE, &error); + if (ac_path) + g_object_notify (G_OBJECT (self), NM_MANAGER_ACTIVE_CONNECTIONS); + else { + nm_warning ("Assumed connection (%d) %s failed to activate: (%d) %s", + nm_connection_get_scope (existing), + nm_connection_get_path (existing), + error ? error->code : -1, + error && error->message ? error->message : "(unknown)"); + g_error_free (error); + } + } } static gboolean @@ -1826,6 +1860,7 @@ internal_activate_device (NMManager *manager, NMConnection *connection, const char *specific_object, gboolean user_requested, + gboolean assumed, GError **error) { NMActRequest *req; @@ -1849,7 +1884,7 @@ internal_activate_device (NMManager *manager, NM_DEVICE_STATE_REASON_NONE); } - req = nm_act_request_new (connection, specific_object, user_requested, (gpointer) device); + req = nm_act_request_new (connection, specific_object, user_requested, assumed, (gpointer) device); g_signal_connect (req, "manager-get-secrets", G_CALLBACK (provider_get_secrets), manager); g_signal_connect (req, "manager-cancel-secrets", G_CALLBACK (provider_cancel_secrets), manager); success = nm_device_interface_activate (dev_iface, req, error); @@ -1961,6 +1996,7 @@ nm_manager_activate_connection (NMManager *manager, connection, specific_object, user_requested, + FALSE, error); }