summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLubomir Rintel <lkundrak@v3.sk>2015-08-19 23:07:13 +0200
committerLubomir Rintel <lkundrak@v3.sk>2015-10-13 18:20:56 +0200
commit9bbf5e94c2ff676f685a7740713140026efbcb7c (patch)
treee8d389fd1e1aea2a7cb014d7dfbf090a80d93f24
parent965363a6d5ef662b2d75cda8aa9d55ac02991176 (diff)
downloadNetworkManager-9bbf5e94c2ff676f685a7740713140026efbcb7c.tar.gz
device: allow multiple vpn IP configurations
-rw-r--r--src/devices/nm-device.c142
-rw-r--r--src/devices/nm-device.h8
-rw-r--r--src/vpn-manager/nm-vpn-connection.c57
3 files changed, 157 insertions, 50 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index eeffbe95b8..2568a100df 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -272,6 +272,7 @@ typedef struct {
NMIP4Config * dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */
NMIP4Config * ext_ip4_config; /* Stuff added outside NM */
NMIP4Config * wwan_ip4_config; /* WWAN configuration */
+ GSList * vpn4_configs; /* VPNs which use this device */
struct {
gboolean v4_has;
gboolean v4_is_assumed;
@@ -289,7 +290,6 @@ typedef struct {
gulong dhcp4_state_sigid;
NMDhcp4Config * dhcp4_config;
guint dhcp4_restart_id;
- NMIP4Config * vpn4_config; /* routes added by a VPN which uses this device */
guint arp_round2_id;
PingInfo gw_ping;
@@ -310,9 +310,9 @@ typedef struct {
NMIP6Config * ip6_config;
IpState ip6_state;
NMIP6Config * con_ip6_config; /* config from the setting */
- NMIP6Config * vpn6_config; /* routes added by a VPN which uses this device */
NMIP6Config * wwan_ip6_config;
NMIP6Config * ext_ip6_config; /* Stuff added outside NM */
+ GSList * vpn6_configs; /* VPNs which use this device */
gboolean nm_ipv6ll; /* TRUE if NM handles the device's IPv6LL address */
guint32 ip6_mtu;
@@ -3577,6 +3577,15 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
}
}
+static void
+_ip4_config_merge_default (gpointer value, gpointer user_data)
+{
+ NMIP4Config *src = (NMIP4Config *) value;
+ NMIP4Config *dst = (NMIP4Config *) user_data;
+
+ nm_ip4_config_merge (dst, src, NM_IP_CONFIG_MERGE_DEFAULT);
+}
+
static gboolean
ip4_config_merge_and_apply (NMDevice *self,
NMIP4Config *config,
@@ -3622,8 +3631,9 @@ ip4_config_merge_and_apply (NMDevice *self,
(ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
| (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0));
}
- if (priv->vpn4_config)
- nm_ip4_config_merge (composite, priv->vpn4_config, NM_IP_CONFIG_MERGE_DEFAULT);
+
+ g_slist_foreach (priv->vpn4_configs, _ip4_config_merge_default, composite);
+
if (priv->ext_ip4_config)
nm_ip4_config_merge (composite, priv->ext_ip4_config, NM_IP_CONFIG_MERGE_DEFAULT);
@@ -4261,6 +4271,15 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
}
}
+static void
+_ip6_config_merge_default (gpointer value, gpointer user_data)
+{
+ NMIP6Config *src = (NMIP6Config *) value;
+ NMIP6Config *dst = (NMIP6Config *) user_data;
+
+ nm_ip6_config_merge (dst, src, NM_IP_CONFIG_MERGE_DEFAULT);
+}
+
static gboolean
ip6_config_merge_and_apply (NMDevice *self,
gboolean commit,
@@ -4306,8 +4325,9 @@ ip6_config_merge_and_apply (NMDevice *self,
(ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0)
| (ignore_auto_dns ? NM_IP_CONFIG_MERGE_NO_DNS : 0));
}
- if (priv->vpn6_config)
- nm_ip6_config_merge (composite, priv->vpn6_config, NM_IP_CONFIG_MERGE_DEFAULT);
+
+ g_slist_foreach (priv->vpn6_configs, _ip6_config_merge_default, composite);
+
if (priv->ext_ip6_config)
nm_ip6_config_merge (composite, priv->ext_ip6_config, NM_IP_CONFIG_MERGE_DEFAULT);
@@ -6854,18 +6874,49 @@ nm_device_set_ip4_config (NMDevice *self,
return success;
}
+static gboolean
+_replace_vpn_config_in_list (GSList **plist, GObject *old, GObject *new)
+{
+ GSList *old_link;
+
+ /* Below, assert that we have an @old instance to replace and that
+ * @new is not yet tracked. But still, behave correctly in any
+ * case. */
+
+ if ( old
+ && (old_link = g_slist_find (*plist, old))) {
+ if (old != new) {
+ if (new)
+ old_link->data = g_object_ref (new);
+ else
+ *plist = g_slist_remove_link (*plist, old_link);
+ g_object_unref (old);
+ }
+ return TRUE;
+ }
+
+ if (new) {
+ if (!g_slist_find (*plist, new))
+ *plist = g_slist_append (*plist, g_object_ref (new));
+ else
+ g_return_val_if_reached (TRUE);
+ g_return_val_if_fail (!old, TRUE);
+ return TRUE;
+ }
+
+ /* return FALSE if both @old and @new are unset. */
+ g_return_val_if_fail (!old, FALSE);
+ return FALSE;
+}
+
void
-nm_device_set_vpn4_config (NMDevice *self, NMIP4Config *config)
+nm_device_replace_vpn4_config (NMDevice *self, NMIP4Config *old, NMIP4Config *config)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- if (priv->vpn4_config == config)
+ if (!_replace_vpn_config_in_list (&priv->vpn4_configs, (GObject *) old, (GObject *) config))
return;
- g_clear_object (&priv->vpn4_config);
- if (config)
- priv->vpn4_config = g_object_ref (config);
-
/* NULL to use existing configs */
if (!ip4_config_merge_and_apply (self, NULL, TRUE, NULL))
_LOGW (LOGD_IP4, "failed to set VPN routes for device");
@@ -6982,17 +7033,13 @@ nm_device_set_ip6_config (NMDevice *self,
}
void
-nm_device_set_vpn6_config (NMDevice *self, NMIP6Config *config)
+nm_device_replace_vpn6_config (NMDevice *self, NMIP6Config *old, NMIP6Config *config)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- if (priv->vpn6_config == config)
+ if (!_replace_vpn_config_in_list (&priv->vpn6_configs, (GObject *) old, (GObject *) config))
return;
- g_clear_object (&priv->vpn6_config);
- if (config)
- priv->vpn6_config = g_object_ref (config);
-
/* NULL to use existing configs */
if (!ip6_config_merge_and_apply (self, TRUE, NULL))
_LOGW (LOGD_IP6, "failed to set VPN routes for device");
@@ -7590,6 +7637,24 @@ capture_lease_config (NMDevice *self,
}
static void
+_ip4_config_intersect (gpointer value, gpointer user_data)
+{
+ NMIP4Config *dst = (NMIP4Config *) value;
+ NMIP4Config *src = (NMIP4Config *) user_data;
+
+ nm_ip4_config_intersect (dst, src);
+}
+
+static void
+_ip4_config_subtract (gpointer value, gpointer user_data)
+{
+ NMIP4Config *dst = (NMIP4Config *) value;
+ NMIP4Config *src = (NMIP4Config *) user_data;
+
+ nm_ip4_config_subtract (dst, src);
+}
+
+static void
update_ip4_config (NMDevice *self, gboolean initial)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -7626,8 +7691,9 @@ update_ip4_config (NMDevice *self, gboolean initial)
nm_ip4_config_intersect (priv->con_ip4_config, priv->ext_ip4_config);
if (priv->dev_ip4_config)
nm_ip4_config_intersect (priv->dev_ip4_config, priv->ext_ip4_config);
- if (priv->vpn4_config)
- nm_ip4_config_intersect (priv->vpn4_config, priv->ext_ip4_config);
+
+ g_slist_foreach (priv->vpn4_configs, _ip4_config_intersect, priv->ext_ip4_config);
+
if (priv->wwan_ip4_config)
nm_ip4_config_intersect (priv->wwan_ip4_config, priv->ext_ip4_config);
@@ -7638,8 +7704,9 @@ update_ip4_config (NMDevice *self, gboolean initial)
nm_ip4_config_subtract (priv->ext_ip4_config, priv->con_ip4_config);
if (priv->dev_ip4_config)
nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config);
- if (priv->vpn4_config)
- nm_ip4_config_subtract (priv->ext_ip4_config, priv->vpn4_config);
+
+ g_slist_foreach (priv->vpn4_configs, _ip4_config_subtract, priv->ext_ip4_config);
+
if (priv->wwan_ip4_config)
nm_ip4_config_subtract (priv->ext_ip4_config, priv->wwan_ip4_config);
@@ -7648,6 +7715,24 @@ update_ip4_config (NMDevice *self, gboolean initial)
}
static void
+_ip6_config_intersect (gpointer value, gpointer user_data)
+{
+ NMIP6Config *dst = (NMIP6Config *) value;
+ NMIP6Config *src = (NMIP6Config *) user_data;
+
+ nm_ip6_config_intersect (dst, src);
+}
+
+static void
+_ip6_config_subtract (gpointer value, gpointer user_data)
+{
+ NMIP6Config *dst = (NMIP6Config *) value;
+ NMIP6Config *src = (NMIP6Config *) user_data;
+
+ nm_ip6_config_subtract (dst, src);
+}
+
+static void
update_ip6_config (NMDevice *self, gboolean initial)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
@@ -7684,8 +7769,7 @@ update_ip6_config (NMDevice *self, gboolean initial)
nm_ip6_config_intersect (priv->dhcp6_ip6_config, priv->ext_ip6_config);
if (priv->wwan_ip6_config)
nm_ip6_config_intersect (priv->wwan_ip6_config, priv->ext_ip6_config);
- if (priv->vpn6_config)
- nm_ip6_config_intersect (priv->vpn6_config, priv->ext_ip6_config);
+ g_slist_foreach (priv->vpn6_configs, _ip6_config_intersect, priv->ext_ip6_config);
/* Remove parts from ext_ip6_config to only contain the information that
* was configured externally -- we already have the same configuration from
@@ -7698,8 +7782,7 @@ update_ip6_config (NMDevice *self, gboolean initial)
nm_ip6_config_subtract (priv->ext_ip6_config, priv->dhcp6_ip6_config);
if (priv->wwan_ip6_config)
nm_ip6_config_subtract (priv->ext_ip6_config, priv->wwan_ip6_config);
- if (priv->vpn6_config)
- nm_ip6_config_subtract (priv->ext_ip6_config, priv->vpn6_config);
+ g_slist_foreach (priv->vpn6_configs, _ip6_config_subtract, priv->ext_ip6_config);
ip6_config_merge_and_apply (self, FALSE, NULL);
}
@@ -8540,15 +8623,18 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
g_clear_object (&priv->dev_ip4_config);
g_clear_object (&priv->ext_ip4_config);
g_clear_object (&priv->wwan_ip4_config);
- g_clear_object (&priv->vpn4_config);
g_clear_object (&priv->ip4_config);
g_clear_object (&priv->con_ip6_config);
g_clear_object (&priv->ac_ip6_config);
g_clear_object (&priv->ext_ip6_config);
- g_clear_object (&priv->vpn6_config);
g_clear_object (&priv->wwan_ip6_config);
g_clear_object (&priv->ip6_config);
+ g_slist_free_full (priv->vpn4_configs, g_object_unref);
+ priv->vpn4_configs = NULL;
+ g_slist_free_full (priv->vpn6_configs, g_object_unref);
+ priv->vpn6_configs = NULL;
+
clear_act_request (self);
/* Clear legacy IPv4 address property */
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 15858194a7..6a5cd46f29 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -345,10 +345,14 @@ NMDhcp4Config * nm_device_get_dhcp4_config (NMDevice *dev);
NMDhcp6Config * nm_device_get_dhcp6_config (NMDevice *dev);
NMIP4Config * nm_device_get_ip4_config (NMDevice *dev);
-void nm_device_set_vpn4_config (NMDevice *dev, NMIP4Config *config);
+void nm_device_replace_vpn4_config (NMDevice *dev,
+ NMIP4Config *old,
+ NMIP4Config *config);
NMIP6Config * nm_device_get_ip6_config (NMDevice *dev);
-void nm_device_set_vpn6_config (NMDevice *dev, NMIP6Config *config);
+void nm_device_replace_vpn6_config (NMDevice *dev,
+ NMIP6Config *old,
+ NMIP6Config *config);
void nm_device_capture_initial_config (NMDevice *dev);
diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c
index 257ebf41ae..d47e15398a 100644
--- a/src/vpn-manager/nm-vpn-connection.c
+++ b/src/vpn-manager/nm-vpn-connection.c
@@ -105,6 +105,13 @@ typedef struct {
guint32 ip4_external_gw;
gboolean has_ip6;
NMIP6Config *ip6_config;
+
+ /* These config instances are passed on to NMDevice and modified by NMDevice.
+ * This pointer is only useful for nm_device_replace_vpn4_config() to clear the
+ * previous configuration. Consider these instances to be owned by NMDevice. */
+ NMIP4Config *last_device_ip4_config;
+ NMIP6Config *last_device_ip6_config;
+
struct in6_addr *ip6_internal_gw;
struct in6_addr *ip6_external_gw;
char *ip_iface;
@@ -330,6 +337,22 @@ fw_call_cleanup (NMVpnConnection *self)
}
static void
+remove_parent_device_config (NMVpnConnection *connection, NMDevice *device)
+{
+ NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+
+ if (priv->last_device_ip4_config) {
+ nm_device_replace_vpn4_config (device, priv->last_device_ip4_config, NULL);
+ g_clear_object (&priv->last_device_ip4_config);
+ }
+
+ if (priv->last_device_ip6_config) {
+ nm_device_replace_vpn6_config (device, priv->last_device_ip6_config, NULL);
+ g_clear_object (&priv->last_device_ip6_config);
+ }
+}
+
+static void
vpn_cleanup (NMVpnConnection *self, NMDevice *parent_dev)
{
NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
@@ -340,8 +363,7 @@ vpn_cleanup (NMVpnConnection *self, NMDevice *parent_dev)
nm_platform_address_flush (NM_PLATFORM_GET, priv->ip_ifindex);
}
- nm_device_set_vpn4_config (parent_dev, NULL);
- nm_device_set_vpn6_config (parent_dev, NULL);
+ remove_parent_device_config (self, parent_dev);
/* Remove zone from firewall */
if (priv->ip_iface) {
@@ -1030,22 +1052,19 @@ apply_parent_device_config (NMVpnConnection *self)
}
}
- if (vpn4_parent_config) {
- /* Add any explicit route to the VPN gateway through the parent device */
- if (priv->ip4_external_gw)
- add_ip4_vpn_gateway_route (vpn4_parent_config, parent_dev, priv->ip4_external_gw);
+ /* Add any explicit route to the VPN gateway through the parent device */
+ if (vpn4_parent_config && priv->ip4_external_gw)
+ add_ip4_vpn_gateway_route (vpn4_parent_config, parent_dev, priv->ip4_external_gw);
+ if (vpn6_parent_config && priv->ip6_external_gw)
+ add_ip6_vpn_gateway_route (vpn6_parent_config, parent_dev, priv->ip6_external_gw);
- nm_device_set_vpn4_config (parent_dev, vpn4_parent_config);
- g_object_unref (vpn4_parent_config);
- }
- if (vpn6_parent_config) {
- /* Add any explicit route to the VPN gateway through the parent device */
- if (priv->ip6_external_gw)
- add_ip6_vpn_gateway_route (vpn6_parent_config, parent_dev, priv->ip6_external_gw);
+ nm_device_replace_vpn4_config (parent_dev, priv->last_device_ip4_config, vpn4_parent_config);
+ g_clear_object (&priv->last_device_ip4_config);
+ priv->last_device_ip4_config = vpn4_parent_config;
- nm_device_set_vpn6_config (parent_dev, vpn6_parent_config);
- g_object_unref (vpn6_parent_config);
- }
+ nm_device_replace_vpn6_config (parent_dev, priv->last_device_ip6_config, vpn6_parent_config);
+ g_clear_object (&priv->last_device_ip6_config);
+ priv->last_device_ip6_config = vpn6_parent_config;
}
static gboolean
@@ -2242,10 +2261,8 @@ device_changed (NMActiveConnection *active,
* 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 (old_device)
+ remove_parent_device_config (NM_VPN_CONNECTION (active), old_device);
if (new_device)
apply_parent_device_config (NM_VPN_CONNECTION (active));