summary refs log tree commit diff
path: root/src/vpn-manager
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2016-01-20 16:26:51 +0100
committerMichael Biebl <biebl@debian.org>2016-01-20 16:26:51 +0100
commit494f296a3baab08522617b24b1f126d8f9a17502 (patch)
treec8ef32fb0dd1c4ff35a0b38e787abb58692de0cd /src/vpn-manager
parent54f6333410ffd570e62717d9e77c5c987175e397 (diff)
Imported Upstream version 1.1.90 upstream/1.1.90
Diffstat (limited to 'src/vpn-manager')
-rw-r--r--src/vpn-manager/nm-vpn-connection.c1915
-rw-r--r--src/vpn-manager/nm-vpn-connection.h41
-rw-r--r--src/vpn-manager/nm-vpn-manager.c285
-rw-r--r--src/vpn-manager/nm-vpn-manager.h7
-rw-r--r--src/vpn-manager/nm-vpn-service.c385
-rw-r--r--src/vpn-manager/nm-vpn-service.h63
6 files changed, 1253 insertions, 1443 deletions
diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c
index 64e5e2ab..c548c9fa 100644
--- a/src/vpn-manager/nm-vpn-connection.c
+++ b/src/vpn-manager/nm-vpn-connection.c
@@ -21,7 +21,6 @@
 
 #include "config.h"
 
-#include <glib.h>
 #include <string.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -29,16 +28,13 @@
 #include <errno.h>
 #include <stdlib.h>
 
+#include "nm-default.h"
 #include "nm-vpn-connection.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
-#include "nm-dbus-manager.h"
 #include "nm-platform.h"
-#include "nm-logging.h"
 #include "nm-active-connection.h"
-#include "nm-dbus-glib-types.h"
 #include "NetworkManagerUtils.h"
-#include "nm-glib-compat.h"
 #include "settings/nm-settings-connection.h"
 #include "nm-dispatcher.h"
 #include "nm-agent-manager.h"
@@ -46,8 +42,11 @@
 #include "nm-default-route-manager.h"
 #include "nm-route-manager.h"
 #include "nm-firewall-manager.h"
+#include "nm-config.h"
+#include "nm-vpn-plugin-info.h"
+#include "nm-vpn-manager.h"
 
-#include "nm-vpn-connection-glue.h"
+#include "nmdbus-vpn-connection.h"
 
 G_DEFINE_TYPE (NMVpnConnection, nm_vpn_connection, NM_TYPE_ACTIVE_CONNECTION)
 
