about summary refs log tree commit diff
path: root/src/vpn-manager/nm-vpn-connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vpn-manager/nm-vpn-connection.c')
-rw-r--r--src/vpn-manager/nm-vpn-connection.c851
1 files changed, 613 insertions, 238 deletions
diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c
index b391f1ec..5f64153b 100644
--- a/src/vpn-manager/nm-vpn-connection.c
+++ b/src/vpn-manager/nm-vpn-connection.c
@@ -43,7 +43,6 @@
 #include "nm-properties-changed-signal.h"
 #include "nm-dbus-glib-types.h"
 #include "NetworkManagerUtils.h"
-#include "nm-dns-manager.h"
 #include "nm-netlink-monitor.h"
 #include "nm-netlink-utils.h"
 #include "nm-glib-compat.h"
@@ -78,16 +77,24 @@ typedef struct {
 	NMDevice *parent_dev;
 	gulong device_monitor;
 	gulong device_ip4;
+	gulong device_ip6;
 
 	NMVPNConnectionState vpn_state;
 	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;
@@ -133,13 +140,61 @@ ac_state_from_vpn_state (NMVPNConnectionState vpn_state)
 }
 
 static void
+call_plugin_disconnect (NMVPNConnection *self)
+{
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+	GError *error = NULL;
+
+	if (priv->proxy) {
+		org_freedesktop_NetworkManager_VPN_Plugin_disconnect (priv->proxy, &error);
+		if (error)
+			nm_log_warn (LOGD_VPN, "error disconnecting VPN: %s", error->message);
+		g_clear_error (&error);
+
+		g_object_unref (priv->proxy);
+		priv->proxy = NULL;
+	}
+}
+
+static void
+vpn_cleanup (NMVPNConnection *connection)
+{
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+
+	if (priv->ip_ifindex) {
+		nm_system_iface_set_up (priv->ip_ifindex, FALSE, NULL);
+		/* FIXME: use AF_UNSPEC here when we have IPv6 support */
+		nm_system_iface_flush_routes (priv->ip_ifindex, AF_INET);
+		nm_system_iface_flush_addresses (priv->ip_ifindex, AF_UNSPEC);
+	}
+
+	if (priv->gw_route) {
+		nm_netlink_route_delete (priv->gw_route);
+		rtnl_route_put (priv->gw_route);
+		priv->gw_route = NULL;
+	}
+
+	g_free (priv->banner);
+	priv->banner = NULL;
+
+	g_free (priv->ip_iface);
+	priv->ip_iface = NULL;
+	priv->ip_ifindex = 0;
+
+	/* Clear out connection secrets to ensure that the settings service
+	 * gets asked for them next time the connection is activated.
+	 */
+	if (priv->connection)
+		nm_connection_clear_secrets (priv->connection);
+}
+
+static void
 nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
                                  NMVPNConnectionState vpn_state,
                                  NMVPNConnectionStateReason reason)
 {
 	NMVPNConnectionPrivate *priv;
 	NMVPNConnectionState old_vpn_state;
-	char *ip_iface;
 
 	g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
 
@@ -155,10 +210,12 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
 	nm_active_connection_set_state (NM_ACTIVE_CONNECTION (connection),
 	                                ac_state_from_vpn_state (vpn_state));
 
-	/* Save ip_iface since when the VPN goes down it may get freed
-	 * before we're done with it.
-	 */
-	ip_iface = g_strdup (priv->ip_iface);
+	/* Clear any in-progress secrets request */
+	if (priv->secrets_id) {
+		nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (priv->connection), priv->secrets_id);
+		priv->secrets_id = 0;
+	}
+	priv->secrets_idx = SECRETS_REQ_SYSTEM;
 
 	/* The connection gets destroyed by the VPN manager when it enters the
 	 * disconnected/failed state, but we need to keep it around for a bit
@@ -166,35 +223,50 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
 	 */
 	g_object_ref (connection);
 
