d4c1522
From d7c369712b9e6298d62303899e372ab7d27a92d9 Mon Sep 17 00:00:00 2001
d4c1522
From: Dan Williams <dcbw@redhat.com>
d4c1522
Date: Mon, 23 Dec 2013 12:21:09 -0600
d4c1522
Subject: [PATCH] vpn: handle missing tunnel interface for IP-based VPNs (bgo
d4c1522
 #721724) (rh #1030068)
d4c1522
MIME-Version: 1.0
d4c1522
Content-Type: text/plain; charset=UTF-8
d4c1522
Content-Transfer-Encoding: 8bit
d4c1522
d4c1522
IPSec-based VPNs that use the kernel IPSec stack don't have tunnel
d4c1522
interfaces, and the IP details (address, routes) get added directly
d4c1522
to the parent network device.  NetworkManager previously required
d4c1522
a tunnel interface and failed the VPN if one was not provided.
d4c1522
d4c1522
When no tunnel interface is passed, construct the VPN IP configuration
d4c1522
using available details and pass that to the NMDevice as the VPN IP
d4c1522
config.  The device will merge that config with its own and apply
d4c1522
any configuration that the kernel/VPN has not already applied.
d4c1522
d4c1522
https://bugzilla.gnome.org/show_bug.cgi?id=721724
d4c1522
https://bugzilla.redhat.com/show_bug.cgi?id=1030068
d4c1522
d4c1522
https://bugzilla.redhat.com/show_bug.cgi?id=865883
d4c1522
https://bugzilla.redhat.com/show_bug.cgi?id=845599
d4c1522
d4c1522
Signed-off-by: Jiří Klimeš <jklimes@redhat.com>
d4c1522
---
d4c1522
 src/nm-policy.c                     |  15 +++--
d4c1522
 src/vpn-manager/nm-vpn-connection.c | 112 ++++++++++++++++++++++--------------
d4c1522
 2 files changed, 79 insertions(+), 48 deletions(-)
d4c1522
d4c1522
diff --git a/src/nm-policy.c b/src/nm-policy.c
d4c1522
index 92ec1ab..090cd04 100644
d4c1522
--- a/src/nm-policy.c
d4c1522
+++ b/src/nm-policy.c
d4c1522
@@ -655,17 +655,21 @@ update_ip4_routing (NMPolicy *policy, gboolean force_update)
d4c1522
 		in_addr_t int_gw = nm_vpn_connection_get_ip4_internal_gateway (vpn);
d4c1522
 		int mss = nm_ip4_config_get_mss (ip4_config);
d4c1522
 
d4c1522
+		/* If no VPN interface, use the parent interface */
d4c1522
+		if (ip_ifindex <= 0)
d4c1522
+			ip_ifindex = parent_ifindex;
d4c1522
+
d4c1522
 		if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, int_gw, 0, mss)) {
d4c1522
 			nm_platform_ip4_route_add (parent_ifindex, gw_addr, 32, 0, 0, parent_mss);
d4c1522
-			if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, int_gw, 0, mss)) {
d4c1522
+			if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, int_gw, 0, mss))
d4c1522
 				nm_log_err (LOGD_IP4 | LOGD_VPN, "Failed to set default route.");
d4c1522
-			}
d4c1522
 		}
d4c1522
 
d4c1522
 		default_device = nm_vpn_connection_get_parent_device (vpn);
d4c1522
 	} else {
d4c1522
 		int mss = nm_ip4_config_get_mss (ip4_config);
d4c1522
 
d4c1522
+		g_assert (ip_iface);
d4c1522
 		if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, gw_addr, 0, mss)) {
d4c1522
 			nm_platform_ip4_route_add (ip_ifindex, gw_addr, 32, 0, 0, mss);
d4c1522
 			if (!nm_platform_ip4_route_add (ip_ifindex, 0, 0, gw_addr, 0, mss)) {
d4c1522
@@ -845,6 +849,10 @@ update_ip6_routing (NMPolicy *policy, gboolean force_update)
d4c1522
 		if (!int_gw)
d4c1522
 			int_gw = &in6addr_any;
d4c1522
 
d4c1522
+		/* If no VPN interface, use the parent interface */
d4c1522
+		if (ip_ifindex <= 0)
d4c1522
+			ip_ifindex = parent_ifindex;
d4c1522
+
d4c1522
 		if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *int_gw, 0, mss)) {
d4c1522
 			nm_platform_ip6_route_add (parent_ifindex, *gw_addr, 128, in6addr_any, 0, parent_mss);
d4c1522
 			if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *int_gw, 0, mss)) {
d4c1522
@@ -858,9 +866,8 @@ update_ip6_routing (NMPolicy *policy, gboolean force_update)
d4c1522
 
d4c1522
 		if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *gw_addr, 0, mss)) {
d4c1522
 			nm_platform_ip6_route_add (ip_ifindex, *gw_addr, 128, in6addr_any, 0, mss);
d4c1522
-			if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *gw_addr, 0, mss)) {
d4c1522
+			if (!nm_platform_ip6_route_add (ip_ifindex, in6addr_any, 0, *gw_addr, 0, mss))
d4c1522
 				nm_log_err (LOGD_IP6, "Failed to set default route.");
d4c1522
-			}
d4c1522
 		}
