diff options
author | Lubomir Rintel <lkundrak@v3.sk> | 2015-08-19 23:07:13 +0200 |
---|---|---|
committer | Lubomir Rintel <lkundrak@v3.sk> | 2015-10-13 18:20:56 +0200 |
commit | 9bbf5e94c2ff676f685a7740713140026efbcb7c (patch) | |
tree | e8d389fd1e1aea2a7cb014d7dfbf090a80d93f24 | |
parent | 965363a6d5ef662b2d75cda8aa9d55ac02991176 (diff) | |
download | NetworkManager-9bbf5e94c2ff676f685a7740713140026efbcb7c.tar.gz |
device: allow multiple vpn IP configurations
-rw-r--r-- | src/devices/nm-device.c | 142 | ||||
-rw-r--r-- | src/devices/nm-device.h | 8 | ||||
-rw-r--r-- | src/vpn-manager/nm-vpn-connection.c | 57 |
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)); |