-	g_signal_emit (connection, signals[VPN_STATE_CHANGED], 0, vpn_state, reason);
+	g_signal_emit (connection, signals[VPN_STATE_CHANGED], 0, vpn_state, old_vpn_state, reason);
 	g_object_notify (G_OBJECT (connection), NM_VPN_CONNECTION_VPN_STATE);
 
-	/* Call dispatcher after the event gets processed internally */
 	switch (vpn_state) {
+	case NM_VPN_CONNECTION_STATE_NEED_AUTH:
+		/* Kick off the secrets requests; first we get existing system secrets
+		 * and ask the plugin if these are sufficient, next we get all existing
+		 * secrets from system and from user agents and ask the plugin again,
+		 * and last we ask the user for new secrets if required.
+		 */
+		get_secrets (connection, SECRETS_REQ_SYSTEM);
+		break;
 	case NM_VPN_CONNECTION_STATE_ACTIVATED:
+		/* Secrets no longer needed now that we're connected */
+		nm_connection_clear_secrets (priv->connection);
+
+		/* Let dispatcher scripts know we're up and running */
 		nm_utils_call_dispatcher ("vpn-up",
 		                          priv->connection,
 		                          priv->parent_dev,
-		                          ip_iface,
+		                          priv->ip_iface,
 		                          priv->ip4_config,
-		                          NULL);
+		                          priv->ip6_config);
 		break;
 	case NM_VPN_CONNECTION_STATE_FAILED:
 	case NM_VPN_CONNECTION_STATE_DISCONNECTED:
 		if (old_vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED) {
+			/* Let dispatcher scripts know we're about to go down */
 			nm_utils_call_dispatcher ("vpn-down",
 			                          priv->connection,
 			                          priv->parent_dev,
-			                          ip_iface,
+			                          priv->ip_iface,
 			                          NULL,
 			                          NULL);
 		}
+
+		/* Tear down and clean up the connection */
+		call_plugin_disconnect (connection);
+		vpn_cleanup (connection);
 		break;
 	default:
 		break;
 	}
 
-	g_free (ip_iface);
 	g_object_unref (connection);
 }
 
@@ -230,11 +302,34 @@ device_ip4_config_changed (NMDevice *device,
 	    || !nm_device_get_ip4_config (device))
 		return;
 
-	if (priv->gw_route)
-		rtnl_route_put (priv->gw_route);
+	/* Re-add the VPN gateway route */
+	if (priv->ip4_external_gw) {
+		if (priv->gw_route)
+			rtnl_route_put (priv->gw_route);
+		priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev,
+		                                                      priv->ip4_external_gw);
+	}
+}
+
+static void
+device_ip6_config_changed (NMDevice *device,
+                           GParamSpec *pspec,
+                           gpointer user_data)
+{
+	NMVPNConnection *vpn = NM_VPN_CONNECTION (user_data);
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (vpn);
+
+	if (   (priv->vpn_state != NM_VPN_CONNECTION_STATE_ACTIVATED)
+	    || !nm_device_get_ip6_config (device))
+		return;
 
 	/* Re-add the VPN gateway route */
-	priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev, priv->ip4_config);
+	if (priv->ip6_external_gw) {
+		if (priv->gw_route)
+			rtnl_route_put (priv->gw_route);
+		priv->gw_route = nm_system_add_ip6_vpn_gateway_route (priv->parent_dev,
+		                                                      priv->ip6_external_gw);
+	}
 }
 
 NMVPNConnection *
