summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2014-10-23 17:04:49 -0500
committerDan Williams <dcbw@redhat.com>2014-11-04 12:02:20 -0600
commitc4ce3daeb7d5ad34caf1f3669df7596ed612e0ed (patch)
tree9899f46ae843d25fd2bd2638355e81704d118a92
parent0bb9b823eafc00eee8bb098b1c6376f38ed5c108 (diff)
downloadNetworkManager-c4ce3daeb7d5ad34caf1f3669df7596ed612e0ed.tar.gz
vpn/core: move VPN gateway route between devices when routing changes
-rw-r--r--src/nm-active-connection.c28
-rw-r--r--src/nm-active-connection.h4
-rw-r--r--src/nm-policy.c28
-rw-r--r--src/vpn-manager/nm-vpn-connection.c105
4 files changed, 140 insertions, 25 deletions
diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c
index c3df3a5b79..bcd3bdbf34 100644
--- a/src/nm-active-connection.c
+++ b/src/nm-active-connection.c
@@ -94,6 +94,12 @@ enum {
LAST_PROP
};
+enum {
+ DEVICE_CHANGED,
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
static void check_master_ready (NMActiveConnection *self);
static void _device_cleanup (NMActiveConnection *self);
@@ -395,15 +401,19 @@ gboolean
nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device)
{
NMActiveConnectionPrivate *priv;
+ NMDevice *old_device;
g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), FALSE);
g_return_val_if_fail (!device || NM_IS_DEVICE (device), FALSE);
priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
+ if (device == priv->device)
+ return TRUE;
- if (device) {
- g_return_val_if_fail (priv->device == NULL, FALSE);
+ old_device = priv->device ? g_object_ref (priv->device) : NULL;
+ _device_cleanup (self);
+ if (device) {
/* Device obviously can't be its own master */
g_return_val_if_fail (!priv->master || device != nm_active_connection_get_device (priv->master), FALSE);
@@ -420,6 +430,12 @@ nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device)
nm_device_add_pending_action (device, priv->pending_activation_id, TRUE);
}
}
+
+ g_signal_emit (self, signals[DEVICE_CHANGED], 0, priv->device, old_device);
+ g_clear_object (&old_device);
+
+ g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_DEVICES);
+
return TRUE;
}
@@ -1014,6 +1030,14 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class)
FALSE, G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
+ signals[DEVICE_CHANGED] =
+ g_signal_new ("device-changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMActiveConnectionClass, device_changed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 2, NM_TYPE_DEVICE, NM_TYPE_DEVICE);
+
nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
G_TYPE_FROM_CLASS (ac_class),
&dbus_glib_nm_active_connection_object_info);
diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h
index 9a6a54711c..8afbfe037e 100644
--- a/src/nm-active-connection.h
+++ b/src/nm-active-connection.h
@@ -71,6 +71,10 @@ typedef struct {
NMDeviceState new_state,
NMDeviceState old_state);
void (*master_failed) (NMActiveConnection *connection);
+
+ void (*device_changed) (NMActiveConnection *connection,
+ NMDevice *new_device,
+ NMDevice *old_device);
} NMActiveConnectionClass;
GType nm_active_connection_get_type (void);
diff --git a/src/nm-policy.c b/src/nm-policy.c
index ddb85bd96b..ca2d340b59 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -658,6 +658,20 @@ update_ip4_routing (NMPolicy *policy, gboolean force_update)
gw_addr = nm_ip4_config_get_gateway (ip4_config);
+ if (best) {
+ const GSList *connections, *iter;
+
+ connections = nm_manager_get_active_connections (priv->manager);
+ for (iter = connections; iter; iter = g_slist_next (iter)) {
+ NMActiveConnection *active = iter->data;
+
+ if ( NM_IS_VPN_CONNECTION (active)
+ && nm_vpn_connection_get_ip4_config (NM_VPN_CONNECTION (active))
+ && !nm_active_connection_get_device (active))
+ nm_active_connection_set_device (active, best);
+ }
+ }
+
if (vpn) {
NMDevice *parent = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn));
int parent_ifindex = nm_device_get_ip_ifindex (parent);
@@ -861,6 +875,20 @@ update_ip6_routing (NMPolicy *policy, gboolean force_update)
if (!gw_addr)
gw_addr = &in6addr_any;
+ if (best) {
+ const GSList *connections, *iter;
+
+ connections = nm_manager_get_active_connections (priv->manager);
+ for (iter = connections; iter; iter = g_slist_next (iter)) {
+ NMActiveConnection *active = iter->data;
+
+ if ( NM_IS_VPN_CONNECTION (active)
+ && nm_vpn_connection_get_ip6_config (NM_VPN_CONNECTION (active))
+ && !nm_active_connection_get_device (active))
+ nm_active_connection_set_device (active, best);
+ }
+ }
+
if (vpn) {
NMDevice *parent = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn));
int parent_ifindex = nm_device_get_ip_ifindex (parent);
diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c
index 7d3fb8d390..a6622f64f2 100644
--- a/src/vpn-manager/nm-vpn-connection.c
+++ b/src/vpn-manager/nm-vpn-connection.c
@@ -435,12 +435,27 @@ _set_vpn_state (NMVpnConnection *connection,
g_object_unref (parent_dev);
}
+static gboolean
+_service_and_connection_can_persist (NMVpnConnection *self)
+{
+ return NM_VPN_CONNECTION_GET_PRIVATE (self)->connection_can_persist &&
+ NM_VPN_CONNECTION_GET_PRIVATE (self)->service_can_persist;
+}
+
static void
device_state_changed (NMActiveConnection *active,
NMDevice *device,
NMDeviceState new_state,
NMDeviceState old_state)
{
+ if (_service_and_connection_can_persist (NM_VPN_CONNECTION (active))) {
+ if (new_state <= NM_DEVICE_STATE_DISCONNECTED ||
+ new_state == NM_DEVICE_STATE_FAILED) {
+ nm_active_connection_set_device (active, NULL);
+ }
+ return;
+ }
+
if (new_state <= NM_DEVICE_STATE_DISCONNECTED) {
_set_vpn_state (NM_VPN_CONNECTION (active),
STATE_DISCONNECTED,
@@ -839,41 +854,29 @@ print_vpn_config (NMVpnConnection *connection)
}
}
-static gboolean
-nm_vpn_connection_apply_config (NMVpnConnection *connection)
+static void
+apply_parent_device_config (NMVpnConnection *connection)
{
NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (connection));
NMIP4Config *vpn4_parent_config = NULL;
NMIP6Config *vpn6_parent_config = NULL;
- if (priv->ip_ifindex > 0) {
- nm_platform_link_set_up (priv->ip_ifindex);
+ if (priv->ip4_config)
+ vpn4_parent_config = nm_ip4_config_new ();
+ if (priv->ip6_config)
+ vpn6_parent_config = nm_ip6_config_new ();
- if (priv->ip4_config) {
- if (!nm_ip4_config_commit (priv->ip4_config, priv->ip_ifindex))
- return FALSE;
- }
-
- if (priv->ip6_config) {
- if (!nm_ip6_config_commit (priv->ip6_config, priv->ip_ifindex))
- return FALSE;
- }
-
- if (priv->ip4_config)
- vpn4_parent_config = nm_ip4_config_new ();
- if (priv->ip6_config)
- vpn6_parent_config = nm_ip6_config_new ();
- } else {
+ if (priv->ip_ifindex <= 0) {
/* If the VPN didn't return a network interface, it is a route-based
* VPN (like kernel IPSec) and all IP addressing and routing should
* be done on the parent interface instead.
*/
- if (priv->ip4_config)
- vpn4_parent_config = g_object_ref (priv->ip4_config);
- if (priv->ip6_config)
- vpn6_parent_config = g_object_ref (priv->ip6_config);
+ if (vpn4_parent_config)
+ nm_ip4_config_merge (vpn4_parent_config, priv->ip4_config);
+ if (vpn6_parent_config)
+ nm_ip6_config_merge (vpn6_parent_config, priv->ip6_config);
}
if (vpn4_parent_config) {
@@ -892,6 +895,28 @@ nm_vpn_connection_apply_config (NMVpnConnection *connection)
nm_device_set_vpn6_config (parent_dev, vpn6_parent_config);
g_object_unref (vpn6_parent_config);
}
+}
+
+static gboolean
+nm_vpn_connection_apply_config (NMVpnConnection *connection)
+{
+ NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+
+ if (priv->ip_ifindex > 0) {
+ nm_platform_link_set_up (priv->ip_ifindex);
+
+ if (priv->ip4_config) {
+ if (!nm_ip4_config_commit (priv->ip4_config, priv->ip_ifindex))
+ return FALSE;
+ }
+
+ if (priv->ip6_config) {
+ if (!nm_ip6_config_commit (priv->ip6_config, priv->ip_ifindex))
+ return FALSE;
+ }
+ }
+
+ apply_parent_device_config (connection);
nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.",
nm_connection_get_id (priv->connection));
@@ -1929,6 +1954,39 @@ plugin_interactive_secrets_required (DBusGProxy *proxy,
/******************************************************************************/
static void
+device_changed (NMActiveConnection *active,
+ NMDevice *new_device,
+ NMDevice *old_device)
+{
+ NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (active);
+
+ if (!_service_and_connection_can_persist (NM_VPN_CONNECTION (active)))
+ return;
+ if (priv->vpn_state < STATE_CONNECT || priv->vpn_state > STATE_ACTIVATED)
+ return;
+
+ /* Route-based VPNs must update their routing and send a new IP config
+ * since all their routes need to be adjusted for new_device.
+ */
+ if (priv->ip_ifindex <= 0)
+ return;
+
+ /* Device changed underneath the VPN connection. Let the plugin figure
+ * out that connectivity is down and start its reconnect attempt if it
+ * needs to.
+ */
+ if (old_device) {
+ nm_device_set_vpn4_config (old_device, NULL);
+ nm_device_set_vpn6_config (old_device, NULL);
+ }
+
+ if (new_device)
+ apply_parent_device_config (NM_VPN_CONNECTION (active));
+}
+
+/******************************************************************************/
+
+static void
nm_vpn_connection_init (NMVpnConnection *self)
{
NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
@@ -2049,6 +2107,7 @@ nm_vpn_connection_class_init (NMVpnConnectionClass *connection_class)
object_class->dispose = dispose;
object_class->finalize = finalize;
active_class->device_state_changed = device_state_changed;
+ active_class->device_changed = device_changed;
g_object_class_override_property (object_class, PROP_MASTER, NM_ACTIVE_CONNECTION_MASTER);