d4c1522
 
d4c1522
 		default_device6 = best;
d4c1522
diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c
d4c1522
index 8541f10..f1d7d46 100644
d4c1522
--- a/src/vpn-manager/nm-vpn-connection.c
d4c1522
+++ b/src/vpn-manager/nm-vpn-connection.c
d4c1522
@@ -301,13 +301,13 @@ device_state_changed (NMActiveConnection *active,
d4c1522
 }
d4c1522
 
d4c1522
 static void
d4c1522
-add_ip4_vpn_gateway_route (NMDevice *parent_device, guint32 vpn_gw)
d4c1522
+add_ip4_vpn_gateway_route (NMIP4Config *config, NMDevice *parent_device, guint32 vpn_gw)
d4c1522
 {
d4c1522
 	NMIP4Config *parent_config;
d4c1522
 	guint32 parent_gw;
d4c1522
 	NMPlatformIP4Route route;
d4c1522
-	NMIP4Config *vpn4_config;
d4c1522
 
d4c1522
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
d4c1522
 	g_return_if_fail (NM_IS_DEVICE (parent_device));
d4c1522
 	g_return_if_fail (vpn_gw != 0);
d4c1522
 
d4c1522
@@ -321,8 +321,6 @@ add_ip4_vpn_gateway_route (NMDevice *parent_device, guint32 vpn_gw)
d4c1522
 	if (!parent_gw)
d4c1522
 		return;
d4c1522
 
d4c1522
-	vpn4_config = nm_ip4_config_new ();
d4c1522
-
d4c1522
 	memset (&route, 0, sizeof (route));
d4c1522
 	route.network = vpn_gw;
d4c1522
 	route.plen = 32;
d4c1522
@@ -335,7 +333,7 @@ add_ip4_vpn_gateway_route (NMDevice *parent_device, guint32 vpn_gw)
d4c1522
 	if (nm_ip4_config_destination_is_direct (parent_config, vpn_gw, 32))
d4c1522
 		route.gateway = 0;
d4c1522
 
d4c1522
-	nm_ip4_config_add_route (vpn4_config, &route);
d4c1522
+	nm_ip4_config_add_route (config, &route);
d4c1522
 
d4c1522
 	/* Ensure there's a route to the parent device's gateway through the
d4c1522
 	 * parent device, since if the VPN claims the default route and the VPN
d4c1522
@@ -346,21 +344,19 @@ add_ip4_vpn_gateway_route (NMDevice *parent_device, guint32 vpn_gw)
d4c1522
 	route.network = parent_gw;
d4c1522
 	route.plen = 32;
d4c1522
 
d4c1522
-	nm_ip4_config_add_route (vpn4_config, &route);
d4c1522
-
d4c1522
-	nm_device_set_vpn4_config (parent_device, vpn4_config);
d4c1522
-	g_object_unref (vpn4_config);
d4c1522
+	nm_ip4_config_add_route (config, &route);
d4c1522
 }
d4c1522
 
d4c1522
 static void
d4c1522
-add_ip6_vpn_gateway_route (NMDevice *parent_device,
d4c1522
+add_ip6_vpn_gateway_route (NMIP6Config *config,
d4c1522
+                           NMDevice *parent_device,
d4c1522
                            const struct in6_addr *vpn_gw)
d4c1522
 {
d4c1522
 	NMIP6Config *parent_config;
d4c1522
 	const struct in6_addr *parent_gw;
d4c1522
 	NMPlatformIP6Route route;
d4c1522
-	NMIP6Config *vpn6_config;
d4c1522
 
d4c1522
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
d4c1522
 	g_return_if_fail (NM_IS_DEVICE (parent_device));
d4c1522
 	g_return_if_fail (vpn_gw != NULL);
d4c1522
 
d4c1522
@@ -370,8 +366,6 @@ add_ip6_vpn_gateway_route (NMDevice *parent_device,
d4c1522
 	if (!parent_gw)
d4c1522
 		return;
d4c1522
 
d4c1522
-	vpn6_config = nm_ip6_config_new ();
d4c1522
-
d4c1522
 	memset (&route, 0, sizeof (route));
d4c1522
 	route.network = *vpn_gw;
d4c1522
 	route.plen = 128;
d4c1522
@@ -384,7 +378,7 @@ add_ip6_vpn_gateway_route (NMDevice *parent_device,
d4c1522
 	if (nm_ip6_config_destination_is_direct (parent_config, vpn_gw, 128))
d4c1522
 		route.gateway = in6addr_any;
d4c1522
 
d4c1522
-	nm_ip6_config_add_route (vpn6_config, &route);
d4c1522
+	nm_ip6_config_add_route (config, &route);
d4c1522
 
d4c1522
 	/* Ensure there's a route to the parent device's gateway through the
d4c1522
 	 * parent device, since if the VPN claims the default route and the VPN
d4c1522
@@ -395,10 +389,7 @@ add_ip6_vpn_gateway_route (NMDevice *parent_device,
d4c1522
 	route.network = *parent_gw;
d4c1522
 	route.plen = 128;
d4c1522
 
d4c1522
-	nm_ip6_config_add_route (vpn6_config, &route);
d4c1522
-
d4c1522
-	nm_device_set_vpn6_config (parent_device, vpn6_config);
d4c1522
-	g_object_unref (vpn6_config);
d4c1522
+	nm_ip6_config_add_route (config, &route);
d4c1522
 }
d4c1522
 
d4c1522
 NMVPNConnection *
d4c1522
@@ -601,7 +592,7 @@ print_vpn_config (NMVPNConnection *connection)
d4c1522
 		             ip6_address_to_string (priv->ip6_external_gw));
d4c1522
 	}
d4c1522
 
d4c1522
-	nm_log_info (LOGD_VPN, "Tunnel Device: %s", priv->ip_iface);
d4c1522
+	nm_log_info (LOGD_VPN, "Tunnel Device: %s", priv->ip_iface ? priv->ip_iface : "(none)");
d4c1522
 
d4c1522
 	if (priv->ip4_config) {
d4c1522
 		nm_log_info (LOGD_VPN, "IPv4 configuration:");
d4c1522
@@ -692,25 +683,54 @@ nm_vpn_connection_apply_config (NMVPNConnection *connection)
d4c1522
 nm_vpn_connection_apply_config (NMVPNConnection *connection)
d4c1522
 {
d4c1522
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
d4c1522
+	NMIP4Config *vpn4_parent_config = NULL;
d4c1522
+	NMIP6Config *vpn6_parent_config = NULL;
d4c1522
 
d4c1522
-	nm_platform_link_set_up (priv->ip_ifindex);
d4c1522
+	if (priv->ip_ifindex > 0) {
d4c1522
+		nm_platform_link_set_up (priv->ip_ifindex);
d4c1522
 
d4c1522
-	if (priv->ip4_config) {
d4c1522
-		if (!nm_ip4_config_commit (priv->ip4_config, priv->ip_ifindex, 0))
d4c1522
-			return FALSE;
d4c1522
+		if (priv->ip4_config) {
d4c1522
+			if (!nm_ip4_config_commit (priv->ip4_config, priv->ip_ifindex, 0))
d4c1522
+				return FALSE;
d4c1522
+		}
d4c1522
+
d4c1522
+		if (priv->ip6_config) {
d4c1522
+			if (!nm_ip6_config_commit (priv->ip6_config, priv->ip_ifindex, 0))
d4c1522
+				return FALSE;
d4c1522
+		}
d4c1522
+
d4c1522
+		if (priv->ip4_config)
d4c1522
+			vpn4_parent_config = nm_ip4_config_new ();
d4c1522
+		if (priv->ip6_config)
d4c1522
+			vpn6_parent_config = nm_ip6_config_new ();
d4c1522
+	} else {
d4c1522
+		/* If the VPN didn't return a network interface, it is a route-based
d4c1522
+		 * VPN (like kernel IPSec) and all IP addressing and routing should
d4c1522
+		 * be done on the parent interface instead.
d4c1522
+		 */
d4c1522
+
d4c1522
+		if (priv->ip4_config)
d4c1522
+			vpn4_parent_config = g_object_ref (priv->ip4_config);
d4c1522
+		if (priv->ip6_config)
d4c1522
+			vpn6_parent_config = g_object_ref (priv->ip6_config);
d4c1522
 	}
