summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiří Klimeš <jklimes@redhat.com>2012-08-21 17:49:41 +0200
committerJiří Klimeš <jklimes@redhat.com>2012-09-24 10:38:26 +0200
commitece5e209cdc409a21e249dacbdbc953a1db4c6b7 (patch)
tree577056533db17727a34299f47ee3848961c8b206
parent65ce4e52ef97a9852c367dac5df298188410d3f6 (diff)
core: VPN autoconnect feature (bgo #560471) (rh #483120)
We go through the SECONDARIES state where we check if there are some secondary (VPN or other) UUIDs that are to be activated before progressing to ACTIVATED. In case of an error with a secondary UUID or its activation, the base connection can't activate successfully.
-rw-r--r--src/nm-device.c17
-rw-r--r--src/nm-policy.c172
-rw-r--r--src/vpn-manager/nm-vpn-manager.c5
3 files changed, 184 insertions, 10 deletions
diff --git a/src/nm-device.c b/src/nm-device.c
index 973758b4..a0e7fc54 100644
--- a/src/nm-device.c
+++ b/src/nm-device.c
@@ -2469,11 +2469,12 @@ out:
nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) complete.", iface);
/* Handle interfaces (bond slaves, etc) that won't have any IP config; they
- * need to move to ACTIVATED.
+ * need to move to SECONDARIES.
*/
if (priv->ip4_state == IP_DONE && priv->ip6_state == IP_DONE) {
/* FIXME: call layer2 stuff to set MTU? */
- nm_device_state_changed (self, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_REASON_NONE);
+
+ nm_device_state_changed (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE);
}
return FALSE;
@@ -2891,10 +2892,10 @@ nm_device_activate_ip4_config_commit (gpointer user_data)
}
}
- /* Enter the ACTIVATED state if this is the first method to complete */
+ /* Enter the SECONDARIES state if this is the first method to complete */
priv->ip4_state = IP_DONE;
if (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG)
- nm_device_state_changed (self, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_REASON_NONE);
+ nm_device_state_changed (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE);
out:
nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 5 of 5 (IPv4 Commit) complete.",
@@ -2977,10 +2978,10 @@ nm_device_activate_ip6_config_commit (gpointer user_data)
NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit (self, config);
if (ip6_config_merge_and_apply (self, config, &reason)) {
- /* Enter the ACTIVATED state if this is the first method to complete */
+ /* Enter the SECONDARIES state if this is the first method to complete */
priv->ip6_state = IP_DONE;
if (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG)
- nm_device_state_changed (self, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_REASON_NONE);
+ nm_device_state_changed (self, NM_DEVICE_STATE_SECONDARIES, NM_DEVICE_STATE_REASON_NONE);
} else {
nm_log_info (LOGD_DEVICE | LOGD_IP6,
"Activation (%s) Stage 5 of 5 (IPv6 Commit) failed",
@@ -4659,6 +4660,10 @@ nm_device_state_changed (NMDevice *device,
*/
nm_device_queue_state (device, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
break;
+ case NM_DEVICE_STATE_SECONDARIES:
+ nm_log_dbg (LOGD_DEVICE, "(%s): device entered SECONDARIES state",
+ nm_device_get_iface (device));
+ break;
default:
break;
}
diff --git a/src/nm-policy.c b/src/nm-policy.c
index 2822405f..74b4c33d 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -42,6 +42,7 @@
#include "nm-manager-auth.h"
#include "nm-firewall-manager.h"
#include "nm-dispatcher.h"
+#include "nm-utils.h"
struct NMPolicy {
NMManager *manager;
@@ -55,6 +56,8 @@ struct NMPolicy {
gulong vpn_activated_id;
gulong vpn_deactivated_id;
+ GSList *pending_secondaries;
+
NMFirewallManager *fw_manager;
gulong fw_started_id;
@@ -104,7 +107,8 @@ get_best_ip4_device (NMManager *manager)
gboolean can_default = FALSE;
const char *method = NULL;
- if (nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED)
+ if ( nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED
+ && nm_device_get_state (dev) != NM_DEVICE_STATE_SECONDARIES)
continue;
ip4_config = nm_device_get_ip4_config (dev);
@@ -176,7 +180,8 @@ get_best_ip6_device (NMManager *manager)
gboolean can_default = FALSE;
const char *method = NULL;
- if (nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED)
+ if ( nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED
+ && nm_device_get_state (dev) != NM_DEVICE_STATE_SECONDARIES)
continue;
ip6_config = nm_device_get_ip6_config (dev);
@@ -930,6 +935,84 @@ find_pending_activation (GSList *list, NMDevice *device)
/*****************************************************************************/
+typedef struct {
+ NMDevice *device;
+ GSList *secondaries;
+} PendingSecondaryData;
+
+static PendingSecondaryData *
+pending_secondary_data_new (NMDevice *device, GSList *secondaries)
+{
+ PendingSecondaryData *data;
+
+ data = g_malloc0 (sizeof (PendingSecondaryData));
+ data->device = g_object_ref (device);
+ data->secondaries = secondaries;
+ return data;
+}
+
+static void
+pending_secondary_data_free (PendingSecondaryData *data)
+{
+ g_object_unref (data->device);
+ nm_utils_slist_free (data->secondaries, g_free);
+ memset (data, 0, sizeof (*data));
+ g_free (data);
+}
+
+static void
+process_secondaries (NMPolicy *policy,
+ NMActiveConnection *active,
+ gboolean connected)
+{
+ NMDevice *device = NULL;
+ const char *ac_path;
+ GSList *iter, *iter2;
+
+ nm_log_dbg (LOGD_DEVICE, "Secondary connection '%s' %s; active path '%s'",
+ nm_active_connection_get_name (active),
+ connected ? "SUCCEEDED" : "FAILED",
+ nm_active_connection_get_path (active));
+
+ ac_path = nm_active_connection_get_path (active);
+
+ if (NM_IS_VPN_CONNECTION (active))
+ device = nm_vpn_connection_get_parent_device (NM_VPN_CONNECTION (active));
+
+ for (iter = policy->pending_secondaries; iter; iter = g_slist_next (iter)) {
+ PendingSecondaryData *secondary_data = (PendingSecondaryData *) iter->data;
+ NMDevice *item_device = secondary_data->device;
+
+ if (!device || item_device == device) {
+ for (iter2 = secondary_data->secondaries; iter2; iter2 = g_slist_next (iter2)) {
+ char *list_ac_path = (char *) iter2->data;
+
+ if (g_strcmp0 (ac_path, list_ac_path) == 0) {
+ if (connected) {
+ /* Secondary connection activated */
+ secondary_data->secondaries = g_slist_remove (secondary_data->secondaries, list_ac_path);
+ g_free (list_ac_path);
+ if (!secondary_data->secondaries) {
+ /* None secondary UUID remained -> remove the secondary data item */
+ policy->pending_secondaries = g_slist_remove (policy->pending_secondaries, secondary_data);
+ pending_secondary_data_free (secondary_data);
+ nm_device_state_changed (item_device, NM_DEVICE_STATE_ACTIVATED, NM_DEVICE_STATE_REASON_NONE);
+ return;
+ }
+ } else {
+ /* Secondary connection failed -> do not watch other connections */
+ policy->pending_secondaries = g_slist_remove (policy->pending_secondaries, secondary_data);
+ pending_secondary_data_free (secondary_data);
+ nm_device_state_changed (item_device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED);
+ return;
+ }
+ }
+ }
+ return;
+ }
+ }
+}
+
static void
vpn_connection_activated (NMVPNManager *manager,
NMVPNConnection *vpn,
@@ -958,6 +1041,8 @@ vpn_connection_activated (NMVPNManager *manager,
update_routing_and_dns ((NMPolicy *) user_data, TRUE);
nm_dns_manager_end_updates (mgr, __func__);
+
+ process_secondaries ((NMPolicy *) user_data, NM_ACTIVE_CONNECTION (vpn), TRUE);
}
static void
@@ -1025,6 +1110,8 @@ vpn_connection_deactivated (NMVPNManager *manager,
update_routing_and_dns ((NMPolicy *) user_data, TRUE);
nm_dns_manager_end_updates (mgr, __func__);
+
+ process_secondaries ((NMPolicy *) user_data, NM_ACTIVE_CONNECTION (vpn), FALSE);
}
static void
@@ -1180,6 +1267,69 @@ activate_slave_connections (NMPolicy *policy, NMConnection *connection,
schedule_activate_all (policy);
}
+static gboolean
+activate_secondary_connections (NMPolicy *policy,
+ NMConnection *connection,
+ NMDevice *device)
+{
+ NMSettingConnection *s_con;
+ NMSettingsConnection *settings_con;
+ NMActiveConnection *ac;
+ PendingSecondaryData *secondary_data;
+ GSList *secondary_ac_list = NULL;
+ GError *error = NULL;
+ guint32 i;
+ gboolean success = TRUE;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_assert (s_con);
+
+ for (i = 0; i < nm_setting_connection_get_num_secondaries (s_con); i++) {
+ const char *sec_uuid = nm_setting_connection_get_secondary (s_con, i);
+
+ settings_con = nm_settings_get_connection_by_uuid (policy->settings, sec_uuid);
+ if (settings_con) {
+ NMActRequest *req = nm_device_get_act_request (device);
+ g_assert (req);
+
+ nm_log_dbg (LOGD_DEVICE, "Activating secondary connection '%s (%s)' for base connection '%s (%s)'",
+ nm_connection_get_id (NM_CONNECTION (settings_con)), sec_uuid,
+ nm_connection_get_id (connection), nm_connection_get_uuid (connection));
+ ac = nm_manager_activate_connection (policy->manager,
+ NM_CONNECTION (settings_con),
+ nm_active_connection_get_path (NM_ACTIVE_CONNECTION (req)),
+ nm_device_get_path (device),
+ nm_act_request_get_dbus_sender (req),
+ &error);
+ if (ac) {
+ secondary_ac_list = g_slist_append (secondary_ac_list,
+ g_strdup (nm_active_connection_get_path (ac)));
+ } else {
+ nm_log_warn (LOGD_DEVICE, "Secondary connection '%s' auto-activation failed: (%d) %s",
+ sec_uuid,
+ error ? error->code : 0,
+ (error && error->message) ? error->message : "unknown");
+ g_clear_error (&error);
+ success = FALSE;
+ break;
+ }
+ } else {
+ nm_log_warn (LOGD_DEVICE, "Secondary connection '%s' auto-activation failed: The connection doesn't exist.",
+ sec_uuid);
+ success = FALSE;
+ break;
+ }
+ }
+
+ if (success && secondary_ac_list != NULL) {
+ secondary_data = pending_secondary_data_new (device, secondary_ac_list);
+ policy->pending_secondaries = g_slist_append (policy->pending_secondaries, secondary_data);
+ } else
+ nm_utils_slist_free (secondary_ac_list, g_free);
+
+ return success;
+}
+
static void
device_state_changed (NMDevice *device,
NMDeviceState new_state,
@@ -1192,6 +1342,7 @@ device_state_changed (NMDevice *device,
const char *ip_iface = nm_device_get_ip_iface (device);
NMIP4Config *ip4_config;
NMIP6Config *ip6_config;
+ NMSettingConnection *s_con;
NMDnsManager *dns_mgr;
if (connection)
@@ -1294,6 +1445,20 @@ device_state_changed (NMDevice *device,
* activation. */
activate_slave_connections (policy, connection, device);
break;
+ case NM_DEVICE_STATE_SECONDARIES:
+ s_con = nm_connection_get_setting_connection (connection);
+ if (s_con && nm_setting_connection_get_num_secondaries (s_con) > 0) {
+ /* Make routes and DNS up-to-date before activating dependent connections */
+ update_routing_and_dns (policy, FALSE);
+
+ /* Activate secondary (VPN) connections */
+ if (!activate_secondary_connections (policy, connection, device))
+ nm_device_state_changed (device, NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED);
+ } else
+ nm_device_state_changed (device, NM_DEVICE_STATE_ACTIVATED,
+ NM_DEVICE_STATE_REASON_NONE);
+ break;
default:
break;
@@ -1747,6 +1912,9 @@ nm_policy_destroy (NMPolicy *policy)
g_slist_foreach (policy->pending_activation_checks, (GFunc) activate_data_free, NULL);
g_slist_free (policy->pending_activation_checks);
+ g_slist_foreach (policy->pending_secondaries, (GFunc) pending_secondary_data_free, NULL);
+ g_slist_free (policy->pending_secondaries);
+
g_signal_handler_disconnect (policy->vpn_manager, policy->vpn_activated_id);
g_signal_handler_disconnect (policy->vpn_manager, policy->vpn_deactivated_id);
g_object_unref (policy->vpn_manager);
diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c
index 8bfa82ae..fe179092 100644
--- a/src/vpn-manager/nm-vpn-manager.c
+++ b/src/vpn-manager/nm-vpn-manager.c
@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2005 - 2011 Red Hat, Inc.
+ * Copyright (C) 2005 - 2012 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
@@ -155,7 +155,8 @@ nm_vpn_manager_activate_connection (NMVPNManager *manager,
g_return_val_if_fail (error != NULL, NULL);
g_return_val_if_fail (*error == NULL, NULL);
- if (nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED) {
+ if ( nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED
+ && nm_device_get_state (device) != NM_DEVICE_STATE_SECONDARIES) {
g_set_error (error,
NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_DEVICE_NOT_ACTIVE,
"%s", "The base device for the VPN connection was not active.");