@@ -271,6 +366,9 @@ nm_vpn_connection_new (NMConnection *connection,
 	priv->device_ip4 = g_signal_connect (parent_device, "notify::" NM_DEVICE_IP4_CONFIG,
 	                                     G_CALLBACK (device_ip4_config_changed),
 	                                     self);
+	priv->device_ip6 = g_signal_connect (parent_device, "notify::" NM_DEVICE_IP6_CONFIG,
+	                                     G_CALLBACK (device_ip6_config_changed),
+	                                     self);
 
 	if (!nm_active_connection_export (NM_ACTIVE_CONNECTION (self),
 	                                  connection,
@@ -372,17 +470,18 @@ plugin_state_changed (DBusGProxy *proxy,
 	}
 }
 
+static char addr_to_string_buf[INET6_ADDRSTRLEN + 1];
+
 static const char *
 ip_address_to_string (guint32 numeric)
 {
 	struct in_addr temp_addr;
-	static char buf[INET_ADDRSTRLEN + 1];
 
-	memset (&buf, '\0', sizeof (buf));
+	memset (&addr_to_string_buf, '\0', sizeof (addr_to_string_buf));
 	temp_addr.s_addr = numeric;
 
-	if (inet_ntop (AF_INET, &temp_addr, buf, INET_ADDRSTRLEN)) {
-		return buf;
+	if (inet_ntop (AF_INET, &temp_addr, addr_to_string_buf, INET_ADDRSTRLEN)) {
+		return addr_to_string_buf;
 	} else {
 		nm_log_warn (LOGD_VPN, "error converting IP4 address 0x%X",
 		             ntohl (temp_addr.s_addr));
@@ -390,115 +489,340 @@ ip_address_to_string (guint32 numeric)
 	}
 }
 
+static const char *
+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)) {
+		return addr_to_string_buf;
+	} else {
+		nm_log_warn (LOGD_VPN, "error converting IP6 address");
+		return NULL;
+	}
+}
+
 static void
-print_vpn_config (NMIP4Config *config,
-                  guint32 internal_gw,
-                  const char *ip_iface,
-                  const char *banner)
+print_vpn_config (NMVPNConnection *connection)
 {
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
 	NMIP4Address *addr;
+	NMIP6Address *addr6;
 	char *dns_domain = NULL;
 	guint32 num, i;
 
-	g_return_if_fail (config != NULL);
-
-	addr = nm_ip4_config_get_address (config, 0);
-
-	nm_log_info (LOGD_VPN, "VPN Gateway: %s", ip_address_to_string (nm_ip4_address_get_gateway (addr)));
-	if (internal_gw)
-		nm_log_info (LOGD_VPN, "Internal Gateway: %s", ip_address_to_string (internal_gw));
-	nm_log_info (LOGD_VPN, "Tunnel Device: %s", 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 (config)));
-	nm_log_info (LOGD_VPN, "Maximum Segment Size (MSS): %d", nm_ip4_config_get_mss (config));
-
-	num = nm_ip4_config_get_num_routes (config);
-	for (i = 0; i < num; i++) {
-		NMIP4Route *route;
-
-		route = nm_ip4_config_get_route (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)));
-	}
+	if (priv->ip4_external_gw) {
+		nm_log_info (LOGD_VPN, "VPN Gateway: %s",
+		             ip_address_to_string (priv->ip4_external_gw));
+	} else if (priv->ip6_external_gw) {
+		nm_log_info (LOGD_VPN, "VPN Gateway: %s",
+		             ip6_address_to_string (priv->ip6_external_gw));
+	} 
 
-	nm_log_info (LOGD_VPN, "Forbid Default Route: %s",
-	             nm_ip4_config_get_never_default (config) ? "yes" : "no");
+	nm_log_info (LOGD_VPN, "Tunnel Device: %s", priv->ip_iface);
 