@@ -80,11 +79,10 @@ typedef enum {
 } VpnState;
 
 typedef struct {
-	NMConnection *connection;
 	gboolean service_can_persist;
 	gboolean connection_can_persist;
 
-	guint32 secrets_id;
+	NMSettingsConnectionCallId secrets_id;
 	SecretsReq secrets_idx;
 	char *username;
 
@@ -93,12 +91,19 @@ typedef struct {
 	NMVpnConnectionStateReason failure_reason;
 
 	NMVpnServiceState service_state;
+	guint start_timeout;
+	gboolean service_running;
+	NMVpnPluginInfo *plugin_info;
+	char *bus_name;
 
 	/* Firewall */
-	NMFirewallPendingCall fw_call;
+	NMFirewallManagerCallId fw_call;
 
-	DBusGProxy *proxy;
-	GHashTable *connect_hash;
+	NMDefaultRouteManager *default_route_manager;
+	NMRouteManager *route_manager;
+	GDBusProxy *proxy;
+	GCancellable *cancellable;
+	GVariant *connect_hash;
 	guint connect_timeout;
 	gboolean has_ip4;
 	NMIP4Config *ip4_config;
@@ -106,6 +111,13 @@ typedef struct {
 	guint32 ip4_external_gw;
 	gboolean has_ip6;
 	NMIP6Config *ip6_config;
+
+	/* These config instances are passed on to NMDevice and modified by NMDevice.
+	 * This pointer is only useful for nm_device_replace_vpn4_config() to clear the
+	 * previous configuration. Consider these instances to be owned by NMDevice. */
+	NMIP4Config *last_device_ip4_config;
+	NMIP6Config *last_device_ip6_config;
+
 	struct in6_addr *ip6_internal_gw;
 	struct in6_addr *ip6_external_gw;
 	char *ip_iface;
@@ -137,22 +149,92 @@ enum {
 	LAST_PROP
 };
 
+static NMSettingsConnection *_get_settings_connection (NMVpnConnection *self,
+                                                       gboolean allow_missing);
+
 static void get_secrets (NMVpnConnection *self,
                          SecretsReq secrets_idx,
                          const char **hints);
 
-static void plugin_interactive_secrets_required (DBusGProxy *proxy,
+static void plugin_interactive_secrets_required (NMVpnConnection *self,
                                                  const char *message,
-                                                 const char **secrets,
-                                                 gpointer user_data);
+                                                 const char **secrets);
 
-static void _set_vpn_state (NMVpnConnection *connection,
+static void _set_vpn_state (NMVpnConnection *self,
                             VpnState vpn_state,
                             NMVpnConnectionStateReason reason,
                             gboolean quitting);
 
 /*********************************************************************/
 
+#define _NMLOG_DOMAIN      LOGD_VPN
+#define _NMLOG_PREFIX_NAME "vpn-connection"
+
+#define __NMLOG_prefix_buf_len 128
+
+static const char *
+__LOG_create_prefix (char *buf, NMVpnConnection *self)
+{
+	NMVpnConnectionPrivate *priv;
+	NMConnection *con;
+	const char *id;
+
+	if (!self)
+		return _NMLOG_PREFIX_NAME;
+
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+	con = NM_CONNECTION (_get_settings_connection (self, TRUE));
+	id = con ? nm_connection_get_id (con) : NULL;
+
+	g_snprintf (buf, __NMLOG_prefix_buf_len,
+	            "%s["
+	            "%p"       /*self*/
+	            "%s%s"     /*con-uuid*/
+	            "%s%s%s%s" /*con-id*/
+	            ",%d"      /*ifindex*/
+	            "%s%s%s%s" /*iface*/
+	            "]",
+	            _NMLOG_PREFIX_NAME,
+	            self,
+	            con ? "," : "--", con ? str_if_set (nm_connection_get_uuid (con), "??") : "",
+	            con ? "," : "", NM_PRINT_FMT_QUOTED (id, "\"", id, "\"", con ? "??" : ""),
+	            priv->ip_ifindex,
+	            priv->ip_iface ? ":" : "", NM_PRINT_FMT_QUOTED (priv->ip_iface, "(", priv->ip_iface, ")", "")
+	            );
+
+	return buf;
+}
+
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+        const NMLogLevel __level = (level); \
+        \
+        if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
+            char __prefix[__NMLOG_prefix_buf_len]; \
+            \
+            _nm_log (__level, _NMLOG_DOMAIN, 0, \
+                     "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                     __LOG_create_prefix (__prefix, self) \
+                     _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+        } \
+    } G_STMT_END
+
+/*********************************************************************/
+
+static void
+cancel_get_secrets (NMVpnConnection *self)
+{
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+	if (priv->secrets_id) {
+		nm_settings_connection_cancel_secrets (_get_settings_connection (self, FALSE),
+		                                       priv->secrets_id);
+		g_warn_if_fail (!priv->secrets_id);
+		priv->secrets_id = NULL;
+	}
+}
+
 static NMVpnConnectionState
 _state_to_nm_vpn_state (VpnState state)
 {
@@ -212,57 +294,93 @@ _state_to_ac_state (VpnState vpn_state)
 	return NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
 }
 
+static NMSettingsConnection *
+_get_settings_connection (NMVpnConnection *self, gboolean allow_missing)
+{
+	NMSettingsConnection *con;
+
+	/* Currently we operate on the assumption, that the settings-connection
+	 * never changes after it is set (though initially, it might be unset).
+	 * Later we might want to change that, but then we need fixes here too. */
+
+	con = _nm_active_connection_get_settings_connection (NM_ACTIVE_CONNECTION (self));
+	if (!con && !allow_missing)
+		g_return_val_if_reached (NULL);
+	return con;
+}
+
+static NMConnection *
+_get_applied_connection (NMVpnConnection *connection)
+{
+	NMConnection *con;
+
+	con = nm_active_connection_get_applied_connection (NM_ACTIVE_CONNECTION (connection));
+	g_return_val_if_fail (con, NULL);
+	return con;
+}
+
 static void
 call_plugin_disconnect (NMVpnConnection *self)
 {
 	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
-	GError *error = NULL;
 
 	if (priv->proxy) {
-		if (!dbus_g_proxy_call (priv->proxy, "Disconnect", &error,
-		                        G_TYPE_INVALID,
-		                        G_TYPE_INVALID)) {
-			nm_log_warn (LOGD_VPN, "error disconnecting VPN: %s", error->message);
-			g_error_free (error);
-		}
-
-		g_object_unref (priv->proxy);
-		priv->proxy = NULL;
+		g_dbus_proxy_call (priv->proxy, "Disconnect", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
+		g_clear_object (&priv->proxy);
 	}
 }
 
 static void
-fw_call_cleanup (NMVpnConnection *connection)
+fw_call_cleanup (NMVpnConnection *self)
 {
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
 	if (priv->fw_call) {
-		nm_firewall_manager_cancel_call (nm_firewall_manager_get (), priv->fw_call);
+		nm_firewall_manager_cancel_call (priv->fw_call);
+		g_warn_if_fail (!priv->fw_call);
 		priv->fw_call = NULL;
 	}
 }
 
 static void
-vpn_cleanup (NMVpnConnection *connection, NMDevice *parent_dev)
+remove_parent_device_config (NMVpnConnection *connection, NMDevice *device)
 {
 	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
 
+	if (priv->last_device_ip4_config) {
+		nm_device_replace_vpn4_config (device, priv->last_device_ip4_config, NULL);
+		g_clear_object (&priv->last_device_ip4_config);
+	}
+
+	if (priv->last_device_ip6_config) {
+		nm_device_replace_vpn6_config (device, priv->last_device_ip6_config, NULL);
+		g_clear_object (&priv->last_device_ip6_config);
+	}
+}
+
+static void
+vpn_cleanup (NMVpnConnection *self, NMDevice *parent_dev)
+{
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
 	if (priv->ip_ifindex) {
 		nm_platform_link_set_down (NM_PLATFORM_GET, priv->ip_ifindex);
-		nm_route_manager_route_flush (nm_route_manager_get (), priv->ip_ifindex);
+		nm_route_manager_route_flush (priv->route_manager, priv->ip_ifindex);
 		nm_platform_address_flush (NM_PLATFORM_GET, priv->ip_ifindex);
 	}
 
-	nm_device_set_vpn4_config (parent_dev, NULL);
-	nm_device_set_vpn6_config (parent_dev, NULL);
+	remove_parent_device_config (self, parent_dev);
 
 	/* Remove zone from firewall */
-	if (priv->ip_iface)
+	if (priv->ip_iface) {
 		nm_firewall_manager_remove_from_zone (nm_firewall_manager_get (),
 		                                      priv->ip_iface,
+		                                      NULL,
+		                                      NULL,
 		                                      NULL);
+	}
 	/* Cancel pending firewall call */
-	fw_call_cleanup (connection);
+	fw_call_cleanup (self);
 
 	g_free (priv->banner);
 	priv->banner = NULL;
@@ -271,12 +389,13 @@ vpn_cleanup (NMVpnConnection *connection, NMDevice *parent_dev)
 	priv->ip_iface = NULL;
 	priv->ip_ifindex = 0;
 
+	g_free (priv->bus_name);
+	priv->bus_name = NULL;
+
 	/* 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);
-
+	nm_active_connection_clear_secrets (NM_ACTIVE_CONNECTION (self));
 }
 
 static void
@@ -311,7 +430,7 @@ dispatcher_cleanup (NMVpnConnection *self)
 }
 
 static void
-_set_vpn_state (NMVpnConnection *connection,
+_set_vpn_state (NMVpnConnection *self,
                 VpnState vpn_state,
                 NMVpnConnectionStateReason reason,
                 gboolean quitting)
@@ -319,11 +438,11 @@ _set_vpn_state (NMVpnConnection *connection,
 	NMVpnConnectionPrivate *priv;
 	VpnState old_vpn_state;
 	NMVpnConnectionState new_external_state, old_external_state;
-	NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (connection));
+	NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (self));
 
-	g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
+	g_return_if_fail (NM_IS_VPN_CONNECTION (self));
 
-	priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
 	if (vpn_state == priv->vpn_state)
 		return;
@@ -338,35 +457,32 @@ _set_vpn_state (NMVpnConnection *connection,
 		g_object_ref (parent_dev);
 
 	/* Update active connection base class state */
-	nm_active_connection_set_state (NM_ACTIVE_CONNECTION (connection),
+	nm_active_connection_set_state (NM_ACTIVE_CONNECTION (self),
 	                                _state_to_ac_state (vpn_state));
 
 	/* 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;
-	}
+	cancel_get_secrets (self);
 
-	dispatcher_cleanup (connection);
+	dispatcher_cleanup (self);
 
-	nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), connection);
-	nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), connection);
+	nm_default_route_manager_ip4_update_default_route (priv->default_route_manager, self);
+	nm_default_route_manager_ip6_update_default_route (priv->default_route_manager, self);
 
 	/* 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
 	 * to send out signals and handle the dispatcher.  So ref it.
 	 */
-	g_object_ref (connection);
+	g_object_ref (self);
 
 	old_external_state = _state_to_nm_vpn_state (old_vpn_state);
 	new_external_state = _state_to_nm_vpn_state (priv->vpn_state);
 	if (new_external_state != old_external_state) {
-		g_signal_emit (connection, signals[VPN_STATE_CHANGED], 0, new_external_state, reason);
-		g_signal_emit (connection, signals[INTERNAL_STATE_CHANGED], 0,
+		g_signal_emit (self, signals[VPN_STATE_CHANGED], 0, new_external_state, reason);
+		g_signal_emit (self, signals[INTERNAL_STATE_CHANGED], 0,
 		               new_external_state,
 		               old_external_state,
 		               reason);
-		g_object_notify (G_OBJECT (connection), NM_VPN_CONNECTION_VPN_STATE);
+		g_object_notify (G_OBJECT (self), NM_VPN_CONNECTION_VPN_STATE);
 	}
 
 	switch (vpn_state) {
@@ -378,25 +494,27 @@ _set_vpn_state (NMVpnConnection *connection,
 		break;
 	case STATE_PRE_UP:
 		if (!nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_PRE_UP,
-		                             priv->connection,
+		                             _get_settings_connection (self, FALSE),
+		                             _get_applied_connection (self),
 		                             parent_dev,
 		                             priv->ip_iface,
 		                             priv->ip4_config,
 		                             priv->ip6_config,
 		                             dispatcher_pre_up_done,
-		                             connection,
+		                             self,
 		                             &priv->dispatcher_id)) {
 			/* Just proceed on errors */
-			dispatcher_pre_up_done (0, connection);
+			dispatcher_pre_up_done (0, self);
 		}
 		break;
 	case STATE_ACTIVATED:
 		/* Secrets no longer needed now that we're connected */
-		nm_connection_clear_secrets (priv->connection);
+		nm_active_connection_clear_secrets (NM_ACTIVE_CONNECTION (self));
 
 		/* Let dispatcher scripts know we're up and running */
 		nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_UP,
-		                        priv->connection,
+		                        _get_settings_connection (self, FALSE),
+		                        _get_applied_connection (self),
 		                        parent_dev,
 		                        priv->ip_iface,
 		                        priv->ip4_config,
@@ -408,23 +526,25 @@ _set_vpn_state (NMVpnConnection *connection,
 	case STATE_DEACTIVATING:
 		if (quitting) {
 			nm_dispatcher_call_vpn_sync (DISPATCHER_ACTION_VPN_PRE_DOWN,
-			                             priv->connection,
+			                             _get_settings_connection (self, FALSE),
+			                             _get_applied_connection (self),
 			                             parent_dev,
 			                             priv->ip_iface,
 			                             priv->ip4_config,
 			                             priv->ip6_config);
 		} else {
 			if (!nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_PRE_DOWN,
-			                             priv->connection,
+			                             _get_settings_connection (self, FALSE),
+			                             _get_applied_connection (self),
 			                             parent_dev,
 			                             priv->ip_iface,
 			                             priv->ip4_config,
 			                             priv->ip6_config,
 			                             dispatcher_pre_down_done,
-			                             connection,
+			                             self,
 			                             &priv->dispatcher_id)) {
 				/* Just proceed on errors */
-				dispatcher_pre_down_done (0, connection);
+				dispatcher_pre_down_done (0, self);
 			}
 		}
 		break;
@@ -435,14 +555,16 @@ _set_vpn_state (NMVpnConnection *connection,
 			/* Let dispatcher scripts know we're about to go down */
 			if (quitting) {
 				nm_dispatcher_call_vpn_sync (DISPATCHER_ACTION_VPN_DOWN,
-				                             priv->connection,
+				                             _get_settings_connection (self, FALSE),
+				                             _get_applied_connection (self),
 				                             parent_dev,
 				                             priv->ip_iface,
 				                             NULL,
 				                             NULL);
 			} else {
 				nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_DOWN,
-				                        priv->connection,
+				                        _get_settings_connection (self, FALSE),
+				                        _get_applied_connection (self),
 				                        parent_dev,
 				                        priv->ip_iface,
 				                        NULL,
@@ -454,15 +576,15 @@ _set_vpn_state (NMVpnConnection *connection,
 		}
 
 		/* Tear down and clean up the connection */
-		call_plugin_disconnect (connection);
-		vpn_cleanup (connection, parent_dev);
+		call_plugin_disconnect (self);
+		vpn_cleanup (self, parent_dev);
 		/* Fall through */
 	default:
 		priv->secrets_idx = SECRETS_REQ_SYSTEM;
 		break;
 	}
 
-	g_object_unref (connection);
+	g_object_unref (self);
 	if (parent_dev)
 		g_object_unref (parent_dev);
 }
@@ -619,16 +741,16 @@ add_ip6_vpn_gateway_route (NMIP6Config *config,
 }
 
 NMVpnConnection *
-nm_vpn_connection_new (NMConnection *connection,
+nm_vpn_connection_new (NMSettingsConnection *settings_connection,
                        NMDevice *parent_device,
                        const char *specific_object,
                        NMAuthSubject *subject)
 {
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+	g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL);
 	g_return_val_if_fail (NM_IS_DEVICE (parent_device), NULL);
 
 	return (NMVpnConnection *) g_object_new (NM_TYPE_VPN_CONNECTION,
-	                                         NM_ACTIVE_CONNECTION_INT_CONNECTION, connection,
+	                                         NM_ACTIVE_CONNECTION_INT_SETTINGS_CONNECTION, settings_connection,
 	                                         NM_ACTIVE_CONNECTION_INT_DEVICE, parent_device,
 	                                         NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, specific_object,
 	                                         NM_ACTIVE_CONNECTION_INT_SUBJECT, subject,
@@ -636,13 +758,12 @@ nm_vpn_connection_new (NMConnection *connection,
 	                                         NULL);
 }
 
-static const char *
-nm_vpn_connection_get_service (NMVpnConnection *connection)
+const char *
+nm_vpn_connection_get_service (NMVpnConnection *self)
 {
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
 	NMSettingVpn *s_vpn;
 
-	s_vpn = nm_connection_get_setting_vpn (priv->connection);
+	s_vpn = nm_connection_get_setting_vpn (_get_applied_connection (self));
 	return nm_setting_vpn_get_service_type (s_vpn);
 }
 
@@ -663,16 +784,13 @@ vpn_plugin_failure_to_string (NMVpnPluginFailure failure)
 }
 
 static void
-plugin_failed (DBusGProxy *proxy,
-               NMVpnPluginFailure plugin_failure,
-               gpointer user_data)
+plugin_failed (NMVpnConnection *self, guint reason)
 {
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (user_data);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
-	nm_log_warn (LOGD_VPN, "VPN plugin failed: %s (%d)",
-	             vpn_plugin_failure_to_string (plugin_failure), plugin_failure);
+	_LOGW ("VPN plugin: failed: %s (%d)", vpn_plugin_failure_to_string (reason), reason);
 
-	switch (plugin_failure) {
+	switch (reason) {
 	case NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED:
 		priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED;
 		break;
@@ -762,30 +880,27 @@ vpn_reason_to_string (NMVpnConnectionStateReason reason)
 }
 
 static void
-plugin_state_changed (DBusGProxy *proxy,
-                      NMVpnServiceState new_service_state,
-                      gpointer user_data)
+plugin_state_changed (NMVpnConnection *self, NMVpnServiceState new_service_state)
 {
-	NMVpnConnection *connection = NM_VPN_CONNECTION (user_data);
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 	NMVpnServiceState old_service_state = priv->service_state;
 
-	nm_log_info (LOGD_VPN, "VPN plugin state changed: %s (%d)",
-	             vpn_service_state_to_string (new_service_state), new_service_state);
+	_LOGI ("VPN plugin: state changed: %s (%d)",
+	       vpn_service_state_to_string (new_service_state), new_service_state);
 	priv->service_state = new_service_state;
 
 	if (new_service_state == NM_VPN_SERVICE_STATE_STOPPED) {
 		/* Clear connection secrets to ensure secrets get requested each time the
 		 * connection is activated.
 		 */
-		nm_connection_clear_secrets (priv->connection);
+		nm_active_connection_clear_secrets (NM_ACTIVE_CONNECTION (self));
 
 		if ((priv->vpn_state >= STATE_WAITING) && (priv->vpn_state <= STATE_ACTIVATED)) {
 			VpnState old_state = priv->vpn_state;
 
-			nm_log_info (LOGD_VPN, "VPN plugin state change reason: %s (%d)",
-			             vpn_reason_to_string (priv->failure_reason), priv->failure_reason);
-			_set_vpn_state (connection, STATE_FAILED, priv->failure_reason, FALSE);
+			_LOGI ("VPN plugin: state change reason: %s (%d)",
+			       vpn_reason_to_string (priv->failure_reason), priv->failure_reason);
+			_set_vpn_state (self, STATE_FAILED, priv->failure_reason, FALSE);
 
 			/* Reset the failure reason */
 			priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_UNKNOWN;
@@ -795,20 +910,20 @@ plugin_state_changed (DBusGProxy *proxy,
 			 */
 			if (   old_state == STATE_ACTIVATED
 			    && priv->vpn_state == STATE_FAILED
-			    && _connection_only_can_persist (connection))
-				g_signal_emit (connection, signals[INTERNAL_RETRY_AFTER_FAILURE], 0);
+			    && _connection_only_can_persist (self))
+				g_signal_emit (self, signals[INTERNAL_RETRY_AFTER_FAILURE], 0);
 		}
 	} else if (new_service_state == NM_VPN_SERVICE_STATE_STARTING &&
 	           old_service_state == NM_VPN_SERVICE_STATE_STARTED) {
 		/* The VPN service got disconnected and is attempting to reconnect */
-		_set_vpn_state (connection, STATE_CONNECT, NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT, FALSE);
+		_set_vpn_state (self, STATE_CONNECT, NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT, FALSE);
 	}
 }
 
 static void
-print_vpn_config (NMVpnConnection *connection)
+print_vpn_config (NMVpnConnection *self)
 {
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 	const NMPlatformIP4Address *address4;
 	const NMPlatformIP6Address *address6;
 	char *dns_domain = NULL;
@@ -816,113 +931,115 @@ print_vpn_config (NMVpnConnection *connection)
 	char buf[NM_UTILS_INET_ADDRSTRLEN];
 
 	if (priv->ip4_external_gw) {
-		nm_log_info (LOGD_VPN, "VPN Gateway: %s",
-		             nm_utils_inet4_ntop (priv->ip4_external_gw, NULL));
+		_LOGI ("Data: VPN Gateway: %s",
+		       nm_utils_inet4_ntop (priv->ip4_external_gw, NULL));
 	} else if (priv->ip6_external_gw) {
-		nm_log_info (LOGD_VPN, "VPN Gateway: %s",
-		             nm_utils_inet6_ntop (priv->ip6_external_gw, NULL));
+		_LOGI ("Data: VPN Gateway: %s",
+		       nm_utils_inet6_ntop (priv->ip6_external_gw, NULL));
 	}
 
-	nm_log_info (LOGD_VPN, "Tunnel Device: %s", priv->ip_iface ? priv->ip_iface : "(none)");
+	_LOGI ("Data: Tunnel Device: %s", priv->ip_iface ? priv->ip_iface : "(none)");
 
 	if (priv->ip4_config) {
-		nm_log_info (LOGD_VPN, "IPv4 configuration:");
+		_LOGI ("Data: IPv4 configuration:");
 
 		address4 = nm_ip4_config_get_address (priv->ip4_config, 0);
 
 		if (priv->ip4_internal_gw)
-			nm_log_info (LOGD_VPN, "  Internal Gateway: %s", nm_utils_inet4_ntop (priv->ip4_internal_gw, NULL));
-		nm_log_info (LOGD_VPN, "  Internal Address: %s", nm_utils_inet4_ntop (address4->address, NULL));
-		nm_log_info (LOGD_VPN, "  Internal Prefix: %d", address4->plen);
-		nm_log_info (LOGD_VPN, "  Internal Point-to-Point Address: %s", nm_utils_inet4_ntop (address4->peer_address, NULL));
-		nm_log_info (LOGD_VPN, "  Maximum Segment Size (MSS): %d", nm_ip4_config_get_mss (priv->ip4_config));
+			_LOGI ("Data:   Internal Gateway: %s", nm_utils_inet4_ntop (priv->ip4_internal_gw, NULL));
+		_LOGI ("Data:   Internal Address: %s", nm_utils_inet4_ntop (address4->address, NULL));
+		_LOGI ("Data:   Internal Prefix: %d", address4->plen);
+		_LOGI ("Data:   Internal Point-to-Point Address: %s", nm_utils_inet4_ntop (address4->peer_address, NULL));
+		_LOGI ("Data:   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++) {
 			const NMPlatformIP4Route *route = nm_ip4_config_get_route (priv->ip4_config, i);
 
-			nm_log_info (LOGD_VPN, "  Static Route: %s/%d   Next Hop: %s",
-			             nm_utils_inet4_ntop (route->network, NULL),
-			             route->plen,
-			             nm_utils_inet4_ntop (route->gateway, buf));
+			_LOGI ("Data:   Static Route: %s/%d   Next Hop: %s",
+			       nm_utils_inet4_ntop (route->network, NULL),
+			       route->plen,
+			       nm_utils_inet4_ntop (route->gateway, buf));
 		}
 
-		nm_log_info (LOGD_VPN, "  Forbid Default Route: %s",
-		             nm_ip4_config_get_never_default (priv->ip4_config) ? "yes" : "no");
+		_LOGI ("Data:   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",
-			             nm_utils_inet4_ntop (nm_ip4_config_get_nameserver (priv->ip4_config, i), NULL));
+			_LOGI ("Data:   Internal DNS: %s",
+			       nm_utils_inet4_ntop (nm_ip4_config_get_nameserver (priv->ip4_config, i), NULL));
 		}
 
 		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)");
+		_LOGI ("Data:   DNS Domain: '%s'", dns_domain ? dns_domain : "(none)");
 	} else
-		nm_log_info (LOGD_VPN, "No IPv4 configuration");
+		_LOGI ("Data: No IPv4 configuration");
 
 	if (priv->ip6_config) {
-		nm_log_info (LOGD_VPN, "IPv6 configuration:");
+		_LOGI ("Data: IPv6 configuration:");
 
 		address6 = nm_ip6_config_get_address (priv->ip6_config, 0);
 
 		if (priv->ip6_internal_gw)
-			nm_log_info (LOGD_VPN, "  Internal Gateway: %s", nm_utils_inet6_ntop (priv->ip6_internal_gw, NULL));
-		nm_log_info (LOGD_VPN, "  Internal Address: %s", nm_utils_inet6_ntop (&address6->address, NULL));
-		nm_log_info (LOGD_VPN, "  Internal Prefix: %d", address6->plen);
-		nm_log_info (LOGD_VPN, "  Internal Point-to-Point Address: %s", nm_utils_inet6_ntop (&address6->peer_address, NULL));
-		nm_log_info (LOGD_VPN, "  Maximum Segment Size (MSS): %d", nm_ip6_config_get_mss (priv->ip6_config));
+			_LOGI ("Data:   Internal Gateway: %s", nm_utils_inet6_ntop (priv->ip6_internal_gw, NULL));
+		_LOGI ("Data:   Internal Address: %s", nm_utils_inet6_ntop (&address6->address, NULL));
+		_LOGI ("Data:   Internal Prefix: %d", address6->plen);
+		_LOGI ("Data:   Internal Point-to-Point Address: %s", nm_utils_inet6_ntop (&address6->peer_address, NULL));
+		_LOGI ("Data:   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++) {
 			const NMPlatformIP6Route *route = nm_ip6_config_get_route (priv->ip6_config, i);
 
-			nm_log_info (LOGD_VPN, "  Static Route: %s/%d   Next Hop: %s",
-			             nm_utils_inet6_ntop (&route->network, NULL),
-			             route->plen,
-			             nm_utils_inet6_ntop (&route->gateway, buf));
+			_LOGI ("Data:   Static Route: %s/%d   Next Hop: %s",
+			       nm_utils_inet6_ntop (&route->network, NULL),
+			       route->plen,
+			       nm_utils_inet6_ntop (&route->gateway, buf));
 		}
 
-		nm_log_info (LOGD_VPN, "  Forbid Default Route: %s",
-		             nm_ip6_config_get_never_default (priv->ip6_config) ? "yes" : "no");
+		_LOGI ("Data:   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",
-			             nm_utils_inet6_ntop (nm_ip6_config_get_nameserver (priv->ip6_config, i), NULL));
+			_LOGI ("Data:   Internal DNS: %s",
+			       nm_utils_inet6_ntop (nm_ip6_config_get_nameserver (priv->ip6_config, i), NULL));
 		}
 
 		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)");
+		_LOGI ("Data:   DNS Domain: '%s'", dns_domain ? dns_domain : "(none)");
 	} else
-		nm_log_info (LOGD_VPN, "No IPv6 configuration");
+		_LOGI ("Data: 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", priv->banner);
-		nm_log_info (LOGD_VPN, "-----------------------------------------");
+		_LOGI ("Data: Login Banner:");
+		_LOGI ("Data: -----------------------------------------");
+		_LOGI ("Data: %s", priv->banner);
+		_LOGI ("Data: -----------------------------------------");
 	}
 }
 
 static void
-apply_parent_device_config (NMVpnConnection *connection)
+apply_parent_device_config (NMVpnConnection *self)
 {
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
-	NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (connection));
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+	NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (self));
 	NMIP4Config *vpn4_parent_config = NULL;
 	NMIP6Config *vpn6_parent_config = NULL;
 
-	if (priv->ip4_config)
-		vpn4_parent_config = nm_ip4_config_new ();
-	if (priv->ip6_config)
-		vpn6_parent_config = nm_ip6_config_new ();
+	if (priv->ip_ifindex > 0) {
+		if (priv->ip4_config)
+			vpn4_parent_config = nm_ip4_config_new (priv->ip_ifindex);
+		if (priv->ip6_config)
+			vpn6_parent_config = nm_ip6_config_new (priv->ip_ifindex);
+	} else {
+		int ifindex;
 
-	if (priv->ip_ifindex <= 0) {
 		/* If the VPN didn't return a network interface, it is a route-based
 		 * VPN (like kernel IPSec) and all IP addressing and routing should
 		 * be done on the parent interface instead.
@@ -931,38 +1048,38 @@ apply_parent_device_config (NMVpnConnection *connection)
 		/* Also clear the gateway. We don't configure the gateway as part of the
 		 * vpn-config. Instead we tell NMDefaultRouteManager directly about the
 		 * default route. */
-		if (vpn4_parent_config) {
+		ifindex = nm_device_get_ip_ifindex (parent_dev);
+		if (priv->ip4_config) {
+			vpn4_parent_config = nm_ip4_config_new (ifindex);
 			nm_ip4_config_merge (vpn4_parent_config, priv->ip4_config, NM_IP_CONFIG_MERGE_DEFAULT);
 			nm_ip4_config_unset_gateway (vpn4_parent_config);
 		}
-		if (vpn6_parent_config) {
+		if (priv->ip6_config) {
+			vpn6_parent_config = nm_ip6_config_new (ifindex);
 			nm_ip6_config_merge (vpn6_parent_config, priv->ip6_config, NM_IP_CONFIG_MERGE_DEFAULT);
 			nm_ip6_config_set_gateway (vpn6_parent_config, NULL);
 		}
 	}
 
-	if (vpn4_parent_config) {
-		/* Add any explicit route to the VPN gateway through the parent device */
-		if (priv->ip4_external_gw)
-			add_ip4_vpn_gateway_route (vpn4_parent_config, parent_dev, priv->ip4_external_gw);
+	/* Add any explicit route to the VPN gateway through the parent device */
+	if (vpn4_parent_config && priv->ip4_external_gw)
+		add_ip4_vpn_gateway_route (vpn4_parent_config, parent_dev, priv->ip4_external_gw);
+	if (vpn6_parent_config && priv->ip6_external_gw)
+		add_ip6_vpn_gateway_route (vpn6_parent_config, parent_dev, priv->ip6_external_gw);
 
-		nm_device_set_vpn4_config (parent_dev, vpn4_parent_config);
-		g_object_unref (vpn4_parent_config);
-	}
-	if (vpn6_parent_config) {
-		/* Add any explicit route to the VPN gateway through the parent device */
-		if (priv->ip6_external_gw)
-			add_ip6_vpn_gateway_route (vpn6_parent_config, parent_dev, priv->ip6_external_gw);
+	nm_device_replace_vpn4_config (parent_dev, priv->last_device_ip4_config, vpn4_parent_config);
+	g_clear_object (&priv->last_device_ip4_config);
+	priv->last_device_ip4_config = vpn4_parent_config;
 
-		nm_device_set_vpn6_config (parent_dev, vpn6_parent_config);
-		g_object_unref (vpn6_parent_config);
-	}
+	nm_device_replace_vpn6_config (parent_dev, priv->last_device_ip6_config, vpn6_parent_config);
+	g_clear_object (&priv->last_device_ip6_config);
+	priv->last_device_ip6_config = vpn6_parent_config;
 }
 
 static gboolean
-nm_vpn_connection_apply_config (NMVpnConnection *connection)
+nm_vpn_connection_apply_config (NMVpnConnection *self)
 {
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
 	if (priv->ip_ifindex > 0) {
 		nm_platform_link_set_up (NM_PLATFORM_GET, priv->ip_ifindex, NULL);
@@ -970,7 +1087,7 @@ nm_vpn_connection_apply_config (NMVpnConnection *connection)
 		if (priv->ip4_config) {
 			if (!nm_ip4_config_commit (priv->ip4_config, priv->ip_ifindex,
 			                           TRUE,
-			                           nm_vpn_connection_get_ip4_route_metric (connection)))
+			                           nm_vpn_connection_get_ip4_route_metric (self)))
 				return FALSE;
 		}
 
@@ -985,56 +1102,60 @@ nm_vpn_connection_apply_config (NMVpnConnection *connection)
 			nm_platform_link_set_mtu (NM_PLATFORM_GET, priv->ip_ifindex, priv->mtu);
 	}
 
-	apply_parent_device_config (connection);
+	apply_parent_device_config (self);
 
-	nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), connection);
-	nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), connection);
+	nm_default_route_manager_ip4_update_default_route (priv->default_route_manager, self);
+	nm_default_route_manager_ip6_update_default_route (priv->default_route_manager, self);
 
-	nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.",
-	             nm_connection_get_id (priv->connection));
-	_set_vpn_state (connection, STATE_PRE_UP, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+	_LOGI ("VPN connection: (IP Config Get) complete");
+	_set_vpn_state (self, STATE_PRE_UP, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
 	return TRUE;
 }
 
 static void
-_cleanup_failed_config (NMVpnConnection *connection)
+_cleanup_failed_config (NMVpnConnection *self)
 {
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
-	g_clear_object (&priv->ip4_config);
-	g_clear_object (&priv->ip6_config);
+	nm_exported_object_clear_and_unexport (&priv->ip4_config);
+	nm_exported_object_clear_and_unexport (&priv->ip6_config);
 
-	nm_log_warn (LOGD_VPN, "VPN connection '%s' did not receive valid IP config information.",
-	             nm_connection_get_id (priv->connection));
-	_set_vpn_state (connection, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID, FALSE);
+	_LOGW ("VPN connection: did not receive valid IP config information");
+	_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID, FALSE);
 }
 
 static void
-fw_change_zone_cb (GError *error, gpointer user_data)
+fw_change_zone_cb (NMFirewallManager *firewall_manager,
+                   NMFirewallManagerCallId call_id,
+                   GError *error,
+                   gpointer user_data)
 {
-	NMVpnConnection *connection = NM_VPN_CONNECTION (user_data);
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMVpnConnection *self = user_data;
+	NMVpnConnectionPrivate *priv;
 
-	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
-		return;
+	g_return_if_fail (NM_IS_VPN_CONNECTION (self));
+
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+	g_return_if_fail (priv->fw_call == call_id);
 
 	priv->fw_call = NULL;
 
+	if (nm_utils_error_is_cancelled (error, FALSE))
+		return;
+
 	if (error) {
-		nm_log_warn (LOGD_VPN, "VPN connection '%s': setting firewall zone failed: '%s'",
-		             nm_connection_get_id (priv->connection), error->message);
 		// FIXME: fail the activation?
 	}
 
-	if (!nm_vpn_connection_apply_config (connection))
-		_cleanup_failed_config (connection);
+	if (!nm_vpn_connection_apply_config (self))
+		_cleanup_failed_config (self);
 }
 
 static void
-nm_vpn_connection_config_maybe_complete (NMVpnConnection *connection,
+nm_vpn_connection_config_maybe_complete (NMVpnConnection *self,
                                          gboolean         success)
 {
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 	NMConnection *base_con;
 	NMSettingConnection *s_con;
 	const char *zone;
@@ -1050,650 +1171,609 @@ nm_vpn_connection_config_maybe_complete (NMVpnConnection *connection,
 		}
 	}
 
-	if (priv->connect_timeout) {
-		g_source_remove (priv->connect_timeout);
-		priv->connect_timeout = 0;
-	}
+	nm_clear_g_source (&priv->connect_timeout);
 
 	if (success) {
-		print_vpn_config (connection);
+		print_vpn_config (self);
 
 		/* Add the tunnel interface to the specified firewall zone */
 		if (priv->ip_iface) {
-			base_con = nm_vpn_connection_get_connection (connection);
-			g_assert (base_con);
+			base_con = _get_applied_connection (self);
 			s_con = nm_connection_get_setting_connection (base_con);
 			zone = nm_setting_connection_get_zone (s_con);
 
-			nm_log_dbg (LOGD_VPN, "VPN connection '%s': setting firewall zone '%s' for '%s'",
-			            nm_connection_get_id (base_con), zone ? zone : "default", priv->ip_iface);
-			fw_call_cleanup (connection);
+			_LOGD ("setting firewall zone %s%s%s for '%s'",
+			       NM_PRINT_FMT_QUOTED (zone, "'", zone, "'", "(default)"),
+			       priv->ip_iface);
+			fw_call_cleanup (self);
 			priv->fw_call = nm_firewall_manager_add_or_change_zone (nm_firewall_manager_get (),
 			                                                        priv->ip_iface,
 			                                                        zone,
 			                                                        FALSE,
 			                                                        fw_change_zone_cb,
-			                                                        connection);
+			                                                        self);
 			return;
 		} else
-			if (nm_vpn_connection_apply_config (connection))
+			if (nm_vpn_connection_apply_config (self))
 				return;
 	}
 
-	_cleanup_failed_config (connection);
+	_cleanup_failed_config (self);
 }
 
-#define LOG_INVALID_ARG(property) \
-    nm_log_dbg (LOGD_VPN, "VPN connection '%s' has invalid argument %s", \
-                          nm_connection_get_id (priv->connection), property)
+static gboolean
+ip6_addr_from_variant (GVariant *v, struct in6_addr *addr)
+{
+	const guint8 *bytes;
+	gsize len;
+
+	g_return_val_if_fail (v, FALSE);
+	g_return_val_if_fail (addr, FALSE);
+
+	if (g_variant_is_of_type (v, G_VARIANT_TYPE ("ay"))) {
+		bytes = g_variant_get_fixed_array (v, &len, sizeof (guint8));
+		if (len == sizeof (struct in6_addr) && !IN6_IS_ADDR_UNSPECIFIED (bytes)) {
+			memcpy (addr, bytes, len);
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
+static struct in6_addr *
+ip6_addr_dup_from_variant (GVariant *v)
+{
+	struct in6_addr *addr;
+
+	addr = g_malloc0 (sizeof (*addr));
+	if (ip6_addr_from_variant (v, addr))
+		return addr;
+	g_free (addr);
+	return NULL;
+}
 
 static gboolean
-process_generic_config (NMVpnConnection *connection,
-                        GHashTable *config_hash)
+process_generic_config (NMVpnConnection *self, GVariant *dict)
 {
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
-	GValue *val;
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+	const char *str;
+	GVariant *v;
+	guint32 u32;
+	gboolean b, success = FALSE;
 
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CAN_PERSIST);
-	if (val && G_VALUE_HOLDS_BOOLEAN (val) && g_value_get_boolean (val)) {
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_CAN_PERSIST, "b", &b) && b) {
 		/* Defaults to FALSE, so only let service indicate TRUE */
 		priv->service_can_persist = TRUE;
 	}
 
 	g_clear_pointer (&priv->ip_iface, g_free);
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_TUNDEV);
-	if (val) {
-		if (G_VALUE_HOLDS (val, G_TYPE_STRING)) {
-			const char *tmp = g_value_get_string (val);
-
-			/* Backwards compat with NM-openswan */
-			if (g_strcmp0 (tmp, "_none_") != 0)
-				priv->ip_iface = g_strdup (tmp);
-		} else
-			LOG_INVALID_ARG (NM_VPN_PLUGIN_CONFIG_TUNDEV);
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_CONFIG_TUNDEV, "&s", &str)) {
+		/* Backwards compat with NM-openswan */
+		if (g_strcmp0 (str, "_none_") != 0)
+			priv->ip_iface = g_strdup (str);
 	}
 
 	if (priv->ip_iface) {
 		/* Grab the interface index for address/routing operations */
 		priv->ip_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface);
-		if (!priv->ip_ifindex) {
-			nm_log_err (LOGD_VPN, "(%s): failed to look up VPN interface index", priv->ip_iface);
-			nm_vpn_connection_config_maybe_complete (connection, FALSE);
+		if (priv->ip_ifindex <= 0) {
+			_LOGE ("failed to look up VPN interface index for \"%s\"", priv->ip_iface);
+			nm_vpn_connection_config_maybe_complete (self, FALSE);
 			return FALSE;
 		}
 	}
 
 	g_clear_pointer (&priv->banner, g_free);
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_BANNER);
-	if (val) {
-		if (G_VALUE_HOLDS (val, G_TYPE_STRING))
-			priv->banner = g_strdup (g_value_get_string (val));
-		else
-			LOG_INVALID_ARG (NM_VPN_PLUGIN_CONFIG_BANNER);
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_CONFIG_BANNER, "&s", &str)) {
+		priv->banner = g_strdup (str);
+		g_object_notify (G_OBJECT (self), NM_VPN_CONNECTION_BANNER);
 	}
 
 	/* External world-visible address of the VPN server */
 	priv->ip4_external_gw = 0;
 	g_clear_pointer (&priv->ip6_external_gw, g_free);
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY);
-	if (val) {
-		GByteArray *ba;
-
-		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) &&
-		           (ba = g_value_get_boxed (val)) &&
-		           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;
-		}
+
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, "u", &u32)) {
+		priv->ip4_external_gw = u32;
+		success = TRUE;
+	} else if (g_variant_lookup (dict, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY, "@ay", &v)) {
+		priv->ip6_external_gw = ip6_addr_dup_from_variant (v);
+		success = !!priv->ip6_external_gw;
+		g_variant_unref (v);
 	}
 
-	priv->mtu = 0;
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_MTU);
-	if (val) {
-		if (G_VALUE_HOLDS (val, G_TYPE_UINT)) {
-			priv->mtu = g_value_get_uint (val);
-		} else
-			LOG_INVALID_ARG (NM_VPN_PLUGIN_CONFIG_MTU);
+	if (!success) {
+		_LOGE ("VPN gateway is neither IPv4 nor IPv6");
+		nm_vpn_connection_config_maybe_complete (self, FALSE);
+		return FALSE;
 	}
 
+	priv->mtu = 0;
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_CONFIG_MTU, "u", &u32))
+		priv->mtu = u32;
+
 	return TRUE;
 }
 
 static void
-nm_vpn_connection_config_get (DBusGProxy *proxy,
-                              GHashTable *config_hash,
-                              gpointer user_data)
+nm_vpn_connection_config_get (NMVpnConnection *self, GVariant *dict)
 {
-	NMVpnConnection *connection = NM_VPN_CONNECTION (user_data);
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
-	GValue *val;
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+	gboolean b;
 
-	nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) reply received.",
-	             nm_connection_get_id (priv->connection));
+	g_return_if_fail (dict && g_variant_is_of_type (dict, G_VARIANT_TYPE_VARDICT));
+
+	_LOGI ("VPN connection: (IP Config Get) reply received.");
 
 	if (priv->vpn_state == STATE_CONNECT)
-		_set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+		_set_vpn_state (self, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
 
-	if (!process_generic_config (connection, config_hash))
+	if (!process_generic_config (self, dict))
 		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 = FALSE;
-	if (val) {
-		if (G_VALUE_HOLDS (val, G_TYPE_BOOLEAN))
-			priv->has_ip4 = g_value_get_boolean (val);
-		else
-			LOG_INVALID_ARG (NM_VPN_PLUGIN_CONFIG_HAS_IP4);
-	}
-	g_clear_object (&priv->ip4_config);
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_CONFIG_HAS_IP4, "b", &b))
+		priv->has_ip4 = b;
+	nm_exported_object_clear_and_unexport (&priv->ip4_config);
 
-	val = g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_HAS_IP6);
 	priv->has_ip6 = FALSE;
-	if (val) {
-		if (G_VALUE_HOLDS (val, G_TYPE_BOOLEAN))
-			priv->has_ip6 = g_value_get_boolean (val);
-		else
-			LOG_INVALID_ARG (NM_VPN_PLUGIN_CONFIG_HAS_IP6);
-	}
-	g_clear_object (&priv->ip6_config);
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_CONFIG_HAS_IP6, "b", &b))
+		priv->has_ip6 = b;
+	nm_exported_object_clear_and_unexport (&priv->ip6_config);
 }
 
 guint32
-nm_vpn_connection_get_ip4_route_metric (NMVpnConnection *connection)
+nm_vpn_connection_get_ip4_route_metric (NMVpnConnection *self)
 {
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
-
-	if (priv->connection) {
-		gint64 route_metric = nm_setting_ip_config_get_route_metric (nm_connection_get_setting_ip4_config (priv->connection));
+	gint64 route_metric;
+	NMConnection *applied;
 
-		if (route_metric >= 0)
-			return route_metric;
-	}
+	applied = _get_applied_connection (self);
+	route_metric = nm_setting_ip_config_get_route_metric (nm_connection_get_setting_ip4_config (applied));
 
-	return NM_VPN_ROUTE_METRIC_DEFAULT;
+	return (route_metric >= 0) ? route_metric : NM_VPN_ROUTE_METRIC_DEFAULT;
 }
 
 guint32
-nm_vpn_connection_get_ip6_route_metric (NMVpnConnection *connection)
+nm_vpn_connection_get_ip6_route_metric (NMVpnConnection *self)
 {
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	gint64 route_metric;
+	NMConnection *applied;
 
-	if (priv->connection) {
-		gint64 route_metric = nm_setting_ip_config_get_route_metric (nm_connection_get_setting_ip6_config (priv->connection));
+	applied = _get_applied_connection (self);
+	route_metric = nm_setting_ip_config_get_route_metric (nm_connection_get_setting_ip6_config (applied));
 
-		if (route_metric >= 0)
-			return route_metric;
-	}
-
-	return NM_VPN_ROUTE_METRIC_DEFAULT;
+	return (route_metric >= 0) ? route_metric : NM_VPN_ROUTE_METRIC_DEFAULT;
 }
 
 static void
-nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
-                                  GHashTable *config_hash,
-                                  gpointer user_data)
+nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
 {
-	NMVpnConnection *connection = NM_VPN_CONNECTION (user_data);
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 	NMPlatformIP4Address address;
 	NMIP4Config *config;
-	GValue *val;
-	int i;
-	guint32 route_metric;
+	guint32 u32, route_metric;
+	GVariantIter *iter;
+	const char *str;
+	GVariant *v;
+	gboolean b;
+	int ifindex;
+
+	g_return_if_fail (dict && g_variant_is_of_type (dict, G_VARIANT_TYPE_VARDICT));
 
 	if (priv->vpn_state == STATE_CONNECT)
-		_set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+		_set_vpn_state (self, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
 
 	if (priv->has_ip4) {
-		nm_log_info (LOGD_VPN, "VPN connection '%s' (IP4 Config Get) reply received.",
-		             nm_connection_get_id (priv->connection));
+		_LOGI ("VPN connection: (IP4 Config Get) reply received");
 
-		if (g_hash_table_size (config_hash) == 0) {
+		if (g_variant_n_children (dict) == 0) {
 			priv->has_ip4 = FALSE;
-			nm_vpn_connection_config_maybe_complete (connection, TRUE);
+			nm_vpn_connection_config_maybe_complete (self, TRUE);
 			return;
 		}
 	} else {
-		nm_log_info (LOGD_VPN, "VPN connection '%s' (IP4 Config Get) reply received from old-style plugin.",
-		             nm_connection_get_id (priv->connection));
+		_LOGI ("VPN connection: (IP4 Config Get) reply received from old-style plugin");
 
 		/* In the old API, the generic and IPv4 configuration items
 		 * were mixed together.
 		 */
-		if (!process_generic_config (connection, config_hash))
+		if (!process_generic_config (self, dict))
 			return;
 
 		priv->has_ip4 = TRUE;
 		priv->has_ip6 = FALSE;
 	}
 
-	config = nm_ip4_config_new ();
+	if (priv->ip_ifindex > 0) {
+		ifindex = priv->ip_ifindex;
+	} else {
+		NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (self));
+		ifindex = nm_device_get_ip_ifindex (parent_dev);
+	}
+	config = nm_ip4_config_new (ifindex);
 
 	memset (&address, 0, sizeof (address));
 	address.plen = 24;
 
 	/* 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);
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY, "u", &u32)) {
+		priv->ip4_internal_gw = u32;
 		nm_ip4_config_set_gateway (config, priv->ip4_internal_gw);
 	}
 
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS);
-	if (val)
-		address.address = g_value_get_uint (val);
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS, "u", &u32))
+		address.address = u32;
 
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_PTP);
-	if (val)
-		address.peer_address = g_value_get_uint (val);
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_PTP, "u", &u32))
+		address.peer_address = u32;
+	else
+		address.peer_address = address.address;
 
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX);
-	if (val)
-		address.plen = g_value_get_uint (val);
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, "u", &u32))
+		address.plen = u32;
 
 	if (address.address && address.plen) {
 		address.source = NM_IP_CONFIG_SOURCE_VPN;
 		nm_ip4_config_add_address (config, &address);
 	} else {
-		nm_log_err (LOGD_VPN, "invalid IP4 config received!");
+		_LOGE ("invalid IP4 config received!");
 		g_object_unref (config);
-		nm_vpn_connection_config_maybe_complete (connection, FALSE);
+		nm_vpn_connection_config_maybe_complete (self, FALSE);
 		return;
 	}
 
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_DNS);
-	if (val) {
-		GArray *dns = (GArray *) g_value_get_boxed (val);
-
-		for (i = 0; i < dns->len; i++)
-			nm_ip4_config_add_nameserver (config, g_array_index (dns, guint, i));
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_DNS, "au", &iter)) {
+		while (g_variant_iter_next (iter, "u", &u32))
+			nm_ip4_config_add_nameserver (config, u32);
+		g_variant_iter_free (iter);
 	}
 
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_NBNS);
-	if (val) {
-		GArray *nbns = (GArray *) g_value_get_boxed (val);
-
-		for (i = 0; i < nbns->len; i++)
-			nm_ip4_config_add_wins (config, g_array_index (nbns, guint, i));
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_NBNS, "au", &iter)) {
+		while (g_variant_iter_next (iter, "u", &u32))
+			nm_ip4_config_add_wins (config, u32);
+		g_variant_iter_free (iter);
 	}
 
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_MSS);
-	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_DOMAIN);
-	if (val)
-		nm_ip4_config_add_domain (config, g_value_get_string (val));
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_MSS, "u", &u32))
+		nm_ip4_config_set_mss (config, u32);
 
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_DOMAINS);
-	if (val) {
-		const char **domains = g_value_get_boxed (val);
-		const char **domain;
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN, "&s", &str))
+		nm_ip4_config_add_domain (config, str);
 
-		for (domain = domains; domain && *domain; domain++)
-			nm_ip4_config_add_domain (config, *domain);
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_DOMAINS, "as", &iter)) {
+		while (g_variant_iter_next (iter, "&s", &str))
+			nm_ip4_config_add_domain (config, str);
+		g_variant_iter_free (iter);
 	}
 
-	route_metric = nm_vpn_connection_get_ip4_route_metric (connection);
+	route_metric = nm_vpn_connection_get_ip4_route_metric (self);
 
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES);
-	if (val) {
-		GSList *routes;
-		GSList *iter;
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES, "aau", &iter)) {
+		while (g_variant_iter_next (iter, "@au", &v)) {
+			NMPlatformIP4Route route = { 0, };
 
-		routes = nm_utils_ip4_routes_from_gvalue (val);
-		for (iter = routes; iter; iter = iter->next) {
-			NMIPRoute *item = iter->data;
-			NMPlatformIP4Route route;
+			switch (g_variant_n_children (v)) {
+			case 5:
+				g_variant_get_child (v, 4, "u", &route.pref_src);
+				/* fallthrough */
+			case 4:
+				g_variant_get_child (v, 0, "u", &route.network);
+				g_variant_get_child (v, 1, "u", &route.plen);
+				g_variant_get_child (v, 2, "u", &route.gateway);
+				/* 4th item is unused route metric */
+				route.metric = route_metric;
+				route.source = NM_IP_CONFIG_SOURCE_VPN;
 
-			memset (&route, 0, sizeof (route));
-			nm_ip_route_get_dest_binary (item, &route.network);
-			route.plen = nm_ip_route_get_prefix (item);
-			nm_ip_route_get_next_hop_binary (item, &route.gateway);
-			route.source = NM_IP_CONFIG_SOURCE_VPN;
-			route.metric = route_metric;
-
-			/* 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->ip4_external_gw && route.network == priv->ip4_external_gw && route.plen == 32)
-				continue;
-
-			/* Otherwise accept the VPN-provided route */
-			nm_ip4_config_add_route (config, &route);
+				/* 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->ip4_external_gw && route.network == priv->ip4_external_gw && route.plen == 32))
+					nm_ip4_config_add_route (config, &route);
+				break;
+			default:
+				_LOGW ("VPN connection: received invalid IPv4 route");
+			}
+			g_variant_unref (v);
 		}
-
-		g_slist_free_full (routes, (GDestroyNotify) nm_ip_route_unref);
+		g_variant_iter_free (iter);
 	}
 
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT);
-	if (val && G_VALUE_HOLDS_BOOLEAN (val))
-		nm_ip4_config_set_never_default (config, g_value_get_boolean (val));
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT, "b", &b))
+		nm_ip4_config_set_never_default (config, b);
 
 	/* Merge in user overrides from the NMConnection's IPv4 setting */
 	nm_ip4_config_merge_setting (config,
-	                             nm_connection_get_setting_ip4_config (priv->connection),
+	                             nm_connection_get_setting_ip4_config (_get_applied_connection (self)),
 	                             route_metric);
 
-	g_clear_object (&priv->ip4_config);
+	nm_exported_object_clear_and_unexport (&priv->ip4_config);
 	priv->ip4_config = config;
-	nm_ip4_config_export (config);
-	g_object_notify (G_OBJECT (connection), NM_ACTIVE_CONNECTION_IP4_CONFIG);
-	nm_vpn_connection_config_maybe_complete (connection, TRUE);
+	nm_exported_object_export (NM_EXPORTED_OBJECT (config));
+	g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_IP4_CONFIG);
+	nm_vpn_connection_config_maybe_complete (self, TRUE);
 }
 
 static void
-nm_vpn_connection_ip6_config_get (DBusGProxy *proxy,
-                                  GHashTable *config_hash,
-                                  gpointer user_data)
+nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict)
 {
-	NMVpnConnection *connection = NM_VPN_CONNECTION (user_data);
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 	NMPlatformIP6Address address;
+	guint32 u32, route_metric;
 	NMIP6Config *config;
-	GValue *val;
-	int i;
-	guint32 route_metric;
+	GVariantIter *iter;
+	const char *str;
+	GVariant *v;
+	gboolean b;
 
-	nm_log_info (LOGD_VPN, "VPN connection '%s' (IP6 Config Get) reply received.",
-	             nm_connection_get_id (priv->connection));
+	g_return_if_fail (dict && g_variant_is_of_type (dict, G_VARIANT_TYPE_VARDICT));
+
+	_LOGI ("VPN connection: (IP6 Config Get) reply received");
 
 	if (priv->vpn_state == STATE_CONNECT)
-		_set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+		_set_vpn_state (self, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
 
-	if (g_hash_table_size (config_hash) == 0) {
+	if (g_variant_n_children (dict) == 0) {
 		priv->has_ip6 = FALSE;
-		nm_vpn_connection_config_maybe_complete (connection, TRUE);
+		nm_vpn_connection_config_maybe_complete (self, TRUE);
 		return;
 	}
 
-	config = nm_ip6_config_new ();
+	config = nm_ip6_config_new (priv->ip_ifindex);
 
 	memset (&address, 0, sizeof (address));
 	address.plen = 128;
 
 	/* Internal address of the VPN subnet's gateway */
 	g_clear_pointer (&priv->ip6_internal_gw, g_free);
-	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);
-			nm_ip6_config_set_gateway (config, priv->ip6_internal_gw);
-		}
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_INT_GATEWAY, "@ay", &v)) {
+		priv->ip6_internal_gw = ip6_addr_dup_from_variant (v);
+		nm_ip6_config_set_gateway (config, priv->ip6_internal_gw);
+		g_variant_unref (v);
 	}
 
-	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))
-			address.address = *(struct in6_addr *) ba->data;
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS, "@ay", &v)) {
+		ip6_addr_from_variant (v, &address.address);
+		g_variant_unref (v);
 	}
 
-	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))
-			address.peer_address = *(struct in6_addr *) ba->data;
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_PTP, "@ay", &v)) {
+		ip6_addr_from_variant (v, &address.peer_address);
+		g_variant_unref (v);
 	}
 
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_PREFIX);
-	if (val)
-		address.plen = g_value_get_uint (val);
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_PREFIX, "u", &u32))
+		address.plen = u32;
 
 	if (!IN6_IS_ADDR_UNSPECIFIED (&address.address) && address.plen) {
 		address.source = NM_IP_CONFIG_SOURCE_VPN;
 		nm_ip6_config_add_address (config, &address);
 	} else {
-		nm_log_err (LOGD_VPN, "invalid IP6 config received!");
+		_LOGE ("invalid IP6 config received!");
 		g_object_unref (config);
-		nm_vpn_connection_config_maybe_complete (connection, FALSE);
+		nm_vpn_connection_config_maybe_complete (self, FALSE);
+		return;
 	}
 
-	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;
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_DNS, "aay", &iter)) {
+		while (g_variant_iter_next (iter, "@ay", &v)) {
+			struct in6_addr dns;
 
-		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);
+			if (ip6_addr_from_variant (v, &dns))
+				nm_ip6_config_add_nameserver (config, &dns);
+			g_variant_unref (v);
 		}
+		g_variant_iter_free (iter);
 	}
 
-	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));
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_MSS, "u", &u32))
+		nm_ip6_config_set_mss (config, u32);
 
-	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));
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_DOMAIN, "&s", &str))
+		nm_ip6_config_add_domain (config, str);
 
-	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);
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_DOMAINS, "as", &iter)) {
+		while (g_variant_iter_next (iter, "&s", &str))
+			nm_ip6_config_add_domain (config, str);
+		g_variant_iter_free (iter);
 	}
 
-	route_metric = nm_vpn_connection_get_ip6_route_metric (connection);
+	route_metric = nm_vpn_connection_get_ip6_route_metric (self);
 
-	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_ROUTES);
-	if (val) {
-		GSList *routes;
-		GSList *iter;
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_ROUTES, "a(ayuayu)", &iter)) {
+		GVariant *dest, *next_hop;
+		guint32 prefix, metric;
 
-		routes = nm_utils_ip6_routes_from_gvalue (val);
-		for (iter = routes; iter; iter = iter->next) {
-			NMIPRoute *item = iter->data;
+		while (g_variant_iter_next (iter, "(@ayu@ayu)", &dest, &prefix, &next_hop, &metric)) {
 			NMPlatformIP6Route route;
 
 			memset (&route, 0, sizeof (route));
-			nm_ip_route_get_dest_binary (item, &route.network);
-			route.plen = nm_ip_route_get_prefix (item);
-			nm_ip_route_get_next_hop_binary (item, &route.gateway);
-			route.source = NM_IP_CONFIG_SOURCE_VPN;
+
+			if (!ip6_addr_from_variant (dest, &route.network)) {
+				_LOGW ("VPN connection: received invalid IPv6 dest address");
+				goto next;
+			}
+
+			route.plen = prefix;
+			ip6_addr_from_variant (next_hop, &route.gateway);
 			route.metric = route_metric;
+			route.source = NM_IP_CONFIG_SOURCE_VPN;
 
-			/* 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.
+			/* Ignore host routes to the VPN gateway since NM adds one itself.
+			 * 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 && IN6_ARE_ADDR_EQUAL (&route.network, priv->ip6_external_gw) && route.plen == 128)
-				continue;
+			if (!(priv->ip6_external_gw && IN6_ARE_ADDR_EQUAL (&route.network, priv->ip6_external_gw) && route.plen == 128))
+				nm_ip6_config_add_route (config, &route);
 
-			/* Otherwise accept the VPN-provided route */
-			nm_ip6_config_add_route (config, &route);
+next:
+			g_variant_unref (dest);
+			g_variant_unref (next_hop);
 		}
-
-		g_slist_free_full (routes, (GDestroyNotify) nm_ip_route_unref);
+		g_variant_iter_free (iter);
 	}
 
-	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));
+	if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_NEVER_DEFAULT, "b", &b))
+		nm_ip6_config_set_never_default (config, b);
 
 	/* Merge in user overrides from the NMConnection's IPv6 setting */
 	nm_ip6_config_merge_setting (config,
-	                             nm_connection_get_setting_ip6_config (priv->connection),
+	                             nm_connection_get_setting_ip6_config (_get_applied_connection (self)),
 	                             route_metric);
 
-	g_clear_object (&priv->ip6_config);
+	nm_exported_object_clear_and_unexport (&priv->ip6_config);
 	priv->ip6_config = config;
-	nm_ip6_config_export (config);
-	g_object_notify (G_OBJECT (connection), NM_ACTIVE_CONNECTION_IP6_CONFIG);
-	nm_vpn_connection_config_maybe_complete (connection, TRUE);
+	nm_exported_object_export (NM_EXPORTED_OBJECT (config));
+	g_object_notify (G_OBJECT (self), NM_ACTIVE_CONNECTION_IP6_CONFIG);
+	nm_vpn_connection_config_maybe_complete (self, TRUE);
 }
 
 static gboolean
 connect_timeout_cb (gpointer user_data)
 {
-	NMVpnConnection *connection = NM_VPN_CONNECTION (user_data);
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMVpnConnection *self = NM_VPN_CONNECTION (user_data);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
 	priv->connect_timeout = 0;
 
 	/* Cancel activation if it's taken too long */
 	if (priv->vpn_state == STATE_CONNECT ||
 	    priv->vpn_state == STATE_IP_CONFIG_GET) {
-		nm_log_warn (LOGD_VPN, "VPN connection '%s' connect timeout exceeded.",
-		             nm_connection_get_id (priv->connection));
-		_set_vpn_state (connection, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT, FALSE);
+		_LOGW ("VPN connection: connect timeout exceeded.");
+		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT, FALSE);
 	}
 
 	return FALSE;
 }
 
 static void
-connect_success (NMVpnConnection *connection)
+connect_success (NMVpnConnection *self)
 {
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+	NMSettingVpn *s_vpn;
+	guint32 timeout;
+
+	s_vpn = nm_connection_get_setting_vpn (_get_applied_connection (self));
+	g_assert (s_vpn);
 
-	/* 40 second timeout waiting for IP config signal from VPN service */
-	priv->connect_timeout = g_timeout_add_seconds (40, connect_timeout_cb, connection);
+	/* Timeout waiting for IP config signal from VPN service
+	 * It is a configured value or 60 seconds */
+	timeout = nm_setting_vpn_get_timeout (s_vpn);
+	if (timeout == 0) {
+		char *value;
+
+		value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+		                                              "vpn.timeout", NULL);
+		timeout = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, 60);
+		timeout = timeout == 0 ? 60 : timeout;
+		g_free (value);
+	}
+	priv->connect_timeout = g_timeout_add_seconds (timeout, connect_timeout_cb, self);
 
-	g_hash_table_destroy (priv->connect_hash);
-	priv->connect_hash = NULL;
+	g_clear_pointer (&priv->connect_hash, g_variant_unref);
 }
 
 static void
-connect_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
+connect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
-	NMVpnConnection *self = NM_VPN_CONNECTION (user_data);
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
-	GError *err = NULL;
-
-	nm_log_info (LOGD_VPN, "VPN connection '%s' (Connect) reply received.",
-	             nm_connection_get_id (priv->connection));
+	NMVpnConnection *self;
+	gs_unref_variant GVariant *reply = NULL;
+	gs_free_error GError *error = NULL;
 
-	dbus_g_proxy_end_call (proxy, call, &err, G_TYPE_INVALID);
-	if (!err) {
-		connect_success (self);
+	reply = g_dbus_proxy_call_finish (proxy, result, &error);
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
-	}
 
-	nm_log_warn (LOGD_VPN, "VPN connection '%s' failed to connect: '%s'.",
-	             nm_connection_get_id (priv->connection), err->message);
-	g_error_free (err);
-	_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE);
+	self = NM_VPN_CONNECTION (user_data);
+
+	if (error) {
+		g_dbus_error_strip_remote_error (error);
+		_LOGW ("VPN connection: failed to connect: '%s'",
+		       error->message);
+		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE);
+	} else
+		connect_success (self);
 }
 
 static void
-connect_interactive_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
+connect_interactive_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
-	NMVpnConnection *self = NM_VPN_CONNECTION (user_data);
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
-	GError *err = NULL;
-
-	nm_log_info (LOGD_VPN, "VPN connection '%s' (ConnectInteractive) reply received.",
-	             nm_connection_get_id (priv->connection));
+	NMVpnConnection *self;
+	NMVpnConnectionPrivate *priv;
+	gs_unref_variant GVariant *reply = NULL;
+	gs_free_error GError *error = NULL;
 
-	dbus_g_proxy_end_call (proxy, call, &err, G_TYPE_INVALID);
-	if (!err) {
-		connect_success (self);
+	reply = g_dbus_proxy_call_finish (proxy, result, &error);
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
-	}
 
-	if (dbus_g_error_has_name (err, NM_DBUS_VPN_ERROR_PREFIX "." NM_DBUS_VPN_INTERACTIVE_NOT_SUPPORTED)) {
+	self = NM_VPN_CONNECTION (user_data);
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+	_LOGI ("VPN connection: (ConnectInteractive) reply received");
+
+	if (g_error_matches (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED)) {
+		_LOGD ("VPN connection: falling back to non-interactive connect");
+
 		/* Fall back to Connect() */
-		dbus_g_proxy_begin_call (priv->proxy, "Connect",
-		                         connect_cb, self, NULL,
-		                         DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, priv->connect_hash,
-		                         G_TYPE_INVALID);
-	} else {
-		nm_log_warn (LOGD_VPN, "VPN connection '%s' failed to connect interactively: '%s'.",
-		             nm_connection_get_id (priv->connection), err->message);
-		g_error_free (err);
+		g_dbus_proxy_call (priv->proxy,
+		                   "Connect",
+		                   g_variant_new ("(@a{sa{sv}})", priv->connect_hash),
+		                   G_DBUS_CALL_FLAGS_NONE,
+		                   -1,
+		                   priv->cancellable,
+		                   (GAsyncReadyCallback) connect_cb,
+		                   self);
+	} else if (error) {
+		g_dbus_error_strip_remote_error (error);
+		_LOGW ("VPN connection: failed to connect interactively: '%s'",
+		       error->message);
 		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE);
-	}
+	} else
+		connect_success (self);
 }
 
 /* Add a username to a hashed connection */
-static GHashTable *
+static GVariant *
 _hash_with_username (NMConnection *connection, const char *username)
 {
-	NMConnection *dup;
+	gs_unref_object NMConnection *dup = NULL;
 	NMSettingVpn *s_vpn;
-	GHashTable *hash;
-	GVariant *dict;
-	const char *existing;
 
 	/* Shortcut if we weren't given a username or if there already was one in
 	 * the VPN setting; don't bother duplicating the connection and everything.
 	 */
 	s_vpn = nm_connection_get_setting_vpn (connection);
 	g_assert (s_vpn);
-	existing = nm_setting_vpn_get_user_name (s_vpn);
-	if (username == NULL || existing) {
-		dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL);
-		hash = nm_utils_connection_dict_to_hash (dict);
-		g_variant_unref (dict);
-		return hash;
-	}
+	if (username == NULL || nm_setting_vpn_get_user_name (s_vpn))
+		return nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL);
 
 	dup = nm_simple_connection_new_clone (connection);
 	g_assert (dup);
 	s_vpn = nm_connection_get_setting_vpn (dup);
 	g_assert (s_vpn);
 	g_object_set (s_vpn, NM_SETTING_VPN_USER_NAME, username, NULL);
-	dict = nm_connection_to_dbus (dup, NM_CONNECTION_SERIALIZE_ALL);
-	g_object_unref (dup);
-
-	hash = nm_utils_connection_dict_to_hash (dict);
-	g_variant_unref (dict);
-	return hash;
+	return nm_connection_to_dbus (dup, NM_CONNECTION_SERIALIZE_ALL);
 }
 
 static void
-really_activate (NMVpnConnection *connection, const char *username)
+really_activate (NMVpnConnection *self, const char *username)
 {
 	NMVpnConnectionPrivate *priv;
-	GHashTable *details;
+	GVariantBuilder details;
 
-	g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
+	g_return_if_fail (NM_IS_VPN_CONNECTION (self));
 
-	priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 	g_return_if_fail (priv->vpn_state == STATE_NEED_AUTH);
 
-	dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED,
-	                                   G_TYPE_NONE, G_TYPE_VALUE, G_TYPE_INVALID);
-
-	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);
-	dbus_g_proxy_connect_signal (priv->proxy, "Ip4Config",
-	                             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);
-
-	if (priv->connect_hash)
-		g_hash_table_destroy (priv->connect_hash);
-	priv->connect_hash = _hash_with_username (priv->connection, username);
-	details = g_hash_table_new (g_str_hash, g_str_equal);
+	g_clear_pointer (&priv->connect_hash, g_variant_unref);
+	priv->connect_hash = _hash_with_username (_get_applied_connection (self), username);
+	g_variant_ref_sink (priv->connect_hash);
 
 	/* If at least one agent doesn't support VPN hints, then we can't use
 	 * ConnectInteractive(), because that agent won't be able to pass hints
@@ -1702,183 +1782,386 @@ really_activate (NMVpnConnection *connection, const char *username)
 	 * the old Connect() call.
 	 */
 	if (nm_agent_manager_all_agents_have_capability (nm_agent_manager_get (),
-	                                                 nm_active_connection_get_subject (NM_ACTIVE_CONNECTION (connection)),
+	                                                 nm_active_connection_get_subject (NM_ACTIVE_CONNECTION (self)),
 	                                                 NM_SECRET_AGENT_CAPABILITY_VPN_HINTS)) {
-		nm_log_dbg (LOGD_VPN, "Allowing interactive secrets as all agents have that capability");
-		dbus_g_proxy_begin_call (priv->proxy, "ConnectInteractive",
-		                         connect_interactive_cb, connection, NULL,
-		                         DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, priv->connect_hash,
-		                         DBUS_TYPE_G_MAP_OF_VARIANT, details,
-		                         G_TYPE_INVALID);
+		_LOGD ("Allowing interactive secrets as all agents have that capability");
+
+		g_variant_builder_init (&details, G_VARIANT_TYPE_VARDICT);
+		g_dbus_proxy_call (priv->proxy,
+		                   "ConnectInteractive",
+		                   g_variant_new ("(@a{sa{sv}}a{sv})", priv->connect_hash, &details),
+		                   G_DBUS_CALL_FLAGS_NONE,
+		                   -1,
+		                   priv->cancellable,
+		                   (GAsyncReadyCallback) connect_interactive_cb,
+		                   self);
 	} else {
-		nm_log_dbg (LOGD_VPN, "Calling old Connect function as not all agents support interactive secrets");
-		dbus_g_proxy_begin_call (priv->proxy, "Connect",
-		                         connect_cb, connection, NULL,
-		                         DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, priv->connect_hash,
-		                         G_TYPE_INVALID);
+		_LOGD ("Calling old Connect function as not all agents support interactive secrets");
+		g_dbus_proxy_call (priv->proxy,
+		                   "Connect",
+		                   g_variant_new ("(@a{sa{sv}})", priv->connect_hash),
+		                   G_DBUS_CALL_FLAGS_NONE,
+		                   -1,
+		                   priv->cancellable,
+		                   (GAsyncReadyCallback) connect_cb,
+		                   self);
 	}
-	g_hash_table_destroy (details);
 
-	_set_vpn_state (connection, STATE_CONNECT, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+	_set_vpn_state (self, STATE_CONNECT, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
 }
 
-void
-nm_vpn_connection_activate (NMVpnConnection *connection)
+static void
+failure_cb (GDBusProxy *proxy,
+            guint32     reason,
+            gpointer    user_data)
+{
+	NMVpnConnection *self = NM_VPN_CONNECTION (user_data);
+
+	plugin_failed (self, reason);
+}
+
+static void
+state_changed_cb (GDBusProxy *proxy,
+                  guint32     new_service_state,
+                  gpointer    user_data)
+{
+	NMVpnConnection *self = NM_VPN_CONNECTION (user_data);
+
+	plugin_state_changed (self, new_service_state);
+}
+
+static void
+secrets_required_cb (GDBusProxy  *proxy,
+                     const char  *message,
+                     const char **secrets,
+                     gpointer     user_data)
+{
+	NMVpnConnection *self = NM_VPN_CONNECTION (user_data);
+
+	plugin_interactive_secrets_required (self, message, secrets);
+}
+
+static void
+config_cb (GDBusProxy *proxy,
+           GVariant   *dict,
+           gpointer    user_data)
+{
+	NMVpnConnection *self = NM_VPN_CONNECTION (user_data);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+	/* Only list to this signals during and after connection */
+	if (priv->vpn_state >= STATE_NEED_AUTH)
+		nm_vpn_connection_config_get (self, dict);
+}
+
+static void
+ip4_config_cb (GDBusProxy *proxy,
+               GVariant   *dict,
+               gpointer    user_data)
+{
+	NMVpnConnection *self = NM_VPN_CONNECTION (user_data);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+	/* Only list to this signals during and after connection */
+	if (priv->vpn_state >= STATE_NEED_AUTH)
+		nm_vpn_connection_ip4_config_get (self, dict);
+}
+
+static void
+ip6_config_cb (GDBusProxy *proxy,
+               GVariant   *dict,
+               gpointer    user_data)
+{
+	NMVpnConnection *self = NM_VPN_CONNECTION (user_data);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+	/* Only list to this signals during and after connection */
+	if (priv->vpn_state >= STATE_NEED_AUTH)
+		nm_vpn_connection_ip6_config_get (self, dict);
+}
+
+static void
+_name_owner_changed (GObject *object,
+                     GParamSpec *pspec,
+                     gpointer user_data)
+{
+	NMVpnConnection *self = NM_VPN_CONNECTION (user_data);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+	char *owner;
+
+	owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object));
+
+	if (owner && !priv->service_running) {
+		/* service appeared */
+		priv->service_running = TRUE;
+		_LOGI ("Saw the service appear; activating connection");
+
+		/* No need to wait for the timeout any longer */
+		nm_clear_g_source (&priv->start_timeout);
+
+		/* Expect success because the VPN service has already appeared */
+		_nm_dbus_signal_connect (priv->proxy, "Failure", G_VARIANT_TYPE ("(u)"),
+		                         G_CALLBACK (failure_cb), self);
+		_nm_dbus_signal_connect (priv->proxy, "StateChanged", G_VARIANT_TYPE ("(u)"),
+		                         G_CALLBACK (state_changed_cb), self);
+		_nm_dbus_signal_connect (priv->proxy, "SecretsRequired", G_VARIANT_TYPE ("(sas)"),
+		                         G_CALLBACK (secrets_required_cb), self);
+		_nm_dbus_signal_connect (priv->proxy, "Config", G_VARIANT_TYPE ("(a{sv})"),
+		                         G_CALLBACK (config_cb), self);
+		_nm_dbus_signal_connect (priv->proxy, "Ip4Config", G_VARIANT_TYPE ("(a{sv})"),
+		                         G_CALLBACK (ip4_config_cb), self);
+		_nm_dbus_signal_connect (priv->proxy, "Ip6Config", G_VARIANT_TYPE ("(a{sv})"),
+		                         G_CALLBACK (ip6_config_cb), self);
+
+		_set_vpn_state (self, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+
+		/* 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, NULL);
+	} else if (!owner && priv->service_running) {
+		/* service went away */
+		priv->service_running = FALSE;
+		_LOGI ("VPN service disappeared");
+		nm_vpn_connection_disconnect (self, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED, FALSE);
+	}
+
+	g_free (owner);
+}
+
+
+static gboolean
+_daemon_exec_timeout (gpointer data)
+{
+	NMVpnConnection *self = NM_VPN_CONNECTION (data);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+	_LOGW ("Timed out waiting for the service to start");
+	priv->start_timeout = 0;
+	nm_vpn_connection_disconnect (self, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT, FALSE);
+
+	g_object_unref (self);
+
+	return G_SOURCE_REMOVE;
+}
+
+static gboolean
+nm_vpn_service_daemon_exec (NMVpnConnection *self, GError **error)
 {
 	NMVpnConnectionPrivate *priv;
-	NMSettingVpn *s_vpn;
-	DBusGConnection *bus;
+	GPid pid;
+	char *vpn_argv[4];
+	gboolean success = FALSE;
+	GError *spawn_error = NULL;
+	int i = 0;
 
-	g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (self), FALSE);
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
-	priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	vpn_argv[i++] = (char *) nm_vpn_plugin_info_get_program (priv->plugin_info);
+	if (nm_vpn_plugin_info_supports_multiple (priv->plugin_info)) {
+		vpn_argv[i++] = "--bus-name";
+		vpn_argv[i++] = priv->bus_name;
+	}
+	vpn_argv[i] = NULL;
+	g_assert (vpn_argv[0]);
 
-	s_vpn = nm_connection_get_setting_vpn (priv->connection);
-	g_assert (s_vpn);
-	priv->connection_can_persist = nm_setting_vpn_get_persistent (s_vpn);
+	success = g_spawn_async (NULL, vpn_argv, NULL, 0, nm_utils_setpgid, NULL, &pid, &spawn_error);
 
-	_set_vpn_state (connection, STATE_PREPARE, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
-
-	bus = nm_dbus_manager_get_connection (nm_dbus_manager_get ());
-	priv->proxy = dbus_g_proxy_new_for_name (bus,
-	                                         nm_vpn_connection_get_service (connection),
-	                                         NM_VPN_DBUS_PLUGIN_PATH,
-	                                         NM_VPN_DBUS_PLUGIN_INTERFACE);
-
-	dbus_g_proxy_add_signal (priv->proxy, "Failure", G_TYPE_UINT, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (priv->proxy, "Failure",
-	                             G_CALLBACK (plugin_failed),
-	                             connection, NULL);
-
-	/* StateChanged signal */
-	dbus_g_proxy_add_signal (priv->proxy, "StateChanged", G_TYPE_UINT, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (priv->proxy, "StateChanged",
-	                             G_CALLBACK (plugin_state_changed),
-	                             connection, NULL);
-
-	dbus_g_object_register_marshaller (g_cclosure_marshal_generic,
-	                                   G_TYPE_NONE, G_TYPE_STRING, G_TYPE_STRV, G_TYPE_INVALID);
-	dbus_g_proxy_add_signal (priv->proxy, "SecretsRequired", G_TYPE_STRING, G_TYPE_STRV, G_TYPE_INVALID);
-	dbus_g_proxy_connect_signal (priv->proxy, "SecretsRequired",
-	                             G_CALLBACK (plugin_interactive_secrets_required),
-	                             connection, NULL);
-
-	_set_vpn_state (connection, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
-
-	/* 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, NULL);
+	if (success) {
+		_LOGI ("Started the VPN service, PID %ld", (long int) pid);
+		priv->start_timeout = g_timeout_add_seconds (5, _daemon_exec_timeout, g_object_ref (self));
+	} else {
+		g_set_error (error,
+		             NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
+		             "%s", spawn_error ? spawn_error->message : "unknown g_spawn_async() error");
+
+		if (spawn_error)
+			g_error_free (spawn_error);
+	}
+
+	return success;
 }
 
-NMConnection *
-nm_vpn_connection_get_connection (NMVpnConnection *connection)
+static void
+on_proxy_acquired (GObject *object, GAsyncResult *result, gpointer user_data)
 {
-	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+	NMVpnConnection *self;
+	NMVpnConnectionPrivate *priv;
+	gs_free_error GError *error = NULL;
+	GDBusProxy *proxy;
 
-	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->connection;
+	proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = NM_VPN_CONNECTION (user_data);
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+	if (error) {
+		_LOGE ("failed to acquire dbus proxy for VPN service: %s",
+		       error->message);
+		_set_vpn_state (self,
+		                STATE_FAILED,
+		                NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED,
+		                FALSE);
+		return;
+	}
+
+	priv->proxy = proxy;
+
+	g_signal_connect (priv->proxy, "notify::g-name-owner",
+	                  G_CALLBACK (_name_owner_changed), self);
+	_name_owner_changed (G_OBJECT (priv->proxy), NULL, self);
+
+	if (priv->service_running)
+		return;
+
+	if (!nm_vpn_service_daemon_exec (self, &error)) {
+		_LOGW ("Could not launch the VPN service. error: %s.",
+		       error->message);
+
+		nm_vpn_connection_disconnect (self, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE);
+	}
 }
 
-const char*
-nm_vpn_connection_get_connection_id (NMVpnConnection *connection)
+void
+nm_vpn_connection_activate (NMVpnConnection *self,
+                            NMVpnPluginInfo *plugin_info)
 {
-	NMConnection *c;
+	NMVpnConnectionPrivate *priv;
+	NMSettingVpn *s_vpn;
+	const char *service;
 
-	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+	g_return_if_fail (NM_IS_VPN_CONNECTION (self));
+	g_return_if_fail (NM_IS_VPN_PLUGIN_INFO (plugin_info));
 
-	c = NM_VPN_CONNECTION_GET_PRIVATE (connection)->connection;
-	return c ? nm_connection_get_id (c) : NULL;
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+	g_return_if_fail (!priv->plugin_info);
+
+	s_vpn = nm_connection_get_setting_vpn (_get_applied_connection (self));
+	g_return_if_fail (s_vpn);
+
+	service = nm_setting_vpn_get_service_type (s_vpn);
+
+	if (nm_vpn_plugin_info_supports_multiple (plugin_info)) {
+		const char *path;
+
+		path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (self));
+		if (path)
+			path = strrchr (path, '/');
+		g_return_if_fail (path);
+
+		priv->bus_name = g_strdup_printf ("%s.Connection_%s", service, &path[1]);
+	} else
+		priv->bus_name = g_strdup (service);
+
+	priv->connection_can_persist = nm_setting_vpn_get_persistent (s_vpn);
+	priv->plugin_info = g_object_ref (plugin_info);
+	priv->cancellable = g_cancellable_new ();
+
+	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+	                          G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+	                          NULL,
+	                          priv->bus_name,
+	                          NM_VPN_DBUS_PLUGIN_PATH,
+	                          NM_VPN_DBUS_PLUGIN_INTERFACE,
+	                          priv->cancellable,
+	                          (GAsyncReadyCallback) on_proxy_acquired,
+	                          self);
+
+	_set_vpn_state (self, STATE_PREPARE, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
 }
 
 NMVpnConnectionState
-nm_vpn_connection_get_vpn_state (NMVpnConnection *connection)
+nm_vpn_connection_get_vpn_state (NMVpnConnection *self)
 {
-	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NM_VPN_CONNECTION_STATE_UNKNOWN);
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (self), NM_VPN_CONNECTION_STATE_UNKNOWN);
 
-	return _state_to_nm_vpn_state (NM_VPN_CONNECTION_GET_PRIVATE (connection)->vpn_state);
+	return _state_to_nm_vpn_state (NM_VPN_CONNECTION_GET_PRIVATE (self)->vpn_state);
 }
 
 const char *
-nm_vpn_connection_get_banner (NMVpnConnection *connection)
+nm_vpn_connection_get_banner (NMVpnConnection *self)
 {
-	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (self), NULL);
 
-	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->banner;
+	return NM_VPN_CONNECTION_GET_PRIVATE (self)->banner;
 }
 
 NMIP4Config *
-nm_vpn_connection_get_ip4_config (NMVpnConnection *connection)
+nm_vpn_connection_get_ip4_config (NMVpnConnection *self)
 {
-	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (self), NULL);
 
-	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip4_config;
+	return NM_VPN_CONNECTION_GET_PRIVATE (self)->ip4_config;
 }
 
 NMIP6Config *
-nm_vpn_connection_get_ip6_config (NMVpnConnection *connection)
+nm_vpn_connection_get_ip6_config (NMVpnConnection *self)
 {
-	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (self), NULL);
 
-	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip6_config;
+	return NM_VPN_CONNECTION_GET_PRIVATE (self)->ip6_config;
 }
 
 const char *
-nm_vpn_connection_get_ip_iface (NMVpnConnection *connection)
+nm_vpn_connection_get_ip_iface (NMVpnConnection *self)
 {
-	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (self), NULL);
 
-	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip_iface;
+	return NM_VPN_CONNECTION_GET_PRIVATE (self)->ip_iface;
 }
 
 int
-nm_vpn_connection_get_ip_ifindex (NMVpnConnection *connection)
+nm_vpn_connection_get_ip_ifindex (NMVpnConnection *self)
 {
-	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), -1);
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (self), -1);
 
-	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip_ifindex;
+	return NM_VPN_CONNECTION_GET_PRIVATE (self)->ip_ifindex;
 }
 
 guint32
-nm_vpn_connection_get_ip4_internal_gateway (NMVpnConnection *connection)
+nm_vpn_connection_get_ip4_internal_gateway (NMVpnConnection *self)
 {
-	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), 0);
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (self), 0);
 
-	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip4_internal_gw;
+	return NM_VPN_CONNECTION_GET_PRIVATE (self)->ip4_internal_gw;
 }
 
 struct in6_addr *
-nm_vpn_connection_get_ip6_internal_gateway (NMVpnConnection *connection)
+nm_vpn_connection_get_ip6_internal_gateway (NMVpnConnection *self)
 {
-	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), 0);
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (self), 0);
 
-	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip6_internal_gw;
+	return NM_VPN_CONNECTION_GET_PRIVATE (self)->ip6_internal_gw;
 }
 
 void
-nm_vpn_connection_disconnect (NMVpnConnection *connection,
+nm_vpn_connection_disconnect (NMVpnConnection *self,
                               NMVpnConnectionStateReason reason,
                               gboolean quitting)
 {
-	g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
+	g_return_if_fail (NM_IS_VPN_CONNECTION (self));
 
-	_set_vpn_state (connection, STATE_DISCONNECTED, reason, quitting);
+	_set_vpn_state (self, STATE_DISCONNECTED, reason, quitting);
 }
 
 gboolean
-nm_vpn_connection_deactivate (NMVpnConnection *connection,
+nm_vpn_connection_deactivate (NMVpnConnection *self,
                               NMVpnConnectionStateReason reason,
                               gboolean quitting)
 {
 	NMVpnConnectionPrivate *priv;
 	gboolean success = FALSE;
 
-	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), FALSE);
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (self), FALSE);
 
-	priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 	if (priv->vpn_state > STATE_UNKNOWN && priv->vpn_state <= STATE_DEACTIVATING) {
-		_set_vpn_state (connection, STATE_DEACTIVATING, reason, quitting);
+		_set_vpn_state (self, STATE_DEACTIVATING, reason, quitting);
 		success = TRUE;
 	}
 	return success;
@@ -1887,127 +2170,136 @@ nm_vpn_connection_deactivate (NMVpnConnection *connection,
 /******************************************************************************/
 
 static void
-plugin_need_secrets_cb  (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
+plugin_need_secrets_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
-	NMVpnConnection *self = NM_VPN_CONNECTION (user_data);
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
-	GError *error = NULL;
-	char *setting_name;
+	NMVpnConnection *self;
+	NMVpnConnectionPrivate *priv;
+	gs_unref_variant GVariant *reply = NULL;
+	gs_free_error GError *error = NULL;
+	const char *setting_name;
+
+	reply = _nm_dbus_proxy_call_finish (proxy, result, G_VARIANT_TYPE ("(s)"), &error);
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = NM_VPN_CONNECTION (user_data);
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
-	dbus_g_proxy_end_call (proxy, call, &error,
-	                       G_TYPE_STRING, &setting_name,
-	                       G_TYPE_INVALID);
 	if (error) {
-		nm_log_err (LOGD_VPN, "(%s/%s) plugin NeedSecrets request #%d failed: %s %s",
-		            nm_connection_get_uuid (priv->connection),
-		            nm_connection_get_id (priv->connection),
-		            priv->secrets_idx + 1,
-		            g_quark_to_string (error->domain),
-		            error->message);
+		g_dbus_error_strip_remote_error (error);
+		_LOGE ("plugin NeedSecrets request #%d failed: %s",
+		       priv->secrets_idx + 1,
+		       error->message);
 		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
-		g_error_free (error);
 		return;
 	}
 
-	if (setting_name && strlen (setting_name)) {
-		/* More secrets required */
+	g_variant_get (reply, "(&s)", &setting_name);
+	if (!strlen (setting_name)) {
+		_LOGD ("service indicated no additional secrets required");
 
-		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));
-			_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
-		} 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, NULL);
-		}
+		/* No secrets required; we can start the VPN */
+		really_activate (self, priv->username);
 		return;
 	}
 
-	nm_log_dbg (LOGD_VPN, "(%s/%s) service indicated no additional secrets required",
-	            nm_connection_get_uuid (priv->connection),
-	            nm_connection_get_id (priv->connection));
-
-	/* No secrets required; we can start the VPN */
-	really_activate (self, priv->username);
+	/* More secrets required */
+	if (priv->secrets_idx == SECRETS_REQ_NEW) {
+		_LOGE ("final secrets request failed to provide sufficient secrets");
+		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
+	} else {
+		_LOGD ("service indicated additional secrets required");
+		get_secrets (self, priv->secrets_idx + 1, NULL);
+	}
 }
 
 static void
-plugin_new_secrets_cb  (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
+plugin_new_secrets_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
-	NMVpnConnection *self = NM_VPN_CONNECTION (user_data);
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
-	GError *error = NULL;
+	NMVpnConnection *self;
+	NMVpnConnectionPrivate *priv;
+	gs_unref_variant GVariant *reply = NULL;
+	gs_free_error GError *error = NULL;
 
-	if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) {
-		_set_vpn_state (self, STATE_CONNECT, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
-	} else {
-		nm_log_err (LOGD_VPN, "(%s/%s) sending new secrets to the plugin failed: %s %s",
-		            nm_connection_get_uuid (priv->connection),
-		            nm_connection_get_id (priv->connection),
-		            g_quark_to_string (error->domain),
-		            error->message);
+	reply = g_dbus_proxy_call_finish (proxy, result, &error);
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = NM_VPN_CONNECTION (user_data);
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+	if (error) {
+		g_dbus_error_strip_remote_error (error);
+		_LOGE ("sending new secrets to the plugin failed: %s",
+		       error->message);
 		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
-		g_error_free (error);
-	}
+	} else
+		_set_vpn_state (self, STATE_CONNECT, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
 }
 
 static void
 get_secrets_cb (NMSettingsConnection *connection,
-                guint32 call_id,
+                NMSettingsConnectionCallId call_id,
                 const char *agent_username,
                 const char *setting_name,
                 GError *error,
                 gpointer user_data)
 {
 	NMVpnConnection *self = NM_VPN_CONNECTION (user_data);
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
-	GHashTable *hash;
+	NMVpnConnectionPrivate *priv;
+	GVariant *dict;
+
+	g_return_if_fail (NM_IS_VPN_CONNECTION (self));
+
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
-	g_return_if_fail (NM_CONNECTION (connection) == priv->connection);
+	g_return_if_fail (connection && connection == _get_settings_connection (self, FALSE));
 	g_return_if_fail (call_id == priv->secrets_id);
 
-	priv->secrets_id = 0;
+	priv->secrets_id = NULL;
+
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
 
 	if (error && priv->secrets_idx >= SECRETS_REQ_NEW) {
-		nm_log_err (LOGD_VPN, "Failed to request VPN secrets #%d: (%d) %s",
-		            priv->secrets_idx + 1, error->code, error->message);
+		_LOGE ("Failed to request VPN secrets #%d: (%d) %s",
+		       priv->secrets_idx + 1, error->code, error->message);
 		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
-	} else {
-		/* Cache the username for later */
-		if (agent_username) {
-			g_free (priv->username);
-			priv->username = g_strdup (agent_username);
-		}
+		return;
+	}
 
-		hash = _hash_with_username (priv->connection, priv->username);
+	/* Cache the username for later */
+	if (agent_username) {
+		g_free (priv->username);
+		priv->username = g_strdup (agent_username);
+	}
 
-		if (priv->secrets_idx == SECRETS_REQ_INTERACTIVE) {
-			nm_log_dbg (LOGD_VPN, "(%s/%s) sending secrets to the plugin",
-			            nm_connection_get_uuid (priv->connection),
-			            nm_connection_get_id (priv->connection));
+	dict = _hash_with_username (_get_applied_connection (self), priv->username);
 
-			/* Send the secrets back to the plugin */
-			dbus_g_proxy_begin_call (priv->proxy, "NewSecrets",
-			                         plugin_new_secrets_cb, self, NULL,
-			                         DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, hash,
-			                         G_TYPE_INVALID);
-		} else {
-			nm_log_dbg (LOGD_VPN, "(%s/%s) asking service if additional secrets are required",
-			            nm_connection_get_uuid (priv->connection),
-			            nm_connection_get_id (priv->connection));
-
-			/* Ask the VPN service if more secrets are required */
-			dbus_g_proxy_begin_call (priv->proxy, "NeedSecrets",
-			                         plugin_need_secrets_cb, self, NULL,
-			                         DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, hash,
-			                         G_TYPE_INVALID);
-		}
+	if (priv->secrets_idx == SECRETS_REQ_INTERACTIVE) {
+		_LOGD ("sending secrets to the plugin");
 
-		g_hash_table_destroy (hash);
+		/* Send the secrets back to the plugin */
+		g_dbus_proxy_call (priv->proxy,
+		                   "NewSecrets",
+		                   g_variant_new ("(@a{sa{sv}})", dict),
+		                   G_DBUS_CALL_FLAGS_NONE,
+		                   -1,
+		                   priv->cancellable,
+		                   (GAsyncReadyCallback) plugin_new_secrets_cb,
+		                   self);
+	} else {
+		_LOGD ("asking service if additional secrets are required");
+
+		/* Ask the VPN service if more secrets are required */
+		g_dbus_proxy_call (priv->proxy,
+		                   "NeedSecrets",
+		                   g_variant_new ("(@a{sa{sv}})", dict),
+		                   G_DBUS_CALL_FLAGS_NONE,
+		                   -1,
+		                   priv->cancellable,
+		                   (GAsyncReadyCallback) plugin_need_secrets_cb,
+		                   self);
 	}
 }
 
@@ -2018,15 +2310,14 @@ get_secrets (NMVpnConnection *self,
 {
 	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 	NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE;
-	GError *error = NULL;
 
 	g_return_if_fail (secrets_idx < SECRETS_REQ_LAST);
 	priv->secrets_idx = secrets_idx;
 
-	nm_log_dbg (LOGD_VPN, "(%s/%s) requesting VPN secrets pass #%d",
-	            nm_connection_get_uuid (priv->connection),
-	            nm_connection_get_id (priv->connection),
-	            priv->secrets_idx + 1);
+	cancel_get_secrets (self);
+
+	_LOGD ("requesting VPN secrets pass #%d",
+	       priv->secrets_idx + 1);
 
 	switch (priv->secrets_idx) {
 	case SECRETS_REQ_SYSTEM:
@@ -2046,44 +2337,35 @@ get_secrets (NMVpnConnection *self,
 	if (nm_active_connection_get_user_requested (NM_ACTIVE_CONNECTION (self)))
 		flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED;
 
-	priv->secrets_id = nm_settings_connection_get_secrets (NM_SETTINGS_CONNECTION (priv->connection),
+	priv->secrets_id = nm_settings_connection_get_secrets (_get_settings_connection (self, FALSE),
+	                                                       _get_applied_connection (self),
 	                                                       nm_active_connection_get_subject (NM_ACTIVE_CONNECTION (self)),
 	                                                       NM_SETTING_VPN_SETTING_NAME,
 	                                                       flags,
 	                                                       hints,
 	                                                       get_secrets_cb,
-	                                                       self,
-	                                                       &error);
-	if (!priv->secrets_id) {
-		if (error) {
-			nm_log_err (LOGD_VPN, "failed to request VPN secrets #%d: (%d) %s",
-			            priv->secrets_idx + 1, error->code, error->message);
-		}
-		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
-		g_clear_error (&error);
-	}
+	                                                       self);
+	g_return_if_fail (priv->secrets_id);
 }
 
 static void
-plugin_interactive_secrets_required (DBusGProxy *proxy,
+plugin_interactive_secrets_required (NMVpnConnection *self,
                                      const char *message,
-                                     const char **secrets,
-                                     gpointer user_data)
+                                     const char **secrets)
 {
-	NMVpnConnection *connection = NM_VPN_CONNECTION (user_data);
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 	guint32 secrets_len = secrets ? g_strv_length ((char **) secrets) : 0;
 	char **hints;
 	guint32 i;
 
-	nm_log_info (LOGD_VPN, "VPN plugin requested secrets; state %s (%d)",
-	             vpn_state_to_string (priv->vpn_state), priv->vpn_state);
+	_LOGI ("VPN plugin: requested secrets; state %s (%d)",
+	       vpn_state_to_string (priv->vpn_state), priv->vpn_state);
 
 	g_return_if_fail (priv->vpn_state == STATE_CONNECT ||
 	                  priv->vpn_state == STATE_NEED_AUTH);
 
 	priv->secrets_idx = SECRETS_REQ_INTERACTIVE;
-	_set_vpn_state (connection, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+	_set_vpn_state (self, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
 
 	/* Copy hints and add message to the end */
 	hints = g_malloc0 (sizeof (char *) * (secrets_len + 2));
@@ -2092,7 +2374,7 @@ plugin_interactive_secrets_required (DBusGProxy *proxy,
 	if (message)
 		hints[i] = g_strdup_printf ("x-vpn-message:%s", message);
 
-	get_secrets (connection, SECRETS_REQ_INTERACTIVE, (const char **) hints);
+	get_secrets (self, SECRETS_REQ_INTERACTIVE, (const char **) hints);
 	g_strfreev (hints);
 }
 
@@ -2120,10 +2402,8 @@ device_changed (NMActiveConnection *active,
 	 * out that connectivity is down and start its reconnect attempt if it
 	 * needs to.
 	 */
-	if (old_device) {
-		nm_device_set_vpn4_config (old_device, NULL);
-		nm_device_set_vpn6_config (old_device, NULL);
-	}
+	if (old_device)
+		remove_parent_device_config (NM_VPN_CONNECTION (active), old_device);
 
 	if (new_device)
 		apply_parent_device_config (NM_VPN_CONNECTION (active));
@@ -2138,50 +2418,39 @@ nm_vpn_connection_init (NMVpnConnection *self)
 
 	priv->vpn_state = STATE_WAITING;
 	priv->secrets_idx = SECRETS_REQ_SYSTEM;
-}
-
-static void
-constructed (GObject *object)
-{
-	NMConnection *connection;
-
-	G_OBJECT_CLASS (nm_vpn_connection_parent_class)->constructed (object);
-
-	connection = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (object));
-	NM_VPN_CONNECTION_GET_PRIVATE (object)->connection = g_object_ref (connection);
+	priv->default_route_manager = g_object_ref (nm_default_route_manager_get ());
+	priv->route_manager = g_object_ref (nm_route_manager_get ());
 }
 
 static void
 dispose (GObject *object)
 {
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object);
+	NMVpnConnection *self = NM_VPN_CONNECTION (object);
+	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
-	if (priv->connect_hash) {
-		g_hash_table_destroy (priv->connect_hash);
-		priv->connect_hash = NULL;
-	}
+	g_clear_pointer (&priv->connect_hash, g_variant_unref);
 
-	if (priv->connect_timeout) {
-		g_source_remove (priv->connect_timeout);
-		priv->connect_timeout = 0;
-	}
+	nm_clear_g_source (&priv->connect_timeout);
 
-	dispatcher_cleanup (NM_VPN_CONNECTION (object));
+	dispatcher_cleanup (self);
 
-	if (priv->secrets_id) {
-		nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (priv->connection),
-		                                       priv->secrets_id);
-		priv->secrets_id = 0;
-	}
+	cancel_get_secrets (self);
 
-	g_clear_object (&priv->ip4_config);
-	g_clear_object (&priv->ip6_config);
+	if (priv->cancellable) {
+		g_cancellable_cancel (priv->cancellable);
+		g_clear_object (&priv->cancellable);
+	}
+	nm_exported_object_clear_and_unexport (&priv->ip4_config);
+	nm_exported_object_clear_and_unexport (&priv->ip6_config);
 	g_clear_object (&priv->proxy);
-	g_clear_object (&priv->connection);
+	g_clear_object (&priv->plugin_info);
 
-	fw_call_cleanup (NM_VPN_CONNECTION (object));
+	fw_call_cleanup (self);
 
 	G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object);
+
+	g_clear_object (&priv->default_route_manager);
+	g_clear_object (&priv->route_manager);
 }
 
 static void
@@ -2206,7 +2475,7 @@ ip_config_valid (VpnState state)
 
 static void
 get_property (GObject *object, guint prop_id,
-		    GValue *value, GParamSpec *pspec)
+              GValue *value, GParamSpec *pspec)
 {
 	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object);
 	NMDevice *parent_dev;
@@ -2219,20 +2488,14 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_string (value, priv->banner ? priv->banner : "");
 		break;
 	case PROP_IP4_CONFIG:
-		if (ip_config_valid (priv->vpn_state) && priv->ip4_config)
-			g_value_set_boxed (value, nm_ip4_config_get_dbus_path (priv->ip4_config));
-		else
-			g_value_set_boxed (value, "/");
+		nm_utils_g_value_set_object_path (value, ip_config_valid (priv->vpn_state) ? priv->ip4_config : NULL);
 		break;
 	case PROP_IP6_CONFIG:
-		if (ip_config_valid (priv->vpn_state) && priv->ip6_config)
-			g_value_set_boxed (value, nm_ip6_config_get_dbus_path (priv->ip6_config));
-		else
-			g_value_set_boxed (value, "/");
+		nm_utils_g_value_set_object_path (value, ip_config_valid (priv->vpn_state) ? priv->ip6_config : NULL);
 		break;
 	case PROP_MASTER:
 		parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (object));
-		g_value_set_boxed (value, parent_dev ? nm_device_get_path (parent_dev) : "/");
+		nm_utils_g_value_set_object_path (value, parent_dev);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -2250,7 +2513,6 @@ nm_vpn_connection_class_init (NMVpnConnectionClass *connection_class)
 
 	/* virtual methods */
 	object_class->get_property = get_property;
-	object_class->constructed = constructed;
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 	active_class->device_state_changed = device_state_changed;
@@ -2302,8 +2564,7 @@ nm_vpn_connection_class_init (NMVpnConnectionClass *connection_class)
 		              0, NULL, NULL, NULL,
 		              G_TYPE_NONE, 0);
 
