Commit 9bbf5e94 authored by Lubomir Rintel's avatar Lubomir Rintel 🥕

device: allow multiple vpn IP configurations

parent 965363a6
......@@ -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");
......@@ -7589,6 +7636,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)
{
......@@ -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);
......@@ -7647,6 +7714,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)
{
......@@ -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 */
......
......@@ -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);
......
......@@ -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;
......@@ -329,6 +336,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)
{
......@@ -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));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment