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.
This commit is contained in:
Dan Williams 2009-08-05 18:03:09 -04:00
parent 4802094985
commit 78bb1c01b7
6 changed files with 132 additions and 34 deletions

View file

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

View file

@ -370,6 +370,11 @@
<tp:docstring>
The device's carrier/link changed.
</tp:docstring>
</tp:enumvalue>
<tp:enumvalue suffix="CONNECTION_ASSUMED" value="41">
<tp:docstring>
The device's existing connection was assumed.
</tp:docstring>
</tp:enumvalue>
</tp:enum>

View file

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

View file

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

View file

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

View file

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