-	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
-	                                        G_TYPE_FROM_CLASS (object_class),
-	                                        &dbus_glib_nm_vpn_connection_object_info);
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (connection_class),
+	                                        NMDBUS_TYPE_VPN_CONNECTION_SKELETON,
+	                                        NULL);
 }
-
diff --git a/src/vpn-manager/nm-vpn-connection.h b/src/vpn-manager/nm-vpn-connection.h
index 0e63caac..19b0eb3f 100644
--- a/src/vpn-manager/nm-vpn-connection.h
+++ b/src/vpn-manager/nm-vpn-connection.h
@@ -22,12 +22,11 @@
 #ifndef __NETWORKMANAGER_VPN_CONNECTION_H__
 #define __NETWORKMANAGER_VPN_CONNECTION_H__
 
-#include <glib.h>
-#include <glib-object.h>
 #include "nm-vpn-dbus-interface.h"
 #include "nm-device.h"
 #include "nm-auth-subject.h"
 #include "nm-active-connection.h"
+#include "nm-vpn-plugin-info.h"
 
 #define NM_TYPE_VPN_CONNECTION            (nm_vpn_connection_get_type ())
 #define NM_VPN_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_CONNECTION, NMVpnConnection))
@@ -57,47 +56,47 @@ typedef struct {
 	NMActiveConnectionClass parent;
 
 	/* Signals */
-	void (*vpn_state_changed) (NMVpnConnection *connection,
+	void (*vpn_state_changed) (NMVpnConnection *self,
 	                           NMVpnConnectionState new_state,
 	                           NMVpnConnectionStateReason reason);
 
 	/* not exported over D-Bus */
-	void (*internal_state_changed) (NMVpnConnection *connection,
+	void (*internal_state_changed) (NMVpnConnection *self,
 	                                NMVpnConnectionState new_state,
 	                                NMVpnConnectionState old_state,
 	                                NMVpnConnectionStateReason reason);
 
-	void (*internal_failed_retry)  (NMVpnConnection *connection);
+	void (*internal_failed_retry)  (NMVpnConnection *self);
 } NMVpnConnectionClass;
 
 GType nm_vpn_connection_get_type (void);
 
-NMVpnConnection * nm_vpn_connection_new (NMConnection *connection,
+NMVpnConnection * nm_vpn_connection_new (NMSettingsConnection *settings_connection,
                                          NMDevice *parent_device,
                                          const char *specific_object,
                                          NMAuthSubject *subject);
 
-void                 nm_vpn_connection_activate        (NMVpnConnection *connection);
-NMConnection *       nm_vpn_connection_get_connection  (NMVpnConnection *connection);
-const char*          nm_vpn_connection_get_connection_id (NMVpnConnection *connection);
-NMVpnConnectionState nm_vpn_connection_get_vpn_state   (NMVpnConnection *connection);
-const char *         nm_vpn_connection_get_banner      (NMVpnConnection *connection);
+void                 nm_vpn_connection_activate        (NMVpnConnection *self,
+                                                        NMVpnPluginInfo *plugin_info);
+NMVpnConnectionState nm_vpn_connection_get_vpn_state   (NMVpnConnection *self);
+const char *         nm_vpn_connection_get_banner      (NMVpnConnection *self);
+const gchar *        nm_vpn_connection_get_service     (NMVpnConnection *self);
 
-gboolean             nm_vpn_connection_deactivate      (NMVpnConnection *connection,
+gboolean             nm_vpn_connection_deactivate      (NMVpnConnection *self,
                                                         NMVpnConnectionStateReason reason,
                                                         gboolean quitting);
-void                 nm_vpn_connection_disconnect      (NMVpnConnection *connection,
+void                 nm_vpn_connection_disconnect      (NMVpnConnection *self,
                                                         NMVpnConnectionStateReason reason,
                                                         gboolean quitting);
 
-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);
-guint32              nm_vpn_connection_get_ip4_internal_gateway (NMVpnConnection *connection);
-struct in6_addr *    nm_vpn_connection_get_ip6_internal_gateway (NMVpnConnection *connection);
+NMIP4Config *        nm_vpn_connection_get_ip4_config  (NMVpnConnection *self);
+NMIP6Config *        nm_vpn_connection_get_ip6_config  (NMVpnConnection *self);
+const char *         nm_vpn_connection_get_ip_iface    (NMVpnConnection *self);
+int                  nm_vpn_connection_get_ip_ifindex  (NMVpnConnection *self);
+guint32              nm_vpn_connection_get_ip4_internal_gateway (NMVpnConnection *self);
+struct in6_addr *    nm_vpn_connection_get_ip6_internal_gateway (NMVpnConnection *self);
 
-guint32              nm_vpn_connection_get_ip4_route_metric (NMVpnConnection *connection);
-guint32              nm_vpn_connection_get_ip6_route_metric (NMVpnConnection *connection);
+guint32              nm_vpn_connection_get_ip4_route_metric (NMVpnConnection *self);
+guint32              nm_vpn_connection_get_ip6_route_metric (NMVpnConnection *self);
 
 #endif /* __NETWORKMANAGER_VPN_CONNECTION_H__ */
diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c
index 1de9b753..45d22998 100644
--- a/src/vpn-manager/nm-vpn-manager.c
+++ b/src/vpn-manager/nm-vpn-manager.c
@@ -22,50 +22,48 @@
 #include "config.h"
 
 #include <string.h>
-#include <gio/gio.h>
 
+#include "nm-default.h"
 #include "nm-vpn-manager.h"
-#include "nm-vpn-service.h"
+#include "nm-vpn-plugin-info.h"
 #include "nm-vpn-connection.h"
 #include "nm-setting-vpn.h"
-#include "nm-dbus-manager.h"
 #include "nm-vpn-dbus-interface.h"
+#include "nm-core-internal.h"
 #include "nm-enum-types.h"
-#include "nm-logging.h"
-
-#define VPN_NAME_FILES_DIR NMCONFDIR "/VPN"
 
 G_DEFINE_TYPE (NMVpnManager, nm_vpn_manager, G_TYPE_OBJECT)
 
 #define NM_VPN_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_MANAGER, NMVpnManagerPrivate))
 
 typedef struct {
-	GHashTable *services;
-	GFileMonitor *monitor;
-	guint monitor_id;
+	GSList *plugins;
+	GFileMonitor *monitor_etc;
+	GFileMonitor *monitor_lib;
+	gulong monitor_id_etc;
+	gulong monitor_id_lib;
+
+	/* This is only used for services that don't support multiple
+	 * connections, to guard access to them. */
+	GHashTable *active_services;
 } NMVpnManagerPrivate;
 
+/******************************************************************************/
 
-static NMVpnService *
-get_service_by_namefile (NMVpnManager *self, const char *namefile)
+static void
+vpn_state_changed (NMVpnConnection *vpn,
+                   GParamSpec *pspec,
+                   NMVpnManager *manager)
 {
-	NMVpnManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
-	GHashTableIter iter;
-	gpointer data;
-
-	g_return_val_if_fail (namefile, NULL);
-	g_return_val_if_fail (g_path_is_absolute (namefile), NULL);
-
-	g_hash_table_iter_init (&iter, priv->services);
-	while (g_hash_table_iter_next (&iter, NULL, &data)) {
-		NMVpnService *candidate = NM_VPN_SERVICE (data);
-		const char *service_namefile;
-
-		service_namefile = nm_vpn_service_get_name_file (candidate);
-		if (!strcmp (namefile, service_namefile))
-			return candidate;
+	NMVpnManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
+	NMActiveConnectionState state = nm_active_connection_get_state (NM_ACTIVE_CONNECTION (vpn));
+	const char *service_name = nm_vpn_connection_get_service (vpn);
+
+	if (state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) {
+		g_hash_table_remove (priv->active_services, service_name);
+		g_signal_handlers_disconnect_by_func (vpn, vpn_state_changed, manager);
+		g_object_unref (manager);
 	}
-	return NULL;
 }
 
 gboolean
@@ -73,17 +71,16 @@ nm_vpn_manager_activate_connection (NMVpnManager *manager,
                                     NMVpnConnection *vpn,
                                     GError **error)
 {
-	NMConnection *connection;
-	NMSettingVpn *s_vpn;
-	NMVpnService *service;
+	NMVpnManagerPrivate *priv;
+	NMVpnPluginInfo *plugin_info;
 	const char *service_name;
 	NMDevice *device;
 
 	g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), FALSE);
 	g_return_val_if_fail (NM_IS_VPN_CONNECTION (vpn), FALSE);
-	g_return_val_if_fail (error != NULL, FALSE);
-	g_return_val_if_fail (*error == NULL, FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
 
+	priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
 	device = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn));
 	g_assert (device);
 	if (   nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED
@@ -93,63 +90,57 @@ nm_vpn_manager_activate_connection (NMVpnManager *manager,
 		return FALSE;
 	}
 
-	connection = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (vpn));
-	g_assert (connection);
-	s_vpn = nm_connection_get_setting_vpn (connection);
-	g_assert (s_vpn);
+	service_name = nm_vpn_connection_get_service (vpn);
 
-	service_name = nm_setting_vpn_get_service_type (s_vpn);
-	g_assert (service_name);
-	service = g_hash_table_lookup (NM_VPN_MANAGER_GET_PRIVATE (manager)->services, service_name);
-	if (!service) {
+	plugin_info = nm_vpn_plugin_info_list_find_by_service (priv->plugins, service_name);
+	if (!plugin_info) {
 		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_AVAILABLE,
 		             "The VPN service '%s' was not installed.",
 		             service_name);
 		return FALSE;
 	}
 
-	return nm_vpn_service_activate (service, vpn, error);
-}
+	if (   !nm_vpn_plugin_info_supports_multiple (plugin_info)
+	    && g_hash_table_contains (priv->active_services, service_name)) {
+		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_NOT_AVAILABLE,
+		             "The '%s' plugin only supports a single active connection.",
+		             nm_vpn_plugin_info_get_name (plugin_info));
+		return FALSE;
+	}
 
-gboolean
-nm_vpn_manager_deactivate_connection (NMVpnManager *self,
-                                      NMVpnConnection *connection,
-                                      NMVpnConnectionStateReason reason)
-{
-	return nm_vpn_connection_deactivate (connection, reason, FALSE);
+	nm_vpn_connection_activate (vpn, plugin_info);
+
+	if (!nm_vpn_plugin_info_supports_multiple (plugin_info)) {
+		/* Block activations of the connections of the same service type. */
+		g_hash_table_add (priv->active_services, g_strdup (service_name));
+		g_signal_connect (vpn, "notify::" NM_ACTIVE_CONNECTION_STATE,
+		                  G_CALLBACK (vpn_state_changed),
+		                  g_object_ref (manager));
+	}
+
+	return TRUE;
 }
 
+/******************************************************************************/
+
 static void
-try_add_service (NMVpnManager *self, const char *namefile)
+try_add_plugin (NMVpnManager *self, NMVpnPluginInfo *plugin_info)
 {
 	NMVpnManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
-	NMVpnService *service = NULL;
-	GHashTableIter iter;
-	GError *error = NULL;
-	const char *service_name;
-
-	g_return_if_fail (g_path_is_absolute (namefile));
+	const char *program;
 
-	/* Make sure we don't add dupes */
-	g_hash_table_iter_init (&iter, priv->services);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &service)) {
-		if (g_strcmp0 (namefile, nm_vpn_service_get_name_file (service)) == 0)
-			return;
-	}
+	program = nm_vpn_plugin_info_get_program (plugin_info);
+	if (!program || !*program)
+		return;
 
-	/* New service */
-	service = nm_vpn_service_new (namefile, &error);
-	if (service) {
-		service_name = nm_vpn_service_get_dbus_service (service);
-		g_hash_table_insert (priv->services, (char *) service_name, service);
-		nm_log_info (LOGD_VPN, "VPN: loaded %s", service_name);
-	} else {
-		nm_log_warn (LOGD_VPN, "failed to load VPN service file %s: (%d) %s",
-		             namefile,
-		             error ? error->code : -1,
-		             error && error->message ? error->message : "(unknown)");
-		g_clear_error (&error);
-	}
+	/* Make sure we don't add dupes.
+	 * We don't really allow reload of the same file. What we do allow is however to
+	 * delete a file and re-add it. */
+	if (nm_vpn_plugin_info_list_find_by_filename (priv->plugins,
+	                                              nm_vpn_plugin_info_get_filename (plugin_info)))
+		return;
+	if (!nm_vpn_plugin_info_list_add (&priv->plugins, plugin_info, NULL))
+		return;
 }
 
 static void