d4c1522
 
d4c1522
-	if (priv->ip6_config) {
d4c1522
-		if (!nm_ip6_config_commit (priv->ip6_config, priv->ip_ifindex, 0))
d4c1522
-			/* FIXME: remove ip4 config */
d4c1522
-			return FALSE;
d4c1522
+	if (vpn4_parent_config) {
d4c1522
+		/* Add any explicit route to the VPN gateway through the parent device */
d4c1522
+		if (priv->ip4_external_gw)
d4c1522
+			add_ip4_vpn_gateway_route (vpn4_parent_config, priv->parent_dev, priv->ip4_external_gw);
d4c1522
+
d4c1522
+		nm_device_set_vpn4_config (priv->parent_dev, vpn4_parent_config);
d4c1522
+		g_object_unref (vpn4_parent_config);
d4c1522
 	}
d4c1522
+	if (vpn6_parent_config) {
d4c1522
+		/* Add any explicit route to the VPN gateway through the parent device */
d4c1522
+		if (priv->ip6_external_gw)
d4c1522
+			add_ip6_vpn_gateway_route (vpn6_parent_config, priv->parent_dev, priv->ip6_external_gw);
d4c1522
 
d4c1522
-	/* Add any explicit route to the VPN gateway through the parent device */
d4c1522
-	if (priv->ip4_external_gw)
d4c1522
-		add_ip4_vpn_gateway_route (priv->parent_dev, priv->ip4_external_gw);
d4c1522
-	if (priv->ip6_external_gw)
d4c1522
-		add_ip6_vpn_gateway_route (priv->parent_dev, priv->ip6_external_gw);
d4c1522
+		nm_device_set_vpn6_config (priv->parent_dev, vpn6_parent_config);
d4c1522
+		g_object_unref (vpn6_parent_config);
d4c1522
+	}
d4c1522
 
d4c1522
 	nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.",
d4c1522
 	             nm_connection_get_id (priv->connection));