-	num = nm_ip4_config_get_num_nameservers (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 (config, i)));
-	}
+	if (priv->ip4_config) {
+		nm_log_info (LOGD_VPN, "IPv4 configuration:");
+
+		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, "  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_routes (priv->ip4_config);
+		for (i = 0; i < num; i++) {
+			NMIP4Route *route;
+
+			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, "  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));
 
-	if (nm_ip4_config_get_num_domains (config) > 0)
-		dns_domain = (char *) nm_ip4_config_get_domain (config, 0);
+		num = nm_ip6_config_get_num_routes (priv->ip6_config);
+		for (i = 0; i < num; i++) {
+			NMIP6Route *route;
 
-	nm_log_info (LOGD_VPN, "DNS Domain: '%s'", dns_domain ? dns_domain : "(none)");
+			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 (banner && strlen (banner)) {
+		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:");
 		nm_log_info (LOGD_VPN, "-----------------------------------------");
-		nm_log_info (LOGD_VPN, "%s", banner);
+		nm_log_info (LOGD_VPN, "%s", priv->banner);
 		nm_log_info (LOGD_VPN, "-----------------------------------------");
 	}
 }
 
-static void
-nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
-                                  GHashTable *config_hash,
-                                  gpointer user_data)
+static gboolean
+nm_vpn_connection_apply_config (NMVPNConnection *connection)
 {
-	NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
-	NMSettingIP4Config *s_ip4;
-	NMIP4Address *addr;
-	NMIP4Config *config;
-	GValue *val;
-	int i;
-	guint32 vpn_ext_gw = 0;
 
-	nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) reply received.",
+	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;
+	}
+
+	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;
 
-	config = nm_ip4_config_new ();
+	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);
 
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV);
+	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!");
-		goto error;
+		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);
-		goto error;
+		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,
+                                  gpointer user_data)
+{
+	NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMSettingIP4Config *s_ip4;
+	NMIP4Address *addr;
+	NMIP4Config *config;
+	GValue *val;
+	int i;
+
+	if (priv->has_ip4) {
+		nm_log_info (LOGD_VPN, "VPN connection '%s' (IP4 Config Get) reply received.",
+		             nm_vpn_connection_get_name (connection));
+
+		if (g_hash_table_size (config_hash) == 0) {
+			priv->has_ip4 = FALSE;
+			nm_vpn_connection_config_maybe_complete (connection, TRUE);
+			return;
+		}
+	} else {
+		nm_log_info (LOGD_VPN, "VPN connection '%s' (IP4 Config Get) reply received from old-style plugin.",
+		             nm_vpn_connection_get_name (connection));
+
+		/* 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 ();
+
 	addr = nm_ip4_address_new ();
 	nm_ip4_address_set_prefix (addr, 24); /* default to class C */
+	if (priv->ip4_external_gw)
+		nm_ip4_address_set_gateway (addr, priv->ip4_external_gw);
 
 	/* Internal address of the VPN subnet's gateway */
 	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY);
 	if (val)
 		priv->ip4_internal_gw = g_value_get_uint (val);
 
-	/* External world-visible address of the VPN server */
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY);
-	if (val) {
-		nm_ip4_address_set_gateway (addr, g_value_get_uint (val));
-		vpn_ext_gw = g_value_get_uint (val);
-	}
-
 	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS);
 	if (val)
 		nm_ip4_address_set_address (addr, g_value_get_uint (val));
@@ -516,7 +840,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);
@@ -539,9 +865,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)
@@ -556,12 +881,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;
@@ -576,10 +895,12 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
 			 * the VPN server, we want to use the NM created route instead of
 			 * whatever the server provides.
 			 */
-			if (   vpn_ext_gw
-			    && nm_ip4_route_get_dest (route) == vpn_ext_gw
-			    && nm_ip4_route_get_prefix (route) == 32)
+			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_unref (route);
 				continue;
+			}
 
 			/* Otherwise accept the VPN-provided route */
 			nm_ip4_config_take_route (config, route);
@@ -592,42 +913,149 @@ 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));
 
-	print_vpn_config (config, priv->ip4_internal_gw, priv->ip_iface, priv->banner);
-
 	/* 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 */
-		priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev, config);
+	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);
+	if (g_hash_table_size (config_hash) == 0) {
+		priv->has_ip6 = FALSE;
+		nm_vpn_connection_config_maybe_complete (connection, TRUE);
+		return;
+	}
 