@@ -161,41 +152,55 @@ vpn_dir_changed (GFileMonitor *monitor,
 {
 	NMVpnManager *self = NM_VPN_MANAGER (user_data);
 	NMVpnManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
-	NMVpnService *service;
-	char *path;
+	NMVpnPluginInfo *plugin_info;
+	gs_free char *path = NULL;
+	GError *error = NULL;
 
 	path = g_file_get_path (file);
-	if (!g_str_has_suffix (path, ".name")) {
-		g_free (path);
+	if (!nm_vpn_plugin_info_validate_filename (path))
 		return;
-	}
 
 	switch (event_type) {
 	case G_FILE_MONITOR_EVENT_DELETED:
-		nm_log_dbg (LOGD_VPN, "service file %s deleted", path);
-
-		service = get_service_by_namefile (self, path);
-		if (service) {
-			const char *service_name = nm_vpn_service_get_dbus_service (service);
+		plugin_info = nm_vpn_plugin_info_list_find_by_filename (priv->plugins, path);
+		if (!plugin_info)
+			break;
 
-			/* Stop active VPN connections and destroy the service */
-			nm_vpn_service_stop_connections (service, FALSE,
-			                                 NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
-			nm_log_info (LOGD_VPN, "VPN: unloaded %s", service_name);
-			g_hash_table_remove (priv->services, service_name);
-		}
+		nm_log_dbg (LOGD_VPN, "vpn: service file %s deleted", path);
+		nm_vpn_plugin_info_list_remove (&priv->plugins, plugin_info);
 		break;
 	case G_FILE_MONITOR_EVENT_CREATED:
 	case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
-		nm_log_dbg (LOGD_VPN, "service file %s created or modified", path);
-		try_add_service (self, path);
+		plugin_info = nm_vpn_plugin_info_list_find_by_filename (priv->plugins, path);
+		if (plugin_info) {
+			/* we don't support reloading an existing plugin. You can only remove the file
+			 * and re-add it. By reloading we want to support the use case of installing
+			 * a VPN plugin after NM started. No need to burden ourself with a complete
+			 * reload. */
+			break;
+		}
+
+		if (!_nm_vpn_plugin_info_check_file (path, TRUE, TRUE, 0,
+		                                     NULL, NULL, &error)) {
+			nm_log_dbg (LOGD_VPN, "vpn: ignore changed service file %s (%s)", path, error->message);
+			g_clear_error (&error);
+			break;
+		}
+		plugin_info = nm_vpn_plugin_info_new_from_file (path, &error);
+		if (!plugin_info) {
+			nm_log_dbg (LOGD_VPN, "vpn: ignore changed service file %s due to invalid content (%s)", path, error->message);
+			g_clear_error (&error);
+			break;
+		}
+
+		nm_log_dbg (LOGD_VPN, "vpn: service file %s created or modified", path);
+		try_add_plugin (self, plugin_info);
+		g_object_unref (plugin_info);
 		break;
 	default:
-		nm_log_dbg (LOGD_VPN, "service file %s change event %d", path, event_type);
+		nm_log_dbg (LOGD_VPN, "vpn: service file %s change event %d", path, event_type);
 		break;
 	}
-
-	g_free (path);
 }
 
 /******************************************************************************/
@@ -207,50 +212,42 @@ nm_vpn_manager_init (NMVpnManager *self)
 {
 	NMVpnManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
 	GFile *file;
-	GDir *dir;
-	const char *fn;
-	char *path;
-
-	priv->services = g_hash_table_new_full (g_str_hash, g_str_equal,
-	                                        NULL, g_object_unref);
+	GSList *infos, *info;
+	const char *conf_dir_etc = _nm_vpn_plugin_info_get_default_dir_etc ();
+	const char *conf_dir_lib = _nm_vpn_plugin_info_get_default_dir_lib ();
 
 	/* Watch the VPN directory for changes */
-	file = g_file_new_for_path (VPN_NAME_FILES_DIR "/");
-	priv->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
+	file = g_file_new_for_path (conf_dir_lib);
+	priv->monitor_lib = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
 	g_object_unref (file);
-	if (priv->monitor) {
-		priv->monitor_id = g_signal_connect (priv->monitor, "changed",
-		                                     G_CALLBACK (vpn_dir_changed), self);
+	if (priv->monitor_lib) {
+		priv->monitor_id_lib = g_signal_connect (priv->monitor_lib, "changed",
+		                                         G_CALLBACK (vpn_dir_changed), self);
 	}
 
-	/* Load VPN service files */
-	dir = g_dir_open (VPN_NAME_FILES_DIR, 0, NULL);
-	if (dir) {
-		while ((fn = g_dir_read_name (dir))) {
-			/* only parse filenames that end with .name */
-			if (g_str_has_suffix (fn, ".name")) {
-				path = g_build_filename (VPN_NAME_FILES_DIR, fn, NULL);
-				try_add_service (self, path);
-				g_free (path);
-			}
-		}
-		g_dir_close (dir);
+	file = g_file_new_for_path (conf_dir_etc);
+	priv->monitor_etc = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
+	g_object_unref (file);
+	if (priv->monitor_etc) {
+		priv->monitor_id_etc = g_signal_connect (priv->monitor_etc, "changed",
+		                                         G_CALLBACK (vpn_dir_changed), self);
 	}
-}
 
-static void
-stop_all_services (NMVpnManager *self)
-{
-	NMVpnManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
-	GHashTableIter iter;
-	NMVpnService *service;
-
-	g_hash_table_iter_init (&iter, priv->services);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &service)) {
-		nm_vpn_service_stop_connections (service,
-		                                 TRUE,
-		                                 NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
-	}
+	/* first read conf_dir_lib. The name files are not really user configuration, but
+	 * plugin configuration. Hence we expect ~newer~ plugins to install their files
+	 * in /usr/lib/NetworkManager. We want to prefer those files.
+	 * In case of no-conflict, the order doesn't matter. */
+	infos = _nm_vpn_plugin_info_list_load_dir (conf_dir_lib, TRUE, 0, NULL, NULL);
+	for (info = infos; info; info = info->next)
+		try_add_plugin (self, info->data);
+	g_slist_free_full (infos, g_object_unref);
+
+	infos = _nm_vpn_plugin_info_list_load_dir (conf_dir_etc, TRUE, 0, NULL, NULL);
+	for (info = infos; info; info = info->next)
+		try_add_plugin (self, info->data);
+	g_slist_free_full (infos, g_object_unref);
+
+	priv->active_services = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 }
 
 static void
@@ -258,19 +255,25 @@ dispose (GObject *object)
 {
 	NMVpnManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (object);
 
-	if (priv->monitor) {
-		if (priv->monitor_id)
-			g_signal_handler_disconnect (priv->monitor, priv->monitor_id);
-		g_file_monitor_cancel (priv->monitor);
-		g_clear_object (&priv->monitor);
+	if (priv->monitor_etc) {
+		if (priv->monitor_id_etc)
+			g_signal_handler_disconnect (priv->monitor_etc, priv->monitor_id_etc);
+		g_file_monitor_cancel (priv->monitor_etc);
+		g_clear_object (&priv->monitor_etc);
 	}
 
-	if (priv->services) {
-		stop_all_services (NM_VPN_MANAGER (object));
-		g_hash_table_destroy (priv->services);
-		priv->services = NULL;
+	if (priv->monitor_lib) {
+		if (priv->monitor_id_lib)
+			g_signal_handler_disconnect (priv->monitor_lib, priv->monitor_id_lib);
+		g_file_monitor_cancel (priv->monitor_lib);
+		g_clear_object (&priv->monitor_lib);
 	}
 
+	while (priv->plugins)
+		nm_vpn_plugin_info_list_remove (&priv->plugins, priv->plugins->data);
+
+	g_hash_table_unref (priv->active_services);
+
 	G_OBJECT_CLASS (nm_vpn_manager_parent_class)->dispose (object);
 }
 
diff --git a/src/vpn-manager/nm-vpn-manager.h b/src/vpn-manager/nm-vpn-manager.h
index b75a6179..f557e27a 100644
--- a/src/vpn-manager/nm-vpn-manager.h
+++ b/src/vpn-manager/nm-vpn-manager.h
@@ -22,8 +22,7 @@
 #ifndef __NETWORKMANAGER_VPN_MANAGER_H__
 #define __NETWORKMANAGER_VPN_MANAGER_H__
 
-#include <glib.h>
-#include <glib-object.h>
+#include "nm-default.h"
 #include "nm-vpn-connection.h"
 
 #define NM_TYPE_VPN_MANAGER            (nm_vpn_manager_get_type ())
@@ -49,8 +48,4 @@ gboolean nm_vpn_manager_activate_connection (NMVpnManager *manager,
                                              NMVpnConnection *vpn,
                                              GError **error);
 
-gboolean nm_vpn_manager_deactivate_connection (NMVpnManager *manager,
-                                               NMVpnConnection *connection,
-                                               NMVpnConnectionStateReason reason);
-
 #endif /* __NETWORKMANAGER_VPN_MANAGER_H__ */
diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c
deleted file mode 100644
index 77220dd2..00000000
--- a/src/vpn-manager/nm-vpn-service.c
+++ /dev/null
@@ -1,385 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2005 - 2014 Red Hat, Inc.
- * Copyright (C) 2005 - 2008 Novell, Inc.
- */
-
-#include "config.h"
-
-#include <glib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <unistd.h>
-
-#include "nm-vpn-service.h"
-#include "nm-dbus-manager.h"
-#include "nm-logging.h"
-#include "nm-vpn-manager.h"
-#include "nm-glib-compat.h"
-
-G_DEFINE_TYPE (NMVpnService, nm_vpn_service, G_TYPE_OBJECT)
-
-typedef struct {
-	char *name;
-	char *dbus_service;
-	char *program;
-	char *namefile;
-
-	NMVpnConnection *active;
-	GSList *pending;
-
-	guint start_timeout;
-	gboolean service_running;
-} NMVpnServicePrivate;
-
-#define NM_VPN_SERVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_SERVICE, NMVpnServicePrivate))
-
-#define VPN_CONNECTION_GROUP "VPN Connection"
-
-static gboolean start_pending_vpn (NMVpnService *self, GError **error);
-
-NMVpnService *
-nm_vpn_service_new (const char *namefile, GError **error)
-{
-	NMVpnService *self;
-	NMVpnServicePrivate *priv;
-	GKeyFile *kf;
-
-	g_return_val_if_fail (namefile != NULL, NULL);
-	g_return_val_if_fail (g_path_is_absolute (namefile), NULL);
-
-	kf = g_key_file_new ();
-	if (!g_key_file_load_from_file (kf, namefile, G_KEY_FILE_NONE, error)) {
-		g_key_file_free (kf);
-		return NULL;
-	}
-
-	self = (NMVpnService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL);
-	priv = NM_VPN_SERVICE_GET_PRIVATE (self);
-	priv->namefile = g_strdup (namefile);
-
-	priv->dbus_service = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", error);
-	if (!priv->dbus_service)
-		goto error;
-
-	priv->program = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "program", error);
-	if (!priv->program)
-		goto error;
-
-	priv->name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "name", error);
-	if (!priv->name)
-		goto error;
-
-	priv->service_running = nm_dbus_manager_name_has_owner (nm_dbus_manager_get (), priv->dbus_service);
-
-	g_key_file_free (kf);
-	return self;
-
-error:
-	g_object_unref (self);
-	g_key_file_free (kf);
-	return NULL;
-}
-
-const char *
-nm_vpn_service_get_dbus_service (NMVpnService *service)
-{
-	g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL);
-
-	return NM_VPN_SERVICE_GET_PRIVATE (service)->dbus_service;
-}
-
-const char *
-nm_vpn_service_get_name_file (NMVpnService *service)
-{
-	g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL);
-
-	return NM_VPN_SERVICE_GET_PRIVATE (service)->namefile;
-}
-
-static void
-connection_vpn_state_changed (NMVpnConnection *connection,
-                              NMVpnConnectionState new_state,
-                              NMVpnConnectionState old_state,
-                              NMVpnConnectionStateReason reason,
-                              gpointer user_data)
-{
-	NMVpnService *self = NM_VPN_SERVICE (user_data);
-	NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
-
-	if (new_state == NM_VPN_CONNECTION_STATE_FAILED ||
-	    new_state == NM_VPN_CONNECTION_STATE_DISCONNECTED) {
-		g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_vpn_state_changed), self);
-		if (connection == priv->active) {
-			priv->active = NULL;
-			start_pending_vpn (self, NULL);
-		} else
-			priv->pending = g_slist_remove (priv->pending, connection);
-		g_object_unref (connection);
-	}
-}
-
-void
-nm_vpn_service_stop_connections (NMVpnService *service,
-                                 gboolean quitting,
-                                 NMVpnConnectionStateReason reason)
-{
-	NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
-	GSList *iter;
-
-	/* Just add priv->active to the beginning of priv->pending,
-	 * since we're going to clear priv->pending immediately anyway.
-	 */
-	if (priv->active) {
-		priv->pending = g_slist_prepend (priv->pending, priv->active);
-		priv->active = NULL;
-	}
-
-	for (iter = priv->pending; iter; iter = iter->next) {
-		NMVpnConnection *vpn = NM_VPN_CONNECTION (iter->data);
-
-		g_signal_handlers_disconnect_by_func (vpn, G_CALLBACK (connection_vpn_state_changed), service);
-		if (quitting) {
-			/* Deactivate to allow pre-down before disconnecting */
-			nm_vpn_connection_deactivate (vpn, reason, quitting);
-		}
-		nm_vpn_connection_disconnect (vpn, reason, quitting);
-		g_object_unref (vpn);
-	}
-	g_clear_pointer (&priv->pending, g_slist_free);
-}
-
-static gboolean
-_daemon_exec_timeout (gpointer data)
-{
-	NMVpnService *self = NM_VPN_SERVICE (data);
-	NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
-
-	nm_log_warn (LOGD_VPN, "VPN service '%s' start timed out", priv->name);
-	priv->start_timeout = 0;
-	nm_vpn_service_stop_connections (self, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT);
-	return G_SOURCE_REMOVE;
-}
-
-static gboolean
-nm_vpn_service_daemon_exec (NMVpnService *service, GError **error)
-{
-	NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
-	GPid pid;
-	char *vpn_argv[2];
-	gboolean success = FALSE;
-	GError *spawn_error = NULL;
-
-	g_return_val_if_fail (NM_IS_VPN_SERVICE (service), FALSE);
-
-	vpn_argv[0] = priv->program;
-	vpn_argv[1] = NULL;
-
-	success = g_spawn_async (NULL, vpn_argv, NULL, 0, nm_utils_setpgid, NULL, &pid, &spawn_error);
-	if (success) {
-		nm_log_info (LOGD_VPN, "VPN service '%s' started (%s), PID %ld",
-		             priv->name, priv->dbus_service, (long int) pid);
-		priv->start_timeout = g_timeout_add_seconds (5, _daemon_exec_timeout, service);
-	} else {
-		nm_log_warn (LOGD_VPN, "VPN service '%s': could not launch the VPN service. error: (%d) %s.",
-		             priv->name,
-		             spawn_error ? spawn_error->code : -1,
-		             spawn_error && spawn_error->message ? spawn_error->message : "(unknown)");
-
-		g_set_error (error,
-		             NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
-		             "%s", spawn_error ? spawn_error->message : "unknown g_spawn_async() error");
-
-		nm_vpn_service_stop_connections (service, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED);
-		if (spawn_error)
-			g_error_free (spawn_error);
-	}
-
-	return success;
-}
-
-static gboolean
-start_active_vpn (NMVpnService *self, GError **error)
-{
-	NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
-
-	if (!priv->active)
-		return TRUE;
-
-	if (priv->service_running) {
-		/* Just activate the VPN */
-		nm_vpn_connection_activate (priv->active);
-		return TRUE;
-	} else if (priv->start_timeout == 0) {
-		/* VPN service not running, start it */
-		nm_log_info (LOGD_VPN, "Starting VPN service '%s'...", priv->name);
-		return nm_vpn_service_daemon_exec (self, error);
-	}
-
-	/* Already started VPN service, waiting for it to appear on D-Bus */
-	return TRUE;
-}
-
-static gboolean
-start_pending_vpn (NMVpnService *self, GError **error)
-{
-	NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
-
-	g_assert (priv->active == NULL);
-
-	if (!priv->pending)
-		return TRUE;
-
-	/* Make next VPN active */
-	priv->active = g_slist_nth_data (priv->pending, 0);
-	priv->pending = g_slist_remove (priv->pending, priv->active);
-
-	return start_active_vpn (self, error);
-}
-
-gboolean
-nm_vpn_service_activate (NMVpnService *service,
-                         NMVpnConnection *vpn,
-                         GError **error)
-{
-	NMVpnServicePrivate *priv;
-
-	g_return_val_if_fail (NM_IS_VPN_SERVICE (service), FALSE);
-	g_return_val_if_fail (NM_IS_VPN_CONNECTION (vpn), FALSE);
-	g_return_val_if_fail (error != NULL, FALSE);
-	g_return_val_if_fail (*error == NULL, FALSE);
-
-	priv = NM_VPN_SERVICE_GET_PRIVATE (service);
-
-	g_signal_connect (vpn, NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED,
-	                  G_CALLBACK (connection_vpn_state_changed),
-	                  service);
-
-	/* Queue up the new VPN connection */
-	priv->pending = g_slist_append (priv->pending, g_object_ref (vpn));
-
-	/* Tell the active VPN to deactivate and wait for it to quit before we
-	 * start the next VPN.  The just-queued VPN will then be started from
-	 * connection_vpn_state_changed().
-	 */
-	if (priv->active) {
-		nm_vpn_connection_deactivate (priv->active, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED, FALSE);
-		return TRUE;
-	}
-
-	/* Otherwise start the next VPN */
-	return start_pending_vpn (service, error);
-}
-
-static void
-_name_owner_changed (NMDBusManager *mgr,
-                     const char *name,
-                     const char *old,
-                     const char *new,
-                     gpointer user_data)
-{
-	NMVpnService *service = NM_VPN_SERVICE (user_data);
-	NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
-	gboolean old_owner_good, new_owner_good, success;
-
-	if (strcmp (name, priv->dbus_service))
-		return;
-
-	/* Service changed, no need to wait for the timeout any longer */
-	if (priv->start_timeout) {
-		g_source_remove (priv->start_timeout);
-		priv->start_timeout = 0;
-	}
-
-	old_owner_good = (old && old[0]);
-	new_owner_good = (new && new[0]);
-
-	if (!old_owner_good && new_owner_good) {
-		/* service appeared */
-		priv->service_running = TRUE;
-		nm_log_info (LOGD_VPN, "VPN service '%s' appeared; activating connections", priv->name);
-		/* Expect success because the VPN service has already appeared */
-		success = start_active_vpn (service, NULL);
-		g_warn_if_fail (success);
-	} else if (old_owner_good && !new_owner_good) {
-		/* service went away */
-		priv->service_running = FALSE;
-		nm_log_info (LOGD_VPN, "VPN service '%s' disappeared", priv->name);
-		nm_vpn_service_stop_connections (service, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
-	}
-}
-
-/******************************************************************************/
-
-static void
-nm_vpn_service_init (NMVpnService *self)
-{
-	g_signal_connect (nm_dbus_manager_get (),
-	                  NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
-	                  G_CALLBACK (_name_owner_changed),
-	                  self);
-}
-
-static void
-dispose (GObject *object)
-{
-	NMVpnService *self = NM_VPN_SERVICE (object);
-	NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
-
-	if (priv->start_timeout) {
-		g_source_remove (priv->start_timeout);
-		priv->start_timeout = 0;
-	}
-
-	/* VPNService owner is required to stop connections before releasing */
-	g_assert (priv->active == NULL);
-	g_assert (priv->pending == NULL);
-
-	g_signal_handlers_disconnect_by_func (nm_dbus_manager_get (),
-	                                      G_CALLBACK (_name_owner_changed),
-	                                      self);
-
-	G_OBJECT_CLASS (nm_vpn_service_parent_class)->dispose (object);
-}
-
-static void
-finalize (GObject *object)
-{
-	NMVpnServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (object);
-
-	g_free (priv->name);
-	g_free (priv->dbus_service);
-	g_free (priv->program);
-	g_free (priv->namefile);
-
-	G_OBJECT_CLASS (nm_vpn_service_parent_class)->finalize (object);
-}
-
-static void
-nm_vpn_service_class_init (NMVpnServiceClass *service_class)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (service_class);
-
-	g_type_class_add_private (service_class, sizeof (NMVpnServicePrivate));
-
-	/* virtual methods */
-	object_class->dispose = dispose;
-	object_class->finalize = finalize;
-}
diff --git a/src/vpn-manager/nm-vpn-service.h b/src/vpn-manager/nm-vpn-service.h
deleted file mode 100644
index cafa6904..00000000
--- a/src/vpn-manager/nm-vpn-service.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2005 - 2011 Red Hat, Inc.
- * Copyright (C) 2005 - 2008 Novell, Inc.
- */
-
-#ifndef __NETWORKMANAGER_VPN_SERVICE_H__
-#define __NETWORKMANAGER_VPN_SERVICE_H__
-
-#include <glib.h>
-#include <glib-object.h>
-#include "nm-device.h"
-#include "nm-vpn-connection.h"
-
-#define NM_TYPE_VPN_SERVICE            (nm_vpn_service_get_type ())
-#define NM_VPN_SERVICE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_VPN_SERVICE, NMVpnService))
-#define NM_VPN_SERVICE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_VPN_SERVICE, NMVpnServiceClass))
-#define NM_IS_VPN_SERVICE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_VPN_SERVICE))
-#define NM_IS_VPN_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_VPN_SERVICE))
-#define NM_VPN_SERVICE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_SERVICE, NMVpnServiceClass))
-
-typedef struct {
-	GObject parent;
-} NMVpnService;
-
-typedef struct {
-	GObjectClass parent;
-} NMVpnServiceClass;
-
-GType nm_vpn_service_get_type (void);
-
-NMVpnService * nm_vpn_service_new (const char *namefile, GError **error);
-
-/* Returns the VPN service's D-Bus service name */
-const char *nm_vpn_service_get_dbus_service (NMVpnService *service);
-
-/* Returns the path of the VPN service's .name file */
-const char *nm_vpn_service_get_name_file (NMVpnService *service);
-
-gboolean nm_vpn_service_activate (NMVpnService *service,
-                                  NMVpnConnection *vpn,
-                                  GError **error);
-
-void nm_vpn_service_stop_connections (NMVpnService *service,
-                                      gboolean quitting,
-                                      NMVpnConnectionStateReason reason);
-
-#endif  /* NM_VPN_VPN_SERVICE_H */