diff options
author | Dan Williams <dcbw@redhat.com> | 2014-10-23 17:04:49 -0500 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2014-11-06 21:17:34 -0600 |
commit | b11798a196bc3cda29e0b0540bc19eef7c09a207 (patch) | |
tree | aad1a8ade34fe2e12aa23260b37845d888018b48 | |
parent | 1f544d337c4ed5d686f114f4fa0b5314bfc55312 (diff) |
vpn/core: move VPN gateway route between devices when routing changes
-rw-r--r-- | src/nm-active-connection.c | 35 | ||||
-rw-r--r-- | src/nm-active-connection.h | 7 | ||||
-rw-r--r-- | src/nm-policy.c | 28 | ||||
-rw-r--r-- | src/vpn-manager/nm-vpn-connection.c | 105 |
4 files changed, 147 insertions, 28 deletions
diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c index c3df3a5b79..73925659df 100644 --- a/src/nm-active-connection.c +++ b/src/nm-active-connection.c @@ -19,6 +19,7 @@ */ #include <glib.h> + #include "nm-types.h" #include "nm-active-connection.h" #include "nm-dbus-interface.h" @@ -30,7 +31,7 @@ #include "nm-auth-utils.h" #include "nm-auth-subject.h" #include "NetworkManagerUtils.h" - +#include "gsystem-local-alloc.h" #include "nm-active-connection-glue.h" /* Base class for anything implementing the Connection.Active D-Bus interface */ @@ -94,6 +95,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,20 +402,23 @@ gboolean nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device) { NMActiveConnectionPrivate *priv; + gs_unref_object NMDevice *old_device = NULL; 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); priv->device = g_object_ref (device); - g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_INT_DEVICE); g_signal_connect (device, "state-changed", G_CALLBACK (device_state_changed), self); @@ -419,7 +429,14 @@ nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device) priv->pending_activation_id = g_strdup_printf ("activation::%p", (void *)self); nm_device_add_pending_action (device, priv->pending_activation_id, TRUE); } - } + } else + priv->device = NULL; + g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_INT_DEVICE); + + g_signal_emit (self, signals[DEVICE_CHANGED], 0, priv->device, old_device); + + g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_DEVICES); + return TRUE; } @@ -1014,6 +1031,14 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class) FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + signals[DEVICE_CHANGED] = + g_signal_new (NM_ACTIVE_CONNECTION_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..710cfeed9f 100644 --- a/src/nm-active-connection.h +++ b/src/nm-active-connection.h @@ -56,6 +56,9 @@ #define NM_ACTIVE_CONNECTION_INT_MASTER "int-master" #define NM_ACTIVE_CONNECTION_INT_MASTER_READY "int-master-ready" +/* Internal signals*/ +#define NM_ACTIVE_CONNECTION_DEVICE_CHANGED "device-changed" + struct _NMActiveConnection { GObject parent; }; @@ -71,6 +74,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); |