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.
This commit is contained in:
Jiří Klimeš 2012-08-21 17:49:41 +02:00
parent 65ce4e52ef
commit ece5e209cd
3 changed files with 184 additions and 10 deletions

View file

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

View file

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

View file

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