diff --git a/include/NetworkManagerVPN.h b/include/NetworkManagerVPN.h
index f755721b6..6e654b3a7 100644
--- a/include/NetworkManagerVPN.h
+++ b/include/NetworkManagerVPN.h
@@ -114,8 +114,28 @@ typedef enum {
} NMVPNPluginFailure;
+/*** Generic config ***/
+
+/* string: VPN interface name (tun0, tap0, etc) */
+#define NM_VPN_PLUGIN_CONFIG_TUNDEV "tundev"
+
+/* string: Login message */
+#define NM_VPN_PLUGIN_CONFIG_BANNER "banner"
+
/* uint32 / array of uint8: IP address of the public external VPN gateway (network byte order) */
-#define NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY "gateway"
+#define NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY "gateway"
+
+/* uint32: Maximum Transfer Unit that the VPN interface should use */
+#define NM_VPN_PLUGIN_CONFIG_MTU "mtu"
+
+/* boolean: Has IP4 configuration? */
+#define NM_VPN_PLUGIN_CONFIG_HAS_IP4 "has-ip4"
+
+/* boolean: Has IP6 configuration? */
+#define NM_VPN_PLUGIN_CONFIG_HAS_IP6 "has-ip6"
+
+
+/*** Ip4Config ***/
/* uint32: IP address of the internal gateway of the subnet the VPN interface is
* on, if the VPN uses subnet configuration (network byte order)
@@ -142,30 +162,14 @@ typedef enum {
/* uint32: Message Segment Size that the VPN interface should use */
#define NM_VPN_PLUGIN_IP4_CONFIG_MSS "mss"
-/* uint32: Maximum Transfer Unit that the VPN interface should use */
-#define NM_VPN_PLUGIN_IP4_CONFIG_MTU "mtu"
-
-/* string: VPN interface name (tun0, tap0, etc) */
-#define NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV "tundev"
-
/* string: DNS domain name */
#define NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN "domain"
/* array of strings: DNS domain names */
#define NM_VPN_PLUGIN_IP4_CONFIG_DOMAINS "domains"
-/* string: Login message */
-#define NM_VPN_PLUGIN_IP4_CONFIG_BANNER "banner"
-
-/* array of array of uint32: custom routes the client should apply. NOTE: NM
- * expects the D-Bus argument signature "aau" here. i.e., an array of
- * routes, where each route is a 4-element array of uint32 values.
- *
- * Each route consists of the following 4 uint32 values, in this order:
- * 1: destination IP address (network byte order)
- * 2: destination prefix (1 - 32 inclusive)
- * 3: IP address of next hop (network byte order)
- * 4: route metric
+/* [ip4 routes]: custom routes the client should apply, in the format used
+ * by nm_utils_ip4_routes_to/from_gvalue
*/
#define NM_VPN_PLUGIN_IP4_CONFIG_ROUTES "routes"
@@ -175,5 +179,52 @@ typedef enum {
/* Deprecated */
#define NM_VPN_PLUGIN_IP4_CONFIG_GATEWAY NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY
+/* Legacy IP4 items; these are included in the IP4 config by older plugins,
+ * but in the generic config by newer plugins.
+ */
+
+#define NM_VPN_PLUGIN_IP4_CONFIG_BANNER NM_VPN_PLUGIN_CONFIG_BANNER
+#define NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY
+#define NM_VPN_PLUGIN_IP4_CONFIG_MTU NM_VPN_PLUGIN_CONFIG_MTU
+#define NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV NM_VPN_PLUGIN_CONFIG_TUNDEV
+
+
+/*** Ip6Config ***/
+
+/* array of uint8: IP address of the internal gateway of the subnet the VPN interface is
+ * on, if the VPN uses subnet configuration (network byte order)
+ */
+#define NM_VPN_PLUGIN_IP6_CONFIG_INT_GATEWAY "internal-gateway"
+
+/* array of uint8: internal IP address of the local VPN interface (network byte order) */
+#define NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS "address"
+
+/* array of uint8: IP address of the other side of Point-to-Point connection if the VPN
+ * uses Point-to-Point configuration. (network byte order)
+ */
+#define NM_VPN_PLUGIN_IP6_CONFIG_PTP "ptp"
+
+/* uint32: prefix length of the VPN interface; 1 - 128 inclusive */
+#define NM_VPN_PLUGIN_IP6_CONFIG_PREFIX "prefix"
+
+/* array of array of uint8: IP addresses of DNS servers for the VPN (network byte order) */
+#define NM_VPN_PLUGIN_IP6_CONFIG_DNS "dns"
+
+/* uint32: Message Segment Size that the VPN interface should use */
+#define NM_VPN_PLUGIN_IP6_CONFIG_MSS "mss"
+
+/* string: DNS domain name */
+#define NM_VPN_PLUGIN_IP6_CONFIG_DOMAIN "domain"
+
+/* array of strings: DNS domain names */
+#define NM_VPN_PLUGIN_IP6_CONFIG_DOMAINS "domains"
+
+/* [ip6 routes]: custom routes the client should apply, in the format used
+ * by nm_utils_ip6_routes_to/from_gvalue
+ */
+#define NM_VPN_PLUGIN_IP6_CONFIG_ROUTES "routes"
+
+/* boolean: prevent this VPN connection from ever getting the default route */
+#define NM_VPN_PLUGIN_IP6_CONFIG_NEVER_DEFAULT "never-default"
#endif /* NETWORK_MANAGER_VPN_H */
diff --git a/introspection/nm-vpn-plugin.xml b/introspection/nm-vpn-plugin.xml
index 72861f266..5fb11622a 100644
--- a/introspection/nm-vpn-plugin.xml
+++ b/introspection/nm-vpn-plugin.xml
@@ -55,6 +55,18 @@
+
+
+ Set generic connection details on the connection.
+
+
+
+
+ Generic configuration details for the connection.
+
+
+
+
Set IPv4 details on the connection.
@@ -62,7 +74,21 @@
- Ip4Config details for the conneciton.
+ Ip4Config details for the connection. You must call
+ SetConfig() before calling this.
+
+
+
+
+
+
+ Set IPv6 details on the connection.
+
+
+
+
+ Ip6Config details for the connection. You must call
+ SetConfig() before calling this.
@@ -96,6 +122,17 @@
+
+
+ The plugin obtained generic configuration information.
+
+
+
+ The configuration information.
+
+
+
+
The plugin obtained an IPv4 configuration.
@@ -107,6 +144,17 @@
+
+
+ The plugin obtained an IPv6 configuration.
+
+
+
+ The IPv6 configuration.
+
+
+
+
Emitted when the plugin receives a login banner from the VPN service.
diff --git a/libnm-glib/nm-vpn-plugin-utils.c b/libnm-glib/nm-vpn-plugin-utils.c
index 8234a9197..b9838df93 100644
--- a/libnm-glib/nm-vpn-plugin-utils.c
+++ b/libnm-glib/nm-vpn-plugin-utils.c
@@ -24,7 +24,9 @@
#include
#include "nm-vpn-plugin-utils.h"
+#include "nm-vpn-plugin.h"
#include "nm-setting-private.h"
+#include "nm-dbus-glib-types.h"
#define DATA_KEY_TAG "DATA_KEY="
#define DATA_VAL_TAG "DATA_VAL="
@@ -185,4 +187,3 @@ nm_vpn_plugin_utils_get_secret_flags (GHashTable *data,
g_free (flag_name);
return success;
}
-
diff --git a/libnm-glib/nm-vpn-plugin.c b/libnm-glib/nm-vpn-plugin.c
index daea2fc8e..d41a4f6dd 100644
--- a/libnm-glib/nm-vpn-plugin.c
+++ b/libnm-glib/nm-vpn-plugin.c
@@ -41,10 +41,18 @@ static gboolean impl_vpn_plugin_need_secrets (NMVPNPlugin *plugin,
static gboolean impl_vpn_plugin_disconnect (NMVPNPlugin *plugin,
GError **err);
+static gboolean impl_vpn_plugin_set_config (NMVPNPlugin *plugin,
+ GHashTable *config,
+ GError **err);
+
static gboolean impl_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin,
GHashTable *config,
GError **err);
+static gboolean impl_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin,
+ GHashTable *config,
+ GError **err);
+
static gboolean impl_vpn_plugin_set_failure (NMVPNPlugin *plugin,
char *reason,
GError **err);
@@ -67,13 +75,21 @@ typedef struct {
guint connect_timer;
guint quit_timer;
guint fail_stop_id;
+
+ gboolean has_ip4, got_ip4;
+ gboolean has_ip6, got_ip6;
+
+ /* Config stuff copied from config to ip4config */
+ GValue banner, tundev, gateway, mtu;
} NMVPNPluginPrivate;
#define NM_VPN_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_PLUGIN, NMVPNPluginPrivate))
enum {
STATE_CHANGED,
+ CONFIG,
IP4_CONFIG,
+ IP6_CONFIG,
LOGIN_BANNER,
FAILURE,
QUIT,
@@ -315,16 +331,108 @@ nm_vpn_plugin_connect (NMVPNPlugin *plugin,
return ret;
}
+void
+nm_vpn_plugin_set_config (NMVPNPlugin *plugin,
+ GHashTable *config)
+{
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
+ GValue *val;
+
+ g_return_if_fail (NM_IS_VPN_PLUGIN (plugin));
+ g_return_if_fail (config != NULL);
+
+ val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_HAS_IP4);
+ if (val && g_value_get_boolean (val))
+ priv->has_ip4 = TRUE;
+ val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_HAS_IP6);
+ if (val && g_value_get_boolean (val))
+ priv->has_ip6 = TRUE;
+
+ g_warn_if_fail (priv->has_ip4 || priv->has_ip6);
+
+ /* Record the items that need to also be inserted into the
+ * ip4config, for compatibility with older daemons.
+ */
+ val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_BANNER);
+ if (val) {
+ g_value_init (&priv->banner, G_VALUE_TYPE (val));
+ g_value_copy (val, &priv->banner);
+ }
+ val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_TUNDEV);
+ if (val) {
+ g_value_init (&priv->tundev, G_VALUE_TYPE (val));
+ g_value_copy (val, &priv->tundev);
+ }
+ val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY);
+ if (val) {
+ g_value_init (&priv->gateway, G_VALUE_TYPE (val));
+ g_value_copy (val, &priv->gateway);
+ }
+ val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_MTU);
+ if (val) {
+ g_value_init (&priv->mtu, G_VALUE_TYPE (val));
+ g_value_copy (val, &priv->mtu);
+ }
+
+ g_signal_emit (plugin, signals[CONFIG], 0, config);
+}
+
void
nm_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin,
GHashTable *ip4_config)
{
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
+ GHashTable *combined_config;
+ GHashTableIter iter;
+ gpointer key, value;
+
g_return_if_fail (NM_IS_VPN_PLUGIN (plugin));
g_return_if_fail (ip4_config != NULL);
- g_signal_emit (plugin, signals[IP4_CONFIG], 0, ip4_config);
+ priv->got_ip4 = TRUE;
- nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED);
+ /* Older NetworkManager daemons expect all config info to be in
+ * the ip4 config, so they won't even notice the "config" signal
+ * being emitted. So just copy all of that data into the ip4
+ * config too.
+ */
+ combined_config = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_iter_init (&iter, ip4_config);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ g_hash_table_insert (combined_config, key, value);
+
+ if (G_VALUE_TYPE (&priv->banner) != G_TYPE_INVALID)
+ g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_BANNER, &priv->banner);
+ if (G_VALUE_TYPE (&priv->tundev) != G_TYPE_INVALID)
+ g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, &priv->tundev);
+ if (G_VALUE_TYPE (&priv->gateway) != G_TYPE_INVALID)
+ g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, &priv->gateway);
+ if (G_VALUE_TYPE (&priv->mtu) != G_TYPE_INVALID)
+ g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_MTU, &priv->mtu);
+
+ g_signal_emit (plugin, signals[IP4_CONFIG], 0, combined_config);
+ g_hash_table_destroy (combined_config);
+
+ if ( priv->has_ip4 == priv->got_ip4
+ && priv->has_ip6 == priv->got_ip6)
+ nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED);
+}
+
+void
+nm_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin,
+ GHashTable *ip6_config)
+{
+ NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin);
+
+ g_return_if_fail (NM_IS_VPN_PLUGIN (plugin));
+ g_return_if_fail (ip6_config != NULL);
+
+ priv->got_ip6 = TRUE;
+ g_signal_emit (plugin, signals[IP6_CONFIG], 0, ip6_config);
+
+ if ( priv->has_ip4 == priv->got_ip4
+ && priv->has_ip6 == priv->got_ip6)
+ nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED);
}
@@ -412,6 +520,16 @@ impl_vpn_plugin_disconnect (NMVPNPlugin *plugin,
return nm_vpn_plugin_disconnect (plugin, err);
}
+static gboolean
+impl_vpn_plugin_set_config (NMVPNPlugin *plugin,
+ GHashTable *config,
+ GError **err)
+{
+ nm_vpn_plugin_set_config (plugin, config);
+
+ return TRUE;
+}
+
static gboolean
impl_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin,
GHashTable *config,
@@ -422,6 +540,16 @@ impl_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin,
return TRUE;
}
+static gboolean
+impl_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin,
+ GHashTable *config,
+ GError **err)
+{
+ nm_vpn_plugin_set_ip6_config (plugin, config);
+
+ return TRUE;
+}
+
static gboolean
impl_vpn_plugin_set_failure (NMVPNPlugin *plugin,
char *reason,
@@ -615,6 +743,11 @@ finalize (GObject *object)
nm_vpn_plugin_set_connection (plugin, NULL);
g_free (priv->dbus_service_name);
+ g_value_unset (&priv->banner);
+ g_value_unset (&priv->tundev);
+ g_value_unset (&priv->gateway);
+ g_value_unset (&priv->mtu);
+
G_OBJECT_CLASS (nm_vpn_plugin_parent_class)->finalize (object);
}
@@ -725,6 +858,16 @@ nm_vpn_plugin_class_init (NMVPNPluginClass *plugin_class)
G_TYPE_NONE, 1,
G_TYPE_UINT);
+ signals[CONFIG] =
+ g_signal_new ("config",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVPNPluginClass, config),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1,
+ DBUS_TYPE_G_MAP_OF_VARIANT);
+
signals[IP4_CONFIG] =
g_signal_new ("ip4-config",
G_OBJECT_CLASS_TYPE (object_class),
@@ -735,6 +878,16 @@ nm_vpn_plugin_class_init (NMVPNPluginClass *plugin_class)
G_TYPE_NONE, 1,
DBUS_TYPE_G_MAP_OF_VARIANT);
+ signals[IP6_CONFIG] =
+ g_signal_new ("ip6-config",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMVPNPluginClass, ip6_config),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE, 1,
+ DBUS_TYPE_G_MAP_OF_VARIANT);
+
signals[LOGIN_BANNER] =
g_signal_new ("login-banner",
G_OBJECT_CLASS_TYPE (object_class),
diff --git a/libnm-glib/nm-vpn-plugin.h b/libnm-glib/nm-vpn-plugin.h
index 252e1a217..cdd55c153 100644
--- a/libnm-glib/nm-vpn-plugin.h
+++ b/libnm-glib/nm-vpn-plugin.h
@@ -91,13 +91,17 @@ typedef struct {
void (*quit) (NMVPNPlugin *plugin);
+ void (*config) (NMVPNPlugin *plugin,
+ GHashTable *config);
+
+ void (*ip6_config) (NMVPNPlugin *plugin,
+ GHashTable *config);
+
/* Padding for future expansion */
void (*_reserved1) (void);
void (*_reserved2) (void);
void (*_reserved3) (void);
void (*_reserved4) (void);
- void (*_reserved5) (void);
- void (*_reserved6) (void);
} NMVPNPluginClass;
GType nm_vpn_plugin_get_type (void);
@@ -115,9 +119,15 @@ void nm_vpn_plugin_set_login_banner (NMVPNPlugin *plugin,
void nm_vpn_plugin_failure (NMVPNPlugin *plugin,
NMVPNPluginFailure reason);
+void nm_vpn_plugin_set_config (NMVPNPlugin *plugin,
+ GHashTable *config);
+
void nm_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin,
GHashTable *ip4_config);
+void nm_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin,
+ GHashTable *ip6_config);
+
gboolean nm_vpn_plugin_disconnect (NMVPNPlugin *plugin,
GError **err);
diff --git a/src/dns-manager/nm-dns-manager.c b/src/dns-manager/nm-dns-manager.c
index b75aa92b5..bb7280760 100644
--- a/src/dns-manager/nm-dns-manager.c
+++ b/src/dns-manager/nm-dns-manager.c
@@ -908,8 +908,6 @@ nm_dns_manager_add_ip6_config (NMDnsManager *mgr,
switch (cfg_type) {
case NM_DNS_IP_CONFIG_TYPE_VPN:
- /* FIXME: not quite yet... */
- g_return_val_if_fail (cfg_type != NM_DNS_IP_CONFIG_TYPE_VPN, FALSE);
priv->ip6_vpn_config = config;
break;
case NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE:
diff --git a/src/nm-policy.c b/src/nm-policy.c
index 16cd117a1..26b24f92e 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -449,9 +449,7 @@ update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update)
int ip_ifindex = 0;
best = get_best_ip4_device (policy->manager, &best_req);
- if (!best)
- goto out;
- if (!force_update && (best == policy->default_device4))
+ if (!force_update && best && (best == policy->default_device4))
goto out;
/* If a VPN connection is active, it is preferred */
@@ -469,7 +467,7 @@ update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update)
/* Check the active IP4 config from the VPN service daemon */
ip4_config = nm_vpn_connection_get_ip4_config (candidate);
- if (ip4_config && nm_ip4_config_get_never_default (ip4_config))
+ if (!ip4_config || nm_ip4_config_get_never_default (ip4_config))
can_default = FALSE;
/* Check the user's preference from the NMConnection */
@@ -481,6 +479,7 @@ update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update)
if (can_default && (vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED)) {
NMIP4Config *parent_ip4;
NMDevice *parent;
+ guint32 parent_mss;
ip_iface = nm_vpn_connection_get_ip_iface (candidate);
ip_ifindex = nm_vpn_connection_get_ip_ifindex (candidate);
@@ -489,13 +488,14 @@ update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update)
parent = nm_vpn_connection_get_parent_device (candidate);
parent_ip4 = nm_device_get_ip4_config (parent);
+ parent_mss = parent_ip4 ? nm_ip4_config_get_mss (parent_ip4) : 0;
nm_system_replace_default_ip4_route_vpn (ip_ifindex,
nm_ip4_address_get_gateway (addr),
nm_vpn_connection_get_ip4_internal_gateway (candidate),
nm_ip4_config_get_mss (ip4_config),
nm_device_get_ip_ifindex (parent),
- nm_ip4_config_get_mss (parent_ip4));
+ parent_mss);
dns_type = NM_DNS_IP_CONFIG_TYPE_VPN;
}
@@ -504,6 +504,9 @@ update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update)
/* The best device gets the default route if a VPN connection didn't */
if (!ip_iface || !ip4_config) {
+ if (!best)
+ goto out;
+
connection = nm_act_request_get_connection (best_req);
ip_iface = nm_device_get_ip_iface (best);
ip_ifindex = nm_device_get_ip_ifindex (best);
@@ -570,9 +573,7 @@ update_ip6_routing_and_dns (NMPolicy *policy, gboolean force_update)
NMActRequest *best_req = NULL;
NMDnsManager *dns_mgr;
GSList *devices = NULL, *iter;
-#if 0
GSList *vpns;
-#endif
NMIP6Config *ip6_config = NULL;
NMIP6Address *addr;
const char *ip_iface = NULL;
@@ -582,12 +583,9 @@ update_ip6_routing_and_dns (NMPolicy *policy, gboolean force_update)
const char *connection_id;
best = get_best_ip6_device (policy->manager, &best_req);
- if (!best)
- goto out;
- if (!force_update && (best == policy->default_device6))
+ if (!force_update && best && (best == policy->default_device6))
goto out;
-#if 0
/* If a VPN connection is active, it is preferred */
vpns = nm_vpn_manager_get_active_connections (policy->vpn_manager);
for (iter = vpns; iter; iter = g_slist_next (iter)) {
@@ -600,6 +598,13 @@ update_ip6_routing_and_dns (NMPolicy *policy, gboolean force_update)
/* If it's marked 'never-default', don't make it default */
vpn_connection = nm_vpn_connection_get_connection (candidate);
g_assert (vpn_connection);
+
+ /* Check the active IP6 config from the VPN service daemon */
+ ip6_config = nm_vpn_connection_get_ip6_config (candidate);
+ if (!ip6_config || nm_ip6_config_get_never_default (ip6_config))
+ can_default = FALSE;
+
+ /* Check the user's preference from the NMConnection */
s_ip6 = nm_connection_get_setting_ip6_config (vpn_connection);
if (s_ip6 && nm_setting_ip6_config_get_never_default (s_ip6))
can_default = FALSE;
@@ -608,30 +613,34 @@ update_ip6_routing_and_dns (NMPolicy *policy, gboolean force_update)
if (can_default && (vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED)) {
NMIP6Config *parent_ip6;
NMDevice *parent;
+ guint32 parent_mss;
ip_iface = nm_vpn_connection_get_ip_iface (candidate);
+ ip_ifindex = nm_vpn_connection_get_ip_ifindex (candidate);
connection = nm_vpn_connection_get_connection (candidate);
- ip6_config = nm_vpn_connection_get_ip6_config (candidate);
addr = nm_ip6_config_get_address (ip6_config, 0);
parent = nm_vpn_connection_get_parent_device (candidate);
parent_ip6 = nm_device_get_ip6_config (parent);
+ parent_mss = parent_ip6 ? nm_ip6_config_get_mss (parent_ip6) : 0;
- nm_system_replace_default_ip6_route_vpn (ip_iface,
+ nm_system_replace_default_ip6_route_vpn (ip_ifindex,
nm_ip6_address_get_gateway (addr),
- nm_vpn_connection_get_ip4_internal_gateway (candidate),
- nm_ip6_config_get_mss (ip4_config),
- nm_device_get_ip_iface (parent),
- nm_ip6_config_get_mss (parent_ip4));
+ nm_vpn_connection_get_ip6_internal_gateway (candidate),
+ nm_ip6_config_get_mss (ip6_config),
+ nm_device_get_ip_ifindex (parent),
+ parent_mss);
dns_type = NM_DNS_IP_CONFIG_TYPE_VPN;
}
}
g_slist_free (vpns);
-#endif
/* The best device gets the default route if a VPN connection didn't */
if (!ip_iface || !ip6_config) {
+ if (!best)
+ goto out;
+
connection = nm_act_request_get_connection (best_req);
ip_iface = nm_device_get_ip_iface (best);
ip_ifindex = nm_device_get_ip_ifindex (best);
diff --git a/src/nm-system.c b/src/nm-system.c
index 741eea601..b58a626bb 100644
--- a/src/nm-system.c
+++ b/src/nm-system.c
@@ -992,7 +992,7 @@ nm_system_replace_default_ip4_route_vpn (int ifindex,
goto out;
if ((err != -NLE_OBJ_NOTFOUND) && (err != -NLE_FAILURE)) {
- nm_log_err (LOGD_DEVICE | LOGD_IP4,
+ nm_log_err (LOGD_DEVICE | LOGD_VPN | LOGD_IP4,
"(%s): failed to set IPv4 default route: %d",
iface, err);
goto out;
@@ -1007,7 +1007,7 @@ nm_system_replace_default_ip4_route_vpn (int ifindex,
err = replace_default_ip4_route (ifindex, int_gw, mss);
if (err != 0) {
nm_netlink_route_delete (gw_route);
- nm_log_err (LOGD_DEVICE | LOGD_IP4,
+ nm_log_err (LOGD_DEVICE | LOGD_VPN | LOGD_IP4,
"(%s): failed to set IPv4 default route (pass #2): %d",
iface, err);
} else
@@ -1072,7 +1072,7 @@ out:
}
static struct rtnl_route *
-add_ip6_route_to_gateway (int ifindex, const struct in6_addr *gw)
+add_ip6_route_to_gateway (int ifindex, const struct in6_addr *gw, int mss)
{
struct nl_sock *nlh;
struct rtnl_route *route = NULL;
@@ -1082,7 +1082,7 @@ add_ip6_route_to_gateway (int ifindex, const struct in6_addr *gw)
g_return_val_if_fail (nlh != NULL, NULL);
/* Gateway might be over a bridge; try adding a route to gateway first */
- route = nm_netlink_route_new (ifindex, AF_INET6, 0,
+ route = nm_netlink_route_new (ifindex, AF_INET6, mss,
NMNL_PROP_SCOPE, RT_SCOPE_LINK,
NMNL_PROP_TABLE, RT_TABLE_MAIN,
NULL);
@@ -1106,7 +1106,7 @@ add_ip6_route_to_gateway (int ifindex, const struct in6_addr *gw)
}
static int
-add_default_ip6_route (int ifindex, const struct in6_addr *gw)
+add_default_ip6_route (int ifindex, const struct in6_addr *gw, int mss)
{
struct rtnl_route *route = NULL;
struct nl_sock *nlh;
@@ -1117,7 +1117,7 @@ add_default_ip6_route (int ifindex, const struct in6_addr *gw)
nlh = nm_netlink_get_default_handle ();
g_return_val_if_fail (nlh != NULL, -ENOMEM);
- route = nm_netlink_route_new (ifindex, AF_INET6, 0,
+ route = nm_netlink_route_new (ifindex, AF_INET6, mss,
NMNL_PROP_SCOPE, RT_SCOPE_UNIVERSE,
NMNL_PROP_TABLE, RT_TABLE_MAIN,
NMNL_PROP_PRIO, 1,
@@ -1150,20 +1150,12 @@ find_static_default_routes (struct rtnl_route *route,
return NULL;
}
-/*
- * nm_system_replace_default_ip6_route
- *
- * Replace default IPv6 route with one via the given gateway
- *
- */
-gboolean
-nm_system_replace_default_ip6_route (int ifindex, const struct in6_addr *gw)
+static int
+replace_default_ip6_route (int ifindex, const struct in6_addr *gw, int mss)
{
GList *def_routes, *iter;
- struct rtnl_route *route, *gw_route = NULL;
- gboolean success = FALSE;
+ struct rtnl_route *route;
char *iface;
- int err;
/* We can't just use NLM_F_REPLACE here like in the IPv4 case, because
* the kernel doesn't like it if we replace the default routes it
@@ -1187,11 +1179,25 @@ nm_system_replace_default_ip6_route (int ifindex, const struct in6_addr *gw)
}
g_list_free (def_routes);
- err = add_default_ip6_route (ifindex, gw);
- if (err == 0)
- return TRUE;
+ return add_default_ip6_route (ifindex, gw, mss);
+}
- if (err == -NLE_EXIST)
+/*
+ * nm_system_replace_default_ip6_route
+ *
+ * Replace default IPv6 route with one via the given gateway
+ *
+ */
+gboolean
+nm_system_replace_default_ip6_route (int ifindex, const struct in6_addr *gw)
+{
+ struct rtnl_route *gw_route = NULL;
+ gboolean success = FALSE;
+ char *iface;
+ int err;
+
+ err = replace_default_ip6_route (ifindex, gw, 0);
+ if (err == 0 || err == -NLE_EXIST)
return TRUE;
iface = nm_netlink_index_to_iface (ifindex);
@@ -1206,12 +1212,12 @@ nm_system_replace_default_ip6_route (int ifindex, const struct in6_addr *gw)
}
/* Try adding a direct route to the gateway first */
- gw_route = add_ip6_route_to_gateway (ifindex, gw);
+ gw_route = add_ip6_route_to_gateway (ifindex, gw, 0);
if (!gw_route)
goto out;
/* Try adding the original route again */
- err = add_default_ip6_route (ifindex, gw);
+ err = replace_default_ip6_route (ifindex, gw, 0);
if (err != 0) {
nm_netlink_route_delete (gw_route);
nm_log_err (LOGD_DEVICE | LOGD_IP6,
@@ -1227,6 +1233,60 @@ out:
return success;
}
+gboolean
+nm_system_replace_default_ip6_route_vpn (int ifindex,
+ const struct in6_addr *ext_gw,
+ const struct in6_addr *int_gw,
+ guint32 mss,
+ int parent_ifindex,
+ guint32 parent_mss)
+{
+ struct rtnl_route *gw_route = NULL;
+ struct nl_sock *nlh;
+ gboolean success = FALSE;
+ int err;
+ char *iface;
+
+ nlh = nm_netlink_get_default_handle ();
+ g_return_val_if_fail (nlh != NULL, FALSE);
+
+ err = replace_default_ip6_route (ifindex, int_gw, mss);
+ if (err == 0)
+ return TRUE;
+
+ iface = nm_netlink_index_to_iface (ifindex);
+ if (!iface)
+ goto out;
+
+ if ((err != -NLE_OBJ_NOTFOUND) && (err != -NLE_FAILURE)) {
+ nm_log_err (LOGD_DEVICE | LOGD_VPN | LOGD_IP6,
+ "(%s): failed to set IPv6 default route: %d",
+ iface, err);
+ goto out;
+ }
+
+ /* Try adding a direct route to the gateway first */
+ gw_route = add_ip6_route_to_gateway (parent_ifindex, ext_gw, parent_mss);
+ if (!gw_route)
+ goto out;
+
+ /* Try adding the original route again */
+ err = replace_default_ip6_route (ifindex, int_gw, mss);
+ if (err != 0) {
+ nm_netlink_route_delete (gw_route);
+ nm_log_err (LOGD_DEVICE | LOGD_VPN | LOGD_IP6,
+ "(%s): failed to set IPv6 default route (pass #2): %d",
+ iface, err);
+ } else
+ success = TRUE;
+
+out:
+ if (gw_route)
+ rtnl_route_put (gw_route);
+ g_free (iface);
+ return success;
+}
+
/*
* nm_system_iface_flush_addresses
*
diff --git a/src/nm-system.h b/src/nm-system.h
index 6a3983809..1acb88538 100644
--- a/src/nm-system.h
+++ b/src/nm-system.h
@@ -53,6 +53,13 @@ gboolean nm_system_replace_default_ip4_route_vpn (int ifindex,
int parent_ifindex,
guint32 parent_mss);
+gboolean nm_system_replace_default_ip6_route_vpn (int ifindex,
+ const struct in6_addr *ext_gw,
+ const struct in6_addr *int_gw,
+ guint32 mss,
+ int parent_ifindex,
+ guint32 parent_mss);
+
struct rtnl_route *nm_system_add_ip4_vpn_gateway_route (NMDevice *parent_device,
guint32 vpn_gw);
struct rtnl_route *nm_system_add_ip6_vpn_gateway_route (NMDevice *parent_device,
diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c
index 45483e150..1f0db995f 100644
--- a/src/vpn-manager/nm-vpn-connection.c
+++ b/src/vpn-manager/nm-vpn-connection.c
@@ -84,13 +84,18 @@ typedef struct {
NMVPNConnectionStateReason failure_reason;
DBusGProxy *proxy;
guint ipconfig_timeout;
+ gboolean has_ip4;
NMIP4Config *ip4_config;
guint32 ip4_internal_gw;
guint32 ip4_external_gw;
+ gboolean has_ip6;
+ NMIP6Config *ip6_config;
+ struct in6_addr *ip6_internal_gw;
struct in6_addr *ip6_external_gw;
char *ip_iface;
int ip_ifindex;
char *banner;
+ guint32 mtu;
struct rtnl_route *gw_route;
} NMVPNConnectionPrivate;
@@ -180,7 +185,7 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
priv->parent_dev,
ip_iface,
priv->ip4_config,
- NULL);
+ priv->ip6_config);
break;
case NM_VPN_CONNECTION_STATE_FAILED:
case NM_VPN_CONNECTION_STATE_DISCONNECTED:
@@ -421,7 +426,7 @@ ip_address_to_string (guint32 numeric)
}
static const char *
-ip6_address_to_string (struct in6_addr *addr)
+ip6_address_to_string (const struct in6_addr *addr)
{
memset (addr_to_string_buf, '\0', sizeof (addr_to_string_buf));
if (inet_ntop (AF_INET6, addr, addr_to_string_buf, INET6_ADDRSTRLEN)) {
@@ -437,6 +442,7 @@ print_vpn_config (NMVPNConnection *connection)
{
NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
NMIP4Address *addr;
+ NMIP6Address *addr6;
char *dns_domain = NULL;
guint32 num, i;
@@ -448,41 +454,87 @@ print_vpn_config (NMVPNConnection *connection)
ip6_address_to_string (priv->ip6_external_gw));
}
- addr = nm_ip4_config_get_address (priv->ip4_config, 0);
-
- if (priv->ip4_internal_gw)
- nm_log_info (LOGD_VPN, "Internal Gateway: %s", ip_address_to_string (priv->ip4_internal_gw));
nm_log_info (LOGD_VPN, "Tunnel Device: %s", priv->ip_iface);
- nm_log_info (LOGD_VPN, "Internal IP4 Address: %s", ip_address_to_string (nm_ip4_address_get_address (addr)));
- nm_log_info (LOGD_VPN, "Internal IP4 Prefix: %d", nm_ip4_address_get_prefix (addr));
- nm_log_info (LOGD_VPN, "Internal IP4 Point-to-Point Address: %s",
- ip_address_to_string (nm_ip4_config_get_ptp_address (priv->ip4_config)));
- nm_log_info (LOGD_VPN, "Maximum Segment Size (MSS): %d", nm_ip4_config_get_mss (priv->ip4_config));
- num = nm_ip4_config_get_num_routes (priv->ip4_config);
- for (i = 0; i < num; i++) {
- NMIP4Route *route;
+ if (priv->ip4_config) {
+ nm_log_info (LOGD_VPN, "IPv4 configuration:");
- route = nm_ip4_config_get_route (priv->ip4_config, i);
- nm_log_info (LOGD_VPN, "Static Route: %s/%d Next Hop: %s",
- ip_address_to_string (nm_ip4_route_get_dest (route)),
- nm_ip4_route_get_prefix (route),
- ip_address_to_string (nm_ip4_route_get_next_hop (route)));
- }
+ addr = nm_ip4_config_get_address (priv->ip4_config, 0);
- nm_log_info (LOGD_VPN, "Forbid Default Route: %s",
- nm_ip4_config_get_never_default (priv->ip4_config) ? "yes" : "no");
+ if (priv->ip4_internal_gw)
+ nm_log_info (LOGD_VPN, " Internal Gateway: %s", ip_address_to_string (priv->ip4_internal_gw));
+ nm_log_info (LOGD_VPN, " Internal Address: %s", ip_address_to_string (nm_ip4_address_get_address (addr)));
+ nm_log_info (LOGD_VPN, " Internal Prefix: %d", nm_ip4_address_get_prefix (addr));
+ nm_log_info (LOGD_VPN, " Internal Point-to-Point Address: %s",
+ ip_address_to_string (nm_ip4_config_get_ptp_address (priv->ip4_config)));
+ nm_log_info (LOGD_VPN, " Maximum Segment Size (MSS): %d", nm_ip4_config_get_mss (priv->ip4_config));
- num = nm_ip4_config_get_num_nameservers (priv->ip4_config);
- for (i = 0; i < num; i++) {
- nm_log_info (LOGD_VPN, "Internal IP4 DNS: %s",
- ip_address_to_string (nm_ip4_config_get_nameserver (priv->ip4_config, i)));
- }
+ num = nm_ip4_config_get_num_routes (priv->ip4_config);
+ for (i = 0; i < num; i++) {
+ NMIP4Route *route;
- if (nm_ip4_config_get_num_domains (priv->ip4_config) > 0)
- dns_domain = (char *) nm_ip4_config_get_domain (priv->ip4_config, 0);
+ route = nm_ip4_config_get_route (priv->ip4_config, i);
+ nm_log_info (LOGD_VPN, " Static Route: %s/%d Next Hop: %s",
+ ip_address_to_string (nm_ip4_route_get_dest (route)),
+ nm_ip4_route_get_prefix (route),
+ ip_address_to_string (nm_ip4_route_get_next_hop (route)));
+ }
- nm_log_info (LOGD_VPN, "DNS Domain: '%s'", dns_domain ? dns_domain : "(none)");
+ nm_log_info (LOGD_VPN, " Forbid Default Route: %s",
+ nm_ip4_config_get_never_default (priv->ip4_config) ? "yes" : "no");
+
+ num = nm_ip4_config_get_num_nameservers (priv->ip4_config);
+ for (i = 0; i < num; i++) {
+ nm_log_info (LOGD_VPN, " Internal DNS: %s",
+ ip_address_to_string (nm_ip4_config_get_nameserver (priv->ip4_config, i)));
+ }
+
+ if (nm_ip4_config_get_num_domains (priv->ip4_config) > 0)
+ dns_domain = (char *) nm_ip4_config_get_domain (priv->ip4_config, 0);
+
+ nm_log_info (LOGD_VPN, " DNS Domain: '%s'", dns_domain ? dns_domain : "(none)");
+ } else
+ nm_log_info (LOGD_VPN, "No IPv4 configuration");
+
+ if (priv->ip6_config) {
+ nm_log_info (LOGD_VPN, "IPv6 configuration:");
+
+ addr6 = nm_ip6_config_get_address (priv->ip6_config, 0);
+
+ if (priv->ip6_internal_gw)
+ nm_log_info (LOGD_VPN, " Internal Gateway: %s", ip6_address_to_string (priv->ip6_internal_gw));
+ nm_log_info (LOGD_VPN, " Internal Address: %s", ip6_address_to_string (nm_ip6_address_get_address (addr6)));
+ nm_log_info (LOGD_VPN, " Internal Prefix: %d", nm_ip6_address_get_prefix (addr6));
+ nm_log_info (LOGD_VPN, " Internal Point-to-Point Address: %s",
+ ip6_address_to_string (nm_ip6_config_get_ptp_address (priv->ip6_config)));
+ nm_log_info (LOGD_VPN, " Maximum Segment Size (MSS): %d", nm_ip6_config_get_mss (priv->ip6_config));
+
+ num = nm_ip6_config_get_num_routes (priv->ip6_config);
+ for (i = 0; i < num; i++) {
+ NMIP6Route *route;
+
+ route = nm_ip6_config_get_route (priv->ip6_config, i);
+ nm_log_info (LOGD_VPN, " Static Route: %s/%d Next Hop: %s",
+ ip6_address_to_string (nm_ip6_route_get_dest (route)),
+ nm_ip6_route_get_prefix (route),
+ ip6_address_to_string (nm_ip6_route_get_next_hop (route)));
+ }
+
+ nm_log_info (LOGD_VPN, " Forbid Default Route: %s",
+ nm_ip6_config_get_never_default (priv->ip6_config) ? "yes" : "no");
+
+ num = nm_ip6_config_get_num_nameservers (priv->ip6_config);
+ for (i = 0; i < num; i++) {
+ nm_log_info (LOGD_VPN, " Internal DNS: %s",
+ ip6_address_to_string (nm_ip6_config_get_nameserver (priv->ip6_config, i)));
+ }
+
+ if (nm_ip6_config_get_num_domains (priv->ip6_config) > 0)
+ dns_domain = (char *) nm_ip6_config_get_domain (priv->ip6_config, 0);
+
+ nm_log_info (LOGD_VPN, " DNS Domain: '%s'", dns_domain ? dns_domain : "(none)");
+ } else
+ nm_log_info (LOGD_VPN, "No IPv6 configuration");
if (priv->banner && strlen (priv->banner)) {
nm_log_info (LOGD_VPN, "Login Banner:");
@@ -492,6 +544,186 @@ print_vpn_config (NMVPNConnection *connection)
}
}
+static gboolean
+nm_vpn_connection_apply_config (NMVPNConnection *connection)
+{
+ NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+ NMDnsManager *dns_mgr;
+
+ nm_system_iface_set_up (priv->ip_ifindex, TRUE, NULL);
+
+ if (priv->ip4_config) {
+ if (!nm_system_apply_ip4_config (priv->ip_ifindex, priv->ip4_config,
+ 0, NM_IP4_COMPARE_FLAG_ALL))
+ return FALSE;
+ }
+
+ if (priv->ip6_config) {
+ if (!nm_system_apply_ip6_config (priv->ip_ifindex, priv->ip6_config,
+ 0, NM_IP6_COMPARE_FLAG_ALL))
+ /* FIXME: remove ip4 config */
+ return FALSE;
+ }
+
+ /* Add any explicit route to the VPN gateway through the parent device */
+ if (priv->ip4_external_gw) {
+ priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev,
+ priv->ip4_external_gw);
+ } else if (priv->ip6_external_gw) {
+ priv->gw_route = nm_system_add_ip6_vpn_gateway_route (priv->parent_dev,
+ priv->ip6_external_gw);
+ } else {
+ priv->gw_route = NULL;
+ }
+
+ /* Add the VPN to DNS */
+ dns_mgr = nm_dns_manager_get (NULL);
+ if (priv->ip4_config) {
+ nm_dns_manager_add_ip4_config (dns_mgr, priv->ip_iface, priv->ip4_config,
+ NM_DNS_IP_CONFIG_TYPE_VPN);
+ }
+ if (priv->ip6_config) {
+ nm_dns_manager_add_ip6_config (dns_mgr, priv->ip_iface, priv->ip6_config,
+ NM_DNS_IP_CONFIG_TYPE_VPN);
+ }
+ g_object_unref (dns_mgr);
+
+ nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.",
+ nm_vpn_connection_get_name (connection));
+ nm_vpn_connection_set_vpn_state (connection,
+ NM_VPN_CONNECTION_STATE_ACTIVATED,
+ NM_VPN_CONNECTION_STATE_REASON_NONE);
+ return TRUE;
+}
+
+static void
+nm_vpn_connection_config_maybe_complete (NMVPNConnection *connection,
+ gboolean success)
+{
+ NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+
+ if (priv->ipconfig_timeout == 0) {
+ /* config_complete() was already called with an error;
+ * ignore further calls.
+ */
+ return;
+ }
+
+ if (success) {
+ if ( (priv->has_ip4 && !priv->ip4_config)
+ || (priv->has_ip6 && !priv->ip6_config)) {
+ /* Need to wait for other config */
+ return;
+ }
+ }
+
+ g_source_remove (priv->ipconfig_timeout);
+ priv->ipconfig_timeout = 0;
+
+ if (success) {
+ print_vpn_config (connection);
+
+ if (nm_vpn_connection_apply_config (connection))
+ return;
+ }
+
+ g_clear_object (&priv->ip4_config);
+ g_clear_object (&priv->ip6_config);
+
+ nm_log_warn (LOGD_VPN, "VPN connection '%s' did not receive valid IP config information.",
+ nm_vpn_connection_get_name (connection));
+ nm_vpn_connection_set_vpn_state (connection,
+ NM_VPN_CONNECTION_STATE_FAILED,
+ NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID);
+}
+
+static gboolean
+process_generic_config (NMVPNConnection *connection,
+ GHashTable *config_hash)
+{
+ NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+ GValue *val;
+
+ 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;
+ }
+
+ /* Grab the interface index for address/routing operations */
+ priv->ip_ifindex = nm_netlink_iface_to_index (priv->ip_iface);
+ if (priv->ip_ifindex < 0) {
+ 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);
+ if (val) {
+ g_free (priv->banner);
+ priv->banner = g_strdup (g_value_get_string (val));
+ }
+
+ /* External world-visible address of the VPN server */
+ priv->ip4_external_gw = 0;
+ priv->ip6_external_gw = NULL;
+ val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY);
+ if (val) {
+ if (G_VALUE_HOLDS (val, G_TYPE_UINT)) {
+ priv->ip4_external_gw = g_value_get_uint (val);
+ } else if (G_VALUE_HOLDS (val, DBUS_TYPE_G_UCHAR_ARRAY)) {
+ GByteArray *ba = g_value_get_boxed (val);
+
+ if (ba->len == sizeof (struct in6_addr))
+ priv->ip6_external_gw = g_memdup (ba->data, ba->len);
+ } else {
+ nm_log_err (LOGD_VPN, "(%s): VPN gateway is neither IPv4 nor IPv6", priv->ip_iface);
+ nm_vpn_connection_config_maybe_complete (connection, FALSE);
+ return FALSE;
+ }
+ }
+
+ /* MTU; this is a per-connection value, though NM's API treats it
+ * like it's IP4-specific. So we store it for now and retrieve it
+ * later in ip4_config_get.
+ */
+ val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_MTU);
+ if (val)
+ priv->mtu = g_value_get_uint (val);
+ else
+ priv->mtu = 0;
+
+ return TRUE;
+}
+
+static void
+nm_vpn_connection_config_get (DBusGProxy *proxy,
+ GHashTable *config_hash,
+ gpointer user_data)
+{
+ NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
+ NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+ GValue *val;
+
+ nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) reply received.",
+ nm_vpn_connection_get_name (connection));
+
+ if (!process_generic_config (connection, config_hash))
+ return;
+
+ /* Note whether to expect IPv4 and IPv6 configs */
+ val = g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_HAS_IP4);
+ priv->has_ip4 = val ? g_value_get_boolean (val) : FALSE;
+ g_clear_object (&priv->ip4_config);
+
+ val = g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_HAS_IP6);
+ priv->has_ip6 = val ? g_value_get_boolean (val) : FALSE;
+ g_clear_object (&priv->ip6_config);
+}
+
static void
nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
GHashTable *config_hash,
@@ -505,47 +737,25 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
GValue *val;
int i;
- nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) reply received.",
- nm_vpn_connection_get_name (connection));
+ if (priv->has_ip4) {
+ nm_log_info (LOGD_VPN, "VPN connection '%s' (IP4 Config Get) reply received.",
+ nm_vpn_connection_get_name (connection));
+ } else {
+ nm_log_info (LOGD_VPN, "VPN connection '%s' (IP4 Config Get) reply received from old-style plugin.",
+ nm_vpn_connection_get_name (connection));
- g_source_remove (priv->ipconfig_timeout);
- priv->ipconfig_timeout = 0;
+ /* In the old API, the generic and IPv4 configuration items
+ * were mixed together.
+ */
+ if (!process_generic_config (connection, config_hash))
+ return;
+
+ priv->has_ip4 = TRUE;
+ priv->has_ip6 = FALSE;
+ }
config = nm_ip4_config_new ();
- val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_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!");
- goto error;
- }
-
- /* Grab the interface index for address/routing operations */
- priv->ip_ifindex = nm_netlink_iface_to_index (priv->ip_iface);
- if (priv->ip_ifindex < 0) {
- nm_log_err (LOGD_VPN, "(%s): failed to look up VPN interface index", priv->ip_iface);
- goto error;
- }
-
- /* External world-visible address of the VPN server */
- priv->ip4_external_gw = 0;
- priv->ip6_external_gw = NULL;
- val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY);
- if (val) {
- if (G_VALUE_HOLDS (val, G_TYPE_UINT)) {
- priv->ip4_external_gw = g_value_get_uint (val);
- } else if (G_VALUE_HOLDS (val, DBUS_TYPE_G_UCHAR_ARRAY)) {
- GByteArray *ba = g_value_get_boxed (val);
-
- if (ba->len == sizeof (struct in6_addr))
- priv->ip6_external_gw = g_memdup (ba->data, ba->len);
- } else {
- nm_log_err (LOGD_VPN, "(%s): VPN gateway is neither IPv4 nor IPv6", priv->ip_iface);
- goto error;
- }
- }
-
addr = nm_ip4_address_new ();
nm_ip4_address_set_prefix (addr, 24); /* default to class C */
if (priv->ip4_external_gw)
@@ -573,7 +783,9 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
} else {
nm_log_err (LOGD_VPN, "invalid IP4 config received!");
nm_ip4_address_unref (addr);
- goto error;
+ g_object_unref (config);
+ nm_vpn_connection_config_maybe_complete (connection, FALSE);
+ return;
}
val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_DNS);
@@ -596,9 +808,8 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
if (val)
nm_ip4_config_set_mss (config, g_value_get_uint (val));
- val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_MTU);
- if (val)
- nm_ip4_config_set_mtu (config, g_value_get_uint (val));
+ if (priv->mtu)
+ nm_ip4_config_set_mtu (config, priv->mtu);
val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN);
if (val)
@@ -613,12 +824,6 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
nm_ip4_config_add_domain (config, *domain);
}
- val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_BANNER);
- if (val) {
- g_free (priv->banner);
- priv->banner = g_strdup (g_value_get_string (val));
- }
-
val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES);
if (val) {
GSList *routes;
@@ -635,8 +840,10 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
*/
if ( priv->ip4_external_gw
&& nm_ip4_route_get_dest (route) == priv->ip4_external_gw
- && nm_ip4_route_get_prefix (route) == 32)
+ && nm_ip4_route_get_prefix (route) == 32) {
+ nm_ip4_route_unref (route);
continue;
+ }
/* Otherwise accept the VPN-provided route */
nm_ip4_config_take_route (config, route);
@@ -649,50 +856,143 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
if (val && G_VALUE_HOLDS_BOOLEAN (val))
nm_ip4_config_set_never_default (config, g_value_get_boolean (val));
- priv->ip4_config = config;
- print_vpn_config (connection);
-
/* Merge in user overrides from the NMConnection's IPv4 setting */
s_ip4 = nm_connection_get_setting_ip4_config (priv->connection);
nm_utils_merge_ip4_config (config, s_ip4);
- nm_system_iface_set_up (priv->ip_ifindex, TRUE, NULL);
+ priv->ip4_config = config;
+ nm_vpn_connection_config_maybe_complete (connection, TRUE);
+}
- if (nm_system_apply_ip4_config (priv->ip_ifindex, config, 0, NM_IP4_COMPARE_FLAG_ALL)) {
- NMDnsManager *dns_mgr;
+static void
+nm_vpn_connection_ip6_config_get (DBusGProxy *proxy,
+ GHashTable *config_hash,
+ gpointer user_data)
+{
+ NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
+ NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+ NMSettingIP6Config *s_ip6;
+ NMIP6Address *addr;
+ NMIP6Config *config;
+ GValue *val;
+ int i;
- /* Add any explicit route to the VPN gateway through the parent device */
- if (priv->ip4_external_gw) {
- priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev,
- priv->ip4_external_gw);
- } else if (priv->ip6_external_gw) {
- priv->gw_route = nm_system_add_ip6_vpn_gateway_route (priv->parent_dev,
- priv->ip6_external_gw);
- } else
- priv->gw_route = NULL;
+ nm_log_info (LOGD_VPN, "VPN connection '%s' (IP6 Config Get) reply received.",
+ nm_vpn_connection_get_name (connection));
- /* Add the VPN to DNS */
- dns_mgr = nm_dns_manager_get (NULL);
- nm_dns_manager_add_ip4_config (dns_mgr, priv->ip_iface, config, NM_DNS_IP_CONFIG_TYPE_VPN);
- g_object_unref (dns_mgr);
+ config = nm_ip6_config_new ();
- nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.",
- nm_vpn_connection_get_name (connection));
- nm_vpn_connection_set_vpn_state (connection,
- NM_VPN_CONNECTION_STATE_ACTIVATED,
- NM_VPN_CONNECTION_STATE_REASON_NONE);
- return;
+ addr = nm_ip6_address_new ();
+ nm_ip6_address_set_prefix (addr, 128); /* default to class C */
+ if (priv->ip6_external_gw)
+ nm_ip6_address_set_gateway (addr, priv->ip6_external_gw);
+
+ /* Internal address of the VPN subnet's gateway */
+ val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_INT_GATEWAY);
+ if (val) {
+ GByteArray *ba = g_value_get_boxed (val);
+
+ if (ba->len == sizeof (struct in6_addr))
+ priv->ip6_internal_gw = g_memdup (ba->data, ba->len);
}
-error:
- g_clear_object (&priv->ip4_config);
+ val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS);
+ if (val) {
+ GByteArray *ba = g_value_get_boxed (val);
- nm_log_warn (LOGD_VPN, "VPN connection '%s' did not receive valid IP config information.",
- nm_vpn_connection_get_name (connection));
- nm_vpn_connection_set_vpn_state (connection,
- NM_VPN_CONNECTION_STATE_FAILED,
- NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID);
- g_object_unref (config);
+ if (ba->len == sizeof (struct in6_addr))
+ nm_ip6_address_set_address (addr, (struct in6_addr *)ba->data);
+ }
+
+ val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_PTP);
+ if (val) {
+ GByteArray *ba = g_value_get_boxed (val);
+
+ if (ba->len == sizeof (struct in6_addr))
+ nm_ip6_config_set_ptp_address (config, (struct in6_addr *)ba->data);
+ }
+
+ val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_PREFIX);
+ if (val)
+ nm_ip6_address_set_prefix (addr, g_value_get_uint (val));
+
+ if (nm_ip6_address_get_address (addr) && nm_ip6_address_get_prefix (addr)) {
+ nm_ip6_config_take_address (config, addr);
+ } else {
+ nm_log_err (LOGD_VPN, "invalid IP6 config received!");
+ nm_ip6_address_unref (addr);
+ g_object_unref (config);
+ nm_vpn_connection_config_maybe_complete (connection, FALSE);
+ }
+
+ val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_DNS);
+ if (val) {
+ GPtrArray *dns = (GPtrArray *) g_value_get_boxed (val);
+ GByteArray *ba;
+
+ for (i = 0; i < dns->len; i++) {
+ ba = dns->pdata[i];
+ if (ba->len == sizeof (struct in6_addr))
+ nm_ip6_config_add_nameserver (config, (struct in6_addr *)ba->data);
+ }
+ }
+
+ val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_MSS);
+ if (val)
+ nm_ip6_config_set_mss (config, g_value_get_uint (val));
+
+ val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_DOMAIN);
+ if (val)
+ nm_ip6_config_add_domain (config, g_value_get_string (val));
+
+ val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_DOMAINS);
+ if (val) {
+ const char **domains = g_value_get_boxed (val);
+ const char **domain;
+
+ for (domain = domains; domain && *domain; domain++)
+ nm_ip6_config_add_domain (config, *domain);
+ }
+
+ val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_ROUTES);
+ if (val) {
+ GSList *routes;
+ GSList *iter;
+
+ routes = nm_utils_ip6_routes_from_gvalue (val);
+ for (iter = routes; iter; iter = iter->next) {
+ NMIP6Route *route = iter->data;
+
+ /* Ignore host routes to the VPN gateway since NM adds one itself
+ * below. Since NM knows more about the routing situation than
+ * the VPN server, we want to use the NM created route instead of
+ * whatever the server provides.
+ */
+ if ( priv->ip6_external_gw
+ && nm_ip6_route_get_prefix (route) == 128
+ && memcmp (nm_ip6_route_get_dest (route), priv->ip6_external_gw,
+ sizeof (struct in6_addr)) == 0) {
+ nm_ip6_route_unref (route);
+ continue;
+ }
+
+ /* Otherwise accept the VPN-provided route */
+ nm_ip6_config_take_route (config, route);
+ }
+
+ g_slist_free (routes);
+ }
+
+ val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_NEVER_DEFAULT);
+ if (val && G_VALUE_HOLDS_BOOLEAN (val))
+ nm_ip6_config_set_never_default (config, g_value_get_boolean (val));
+
+ /* Merge in user overrides from the NMConnection's IPv6 setting */
+ s_ip6 = nm_connection_get_setting_ip6_config (priv->connection);
+ nm_utils_merge_ip6_config (config, s_ip6);
+
+ priv->ip6_config = config;
+ nm_vpn_connection_config_maybe_complete (connection, TRUE);
}
static gboolean
@@ -781,9 +1081,18 @@ really_activate (NMVPNConnection *connection, const char *username)
priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
- /* Ip4Config signal */
dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED,
G_TYPE_NONE, G_TYPE_VALUE, G_TYPE_INVALID);
+
+ /* Config signal */
+ dbus_g_proxy_add_signal (priv->proxy, "Config",
+ DBUS_TYPE_G_MAP_OF_VARIANT,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->proxy, "Config",
+ G_CALLBACK (nm_vpn_connection_config_get),
+ connection, NULL);
+
+ /* Ip4Config signal */
dbus_g_proxy_add_signal (priv->proxy, "Ip4Config",
DBUS_TYPE_G_MAP_OF_VARIANT,
G_TYPE_INVALID);
@@ -791,6 +1100,14 @@ really_activate (NMVPNConnection *connection, const char *username)
G_CALLBACK (nm_vpn_connection_ip4_config_get),
connection, NULL);
+ /* Ip6Config signal */
+ dbus_g_proxy_add_signal (priv->proxy, "Ip6Config",
+ DBUS_TYPE_G_MAP_OF_VARIANT,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->proxy, "Ip6Config",
+ G_CALLBACK (nm_vpn_connection_ip6_config_get),
+ connection, NULL);
+
hash = _hash_with_username (priv->connection, username);
org_freedesktop_NetworkManager_VPN_Plugin_connect_async (priv->proxy,
hash,
@@ -883,6 +1200,14 @@ nm_vpn_connection_get_ip4_config (NMVPNConnection *connection)
return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip4_config;
}
+NMIP6Config *
+nm_vpn_connection_get_ip6_config (NMVPNConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+
+ return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip6_config;
+}
+
const char *
nm_vpn_connection_get_ip_iface (NMVPNConnection *connection)
{
@@ -915,6 +1240,14 @@ nm_vpn_connection_get_ip4_internal_gateway (NMVPNConnection *connection)
return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip4_internal_gw;
}
+struct in6_addr *
+nm_vpn_connection_get_ip6_internal_gateway (NMVPNConnection *connection)
+{
+ g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), 0);
+
+ return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip6_internal_gw;
+}
+
void
nm_vpn_connection_fail (NMVPNConnection *connection,
NMVPNConnectionStateReason reason)
@@ -1090,10 +1423,6 @@ vpn_cleanup (NMVPNConnection *connection)
nm_dns_manager_remove_ip4_config (dns_mgr, priv->ip_iface, priv->ip4_config);
g_object_unref (dns_mgr);
- /* Remove any previously added VPN gateway host route */
- if (priv->gw_route)
- nm_netlink_route_delete (priv->gw_route);
-
/* Reset routes and addresses of the currently active device */
parent_config = nm_device_get_ip4_config (priv->parent_dev);
if (parent_config) {
@@ -1106,7 +1435,29 @@ vpn_cleanup (NMVPNConnection *connection)
}
}
+ if (priv->ip6_config) {
+ NMIP6Config *parent_config;
+ NMDnsManager *dns_mgr;
+
+ /* Remove attributes of the VPN's IP6 Config */
+ dns_mgr = nm_dns_manager_get (NULL);
+ nm_dns_manager_remove_ip6_config (dns_mgr, priv->ip_iface, priv->ip6_config);
+ g_object_unref (dns_mgr);
+
+ /* Reset routes and addresses of the currently active device */
+ parent_config = nm_device_get_ip6_config (priv->parent_dev);
+ if (parent_config) {
+ if (!nm_system_apply_ip6_config (nm_device_get_ip_ifindex (priv->parent_dev),
+ nm_device_get_ip6_config (priv->parent_dev),
+ nm_device_get_priority (priv->parent_dev),
+ NM_IP6_COMPARE_FLAG_ADDRESSES | NM_IP6_COMPARE_FLAG_ROUTES)) {
+ nm_log_err (LOGD_VPN, "failed to re-apply VPN parent device addresses and routes.");
+ }
+ }
+ }
+
if (priv->gw_route) {
+ nm_netlink_route_delete (priv->gw_route);
rtnl_route_put (priv->gw_route);
priv->gw_route = NULL;
}
@@ -1192,6 +1543,8 @@ dispose (GObject *object)
if (priv->gw_route)
rtnl_route_put (priv->gw_route);
+ if (priv->ip6_internal_gw)
+ g_free (priv->ip6_internal_gw);
if (priv->ip6_external_gw)
g_free (priv->ip6_external_gw);
@@ -1207,6 +1560,8 @@ dispose (GObject *object)
if (priv->ip4_config)
g_object_unref (priv->ip4_config);
+ if (priv->ip6_config)
+ g_object_unref (priv->ip6_config);
if (priv->ipconfig_timeout)
g_source_remove (priv->ipconfig_timeout);
diff --git a/src/vpn-manager/nm-vpn-connection.h b/src/vpn-manager/nm-vpn-connection.h
index 93cc06d16..be1d20ee7 100644
--- a/src/vpn-manager/nm-vpn-connection.h
+++ b/src/vpn-manager/nm-vpn-connection.h
@@ -68,9 +68,11 @@ void nm_vpn_connection_fail (NMVPNConnection *connect
void nm_vpn_connection_disconnect (NMVPNConnection *connection,
NMVPNConnectionStateReason reason);
NMIP4Config * nm_vpn_connection_get_ip4_config (NMVPNConnection *connection);
+NMIP6Config * nm_vpn_connection_get_ip6_config (NMVPNConnection *connection);
const char * nm_vpn_connection_get_ip_iface (NMVPNConnection *connection);
int nm_vpn_connection_get_ip_ifindex (NMVPNConnection *connection);
NMDevice * nm_vpn_connection_get_parent_device (NMVPNConnection *connection);
guint32 nm_vpn_connection_get_ip4_internal_gateway (NMVPNConnection *connection);
+struct in6_addr * nm_vpn_connection_get_ip6_internal_gateway (NMVPNConnection *connection);
#endif /* NM_VPN_CONNECTION_H */