-		priv->ip4_config = config;
+	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:
-	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);
+	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS);
+	if (val) {
+		GByteArray *ba = g_value_get_boxed (val);
+
+		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
@@ -716,9 +1144,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);
@@ -726,6 +1163,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,
@@ -818,6 +1263,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)
 {
@@ -850,6 +1303,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)
@@ -896,11 +1357,19 @@ plugin_need_secrets_cb  (DBusGProxy *proxy,
 
 	if (setting_name && strlen (setting_name)) {
 		/* More secrets required */
-		nm_log_dbg (LOGD_VPN, "(%s/%s) service indicated additional secrets required",
-		            nm_connection_get_uuid (priv->connection),
-		            nm_connection_get_id (priv->connection));
 
-		get_secrets (self, priv->secrets_idx + 1);
+		if (priv->secrets_idx == SECRETS_REQ_NEW) {
+			nm_log_err (LOGD_VPN, "(%s/%s) final secrets request failed to provide sufficient secrets",
+			            nm_connection_get_uuid (priv->connection),
+			            nm_connection_get_id (priv->connection));
+			nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
+		} else {
+			nm_log_dbg (LOGD_VPN, "(%s/%s) service indicated additional secrets required",
+			            nm_connection_get_uuid (priv->connection),
+			            nm_connection_get_id (priv->connection));
+
+			get_secrets (self, priv->secrets_idx + 1);
+		}
 		return;
 	}
 
@@ -985,6 +1454,9 @@ get_secrets (NMVPNConnection *self, SecretsReq secrets_idx)
 		g_assert_not_reached ();
 	}
 
+	if (priv->user_requested)
+		flags |= NM_SETTINGS_GET_SECRETS_FLAG_USER_REQUESTED;
+
 	priv->secrets_id = nm_settings_connection_get_secrets (NM_SETTINGS_CONNECTION (priv->connection),
 	                                                       filter_by_uid,
 	                                                       priv->user_uid,
@@ -1004,109 +1476,7 @@ get_secrets (NMVPNConnection *self, SecretsReq secrets_idx)
 	}
 }
 
