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