d4c1522
@@ -768,21 +788,25 @@ process_generic_config (NMVPNConnection *connection,
d4c1522
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
d4c1522
 	GValue *val;
d4c1522
 
d4c1522
+	g_clear_pointer (&priv->ip_iface, g_free);
d4c1522
+
d4c1522
 	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_TUNDEV);
d4c1522
-	if (val)
d4c1522
-		priv->ip_iface = g_strdup (g_value_get_string (val));
d4c1522
-	else {
d4c1522
-		nm_log_err (LOGD_VPN, "invalid or missing tunnel device received!");
d4c1522
-		nm_vpn_connection_config_maybe_complete (connection, FALSE);
d4c1522
-		return FALSE;
d4c1522
+	if (val) {
d4c1522
+		const char *tmp = g_value_get_string (val);
d4c1522
+
d4c1522
+		/* Backwards compat with NM-openswan */
d4c1522
+		if (g_strcmp0 (tmp, "_none_") != 0)
d4c1522
+			priv->ip_iface = g_strdup (tmp);
d4c1522
 	}
d4c1522
 
d4c1522
-	/* Grab the interface index for address/routing operations */
d4c1522
-	priv->ip_ifindex = nm_platform_link_get_ifindex (priv->ip_iface);
d4c1522
-	if (!priv->ip_ifindex) {
d4c1522
-		nm_log_err (LOGD_VPN, "(%s): failed to look up VPN interface index", priv->ip_iface);
d4c1522
-		nm_vpn_connection_config_maybe_complete (connection, FALSE);
d4c1522
-		return FALSE;
d4c1522
+	if (priv->ip_iface) {
d4c1522
+		/* Grab the interface index for address/routing operations */
d4c1522
+		priv->ip_ifindex = nm_platform_link_get_ifindex (priv->ip_iface);
d4c1522
+		if (!priv->ip_ifindex) {
d4c1522
+			nm_log_err (LOGD_VPN, "(%s): failed to look up VPN interface index", priv->ip_iface);
d4c1522
+			nm_vpn_connection_config_maybe_complete (connection, FALSE);
d4c1522
+			return FALSE;
d4c1522
+		}
d4c1522
 	}
d4c1522
 
d4c1522
 	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_BANNER);
d4c1522
-- 
d4c1522
1.7.11.7
d4c1522