-static void
-vpn_cleanup (NMVPNConnection *connection)
-{
-	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
-
-	if (priv->ip_ifindex) {
-		nm_system_iface_set_up (priv->ip_ifindex, FALSE, NULL);
-		/* FIXME: use AF_UNSPEC here when we have IPv6 support */
-		nm_system_iface_flush_routes (priv->ip_ifindex, AF_INET);
-		nm_system_iface_flush_addresses (priv->ip_ifindex, AF_UNSPEC);
-	}
-
-	if (priv->ip4_config) {
-		NMIP4Config *parent_config;
-		NMDnsManager *dns_mgr;
-
-		/* Remove attributes of the VPN's IP4 Config */
-		dns_mgr = nm_dns_manager_get (NULL);
-		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) {
-			if (!nm_system_apply_ip4_config (nm_device_get_ip_ifindex (priv->parent_dev),
-			                                 nm_device_get_ip4_config (priv->parent_dev),
-			                                 nm_device_get_priority (priv->parent_dev),
-			                                 NM_IP4_COMPARE_FLAG_ADDRESSES | NM_IP4_COMPARE_FLAG_ROUTES)) {
-				nm_log_err (LOGD_VPN, "failed to re-apply VPN parent device addresses and routes.");
-			}
-		}
-	}
-
-	if (priv->gw_route) {
-		rtnl_route_put (priv->gw_route);
-		priv->gw_route = NULL;
-	}
-
-	g_free (priv->banner);
-	priv->banner = NULL;
-
-	g_free (priv->ip_iface);
-	priv->ip_iface = NULL;
-	priv->ip_ifindex = 0;
-
-	/* Clear out connection secrets to ensure that the settings service
-	 * gets asked for them next time the connection is activated.
-	 */
-	if (priv->connection)
-		nm_connection_clear_secrets (priv->connection);
-}
-
-static void
-connection_state_changed (NMVPNConnection *self,
-                          NMVPNConnectionState state,
-                          NMVPNConnectionStateReason reason)
-{
-	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
-
-	/* Clear any in-progress secrets request */
-	if (priv->secrets_id) {
-		nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (priv->connection), priv->secrets_id);
-		priv->secrets_id = 0;
-	}
-	priv->secrets_idx = SECRETS_REQ_SYSTEM;
-
-	switch (state) {
-	case NM_VPN_CONNECTION_STATE_NEED_AUTH:
-		/* Kick off the secrets requests; first we get existing system secrets
-		 * and ask the plugin if these are sufficient, next we get all existing
-		 * secrets from system and from user agents and ask the plugin again,
-		 * and last we ask the user for new secrets if required.
-		 */
-		get_secrets (self, SECRETS_REQ_SYSTEM);
-		break;
-	case NM_VPN_CONNECTION_STATE_ACTIVATED:
-		/* Secrets no longer needed now that we're connected */
-		nm_connection_clear_secrets (priv->connection);
-		break;
-	case NM_VPN_CONNECTION_STATE_DISCONNECTED:
-	case NM_VPN_CONNECTION_STATE_FAILED:
-		if (priv->proxy) {
-			GError *err = NULL;
-
-			org_freedesktop_NetworkManager_VPN_Plugin_disconnect (priv->proxy, &err);
-			if (err) {
-				nm_log_warn (LOGD_VPN, "error disconnecting VPN: %s", err->message);
-				g_error_free (err);
-			}
-
-			g_object_unref (priv->proxy);
-			priv->proxy = NULL;
-		}
-		vpn_cleanup (self);
-		break;
-	default:
-		break;
-	}
-}
+/******************************************************************************/
 
 static void
 nm_vpn_connection_init (NMVPNConnection *self)
@@ -1127,9 +1497,15 @@ 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);
 
 	if (priv->device_ip4)
 		g_signal_handler_disconnect (priv->parent_dev, priv->device_ip4);
+	if (priv->device_ip6)
+		g_signal_handler_disconnect (priv->parent_dev, priv->device_ip6);
 
 	if (priv->device_monitor)
 		g_signal_handler_disconnect (priv->parent_dev, priv->device_monitor);
@@ -1138,6 +1514,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);
@@ -1197,7 +1575,6 @@ nm_vpn_connection_class_init (NMVPNConnectionClass *connection_class)
 	g_type_class_add_private (connection_class, sizeof (NMVPNConnectionPrivate));
 
 	/* virtual methods */
-	connection_class->vpn_state_changed = connection_state_changed;
 	object_class->get_property = get_property;
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
@@ -1223,16 +1600,14 @@ nm_vpn_connection_class_init (NMVPNConnectionClass *connection_class)
 
 	/* signals */
 	signals[VPN_STATE_CHANGED] =
-		g_signal_new ("vpn-state-changed",
-				    G_OBJECT_CLASS_TYPE (object_class),
-				    G_SIGNAL_RUN_FIRST,
-				    G_STRUCT_OFFSET (NMVPNConnectionClass, vpn_state_changed),
-				    NULL, NULL,
-				    _nm_marshal_VOID__UINT_UINT,
-				    G_TYPE_NONE, 2,
-				    G_TYPE_UINT, G_TYPE_UINT);
+		g_signal_new (NM_VPN_CONNECTION_VPN_STATE_CHANGED,
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              0, NULL, NULL,
+		              _nm_marshal_VOID__UINT_UINT_UINT,
+		              G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
 
 	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (object_class),
-									 &dbus_glib_nm_vpn_connection_object_info);
+	                                 &dbus_glib_nm_vpn_connection_object_info);
 }