about summary refs log tree commit diff
path: root/src/vpn-manager
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2014-07-06 03:04:17 +0200
committerMichael Biebl <biebl@debian.org>2014-07-06 03:04:17 +0200
commit3ce667b6ee6b86291bbe60ef93cba7f11a5c5400 (patch)
treeaf3daa7221e898b72f3de26eb2cf0c1b1c8661b0 /src/vpn-manager
parentf02d31d95af29678092d1ff01f714621bc9dcc0f (diff)
parent33491bc4279481db8ae47213e34a6d695a0e8830 (diff)
Merge tag 'upstream/0.9.10.0'
Upstream version 0.9.10.0
Diffstat (limited to 'src/vpn-manager')
-rw-r--r--src/vpn-manager/Makefile.am49
-rw-r--r--src/vpn-manager/Makefile.in784
-rw-r--r--src/vpn-manager/nm-vpn-connection.c1383
-rw-r--r--src/vpn-manager/nm-vpn-connection.h15
-rw-r--r--src/vpn-manager/nm-vpn-manager.c202
-rw-r--r--src/vpn-manager/nm-vpn-manager.h10
-rw-r--r--src/vpn-manager/nm-vpn-service.c390
-rw-r--r--src/vpn-manager/nm-vpn-service.h18
8 files changed, 1148 insertions, 1703 deletions
diff --git a/src/vpn-manager/Makefile.am b/src/vpn-manager/Makefile.am
deleted file mode 100644
index 94809ddd..00000000
--- a/src/vpn-manager/Makefile.am
+++ /dev/null
@@ -1,49 +0,0 @@
-noinst_LTLIBRARIES = libvpn-manager.la
-
-libvpn_manager_la_SOURCES = \
-	nm-vpn-manager.c \
-	nm-vpn-manager.h \
-	nm-vpn-service.c \
-	nm-vpn-service.h \
-	nm-vpn-connection.c \
-	nm-vpn-connection.h
-
-libvpn_manager_la_CPPFLAGS = \
-	-I${top_srcdir} \
-	-I${top_builddir}/include \
-	-I${top_srcdir}/include \
-	-I${top_builddir}/libnm-util \
-	-I${top_srcdir}/libnm-util \
-	-I${top_builddir}/src/generated \
-	-I${top_srcdir}/src/generated \
-	-I${top_srcdir}/src/logging \
-	-I${top_srcdir}/src/posix-signals \
-	-I${top_srcdir}/src \
-	-I${top_srcdir}/src/dns-manager \
-	$(LIBNL_CFLAGS) \
-	$(DBUS_CFLAGS) \
-	$(GLIB_CFLAGS) \
-	-DVPN_NAME_FILES_DIR=\""$(sysconfdir)/NetworkManager/VPN"\"
-
-libvpn_manager_la_LIBADD = \
-	$(top_builddir)/src/generated/libnm-generated.la \
-	$(top_builddir)/src/logging/libnm-logging.la \
-	$(top_builddir)/src/posix-signals/libnm-posix-signals.la \
-	$(top_builddir)/libnm-util/libnm-util.la \
-	$(LIBNL_LIBS) \
-	$(DBUS_LIBS) \
-	$(GLIB_LIBS)
-
-nm-vpn-connection-glue.h: $(top_srcdir)/introspection/nm-vpn-connection.xml
-	$(AM_V_GEN) dbus-binding-tool --prefix=nm_vpn_connection --mode=glib-server --output=$@ $<
-
-nm-vpn-plugin-bindings.h: $(top_srcdir)/introspection/nm-vpn-plugin.xml
-	$(AM_V_GEN) dbus-binding-tool --prefix=nm_vpn_plugin --mode=glib-client --output=$@ $<
-
-
-BUILT_SOURCES = \
-	nm-vpn-connection-glue.h \
-	nm-vpn-plugin-bindings.h
-
-CLEANFILES = $(BUILT_SOURCES)
-
diff --git a/src/vpn-manager/Makefile.in b/src/vpn-manager/Makefile.in
deleted file mode 100644
index 94e33078..00000000
--- a/src/vpn-manager/Makefile.in
+++ /dev/null
@@ -1,784 +0,0 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
-# @configure_input@
-
-# Copyright (C) 1994-2013 Free Software Foundation, Inc.
-
-# This Makefile.in is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-@SET_MAKE@
-
-VPATH = @srcdir@
-am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
-am__make_running_with_option = \
-  case $${target_option-} in \
-      ?) ;; \
-      *) echo "am__make_running_with_option: internal error: invalid" \
-              "target option '$${target_option-}' specified" >&2; \
-         exit 1;; \
-  esac; \
-  has_opt=no; \
-  sane_makeflags=$$MAKEFLAGS; \
-  if $(am__is_gnu_make); then \
-    sane_makeflags=$$MFLAGS; \
-  else \
-    case $$MAKEFLAGS in \
-      *\\[\ \	]*) \
-        bs=\\; \
-        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
-          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
-    esac; \
-  fi; \
-  skip_next=no; \
-  strip_trailopt () \
-  { \
-    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
-  }; \
-  for flg in $$sane_makeflags; do \
-    test $$skip_next = yes && { skip_next=no; continue; }; \
-    case $$flg in \
-      *=*|--*) continue;; \
-        -*I) strip_trailopt 'I'; skip_next=yes;; \
-      -*I?*) strip_trailopt 'I';; \
-        -*O) strip_trailopt 'O'; skip_next=yes;; \
-      -*O?*) strip_trailopt 'O';; \
-        -*l) strip_trailopt 'l'; skip_next=yes;; \
-      -*l?*) strip_trailopt 'l';; \
-      -[dEDm]) skip_next=yes;; \
-      -[JT]) skip_next=yes;; \
-    esac; \
-    case $$flg in \
-      *$$target_option*) has_opt=yes; break;; \
-    esac; \
-  done; \
-  test $$has_opt = yes
-am__make_dryrun = (target_option=n; $(am__make_running_with_option))
-am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
-pkgdatadir = $(datadir)/@PACKAGE@
-pkgincludedir = $(includedir)/@PACKAGE@
-pkglibdir = $(libdir)/@PACKAGE@
-pkglibexecdir = $(libexecdir)/@PACKAGE@
-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
-install_sh_DATA = $(install_sh) -c -m 644
-install_sh_PROGRAM = $(install_sh) -c
-install_sh_SCRIPT = $(install_sh) -c
-INSTALL_HEADER = $(INSTALL_DATA)
-transform = $(program_transform_name)
-NORMAL_INSTALL = :
-PRE_INSTALL = :
-POST_INSTALL = :
-NORMAL_UNINSTALL = :
-PRE_UNINSTALL = :
-POST_UNINSTALL = :
-build_triplet = @build@
-host_triplet = @host@
-subdir = src/vpn-manager
-DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
-	$(top_srcdir)/build-aux/depcomp
-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
-	$(top_srcdir)/m4/gettext.m4 \
-	$(top_srcdir)/m4/gnome-code-coverage.m4 \
-	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
-	$(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \
-	$(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \
-	$(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
-	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
-	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
-	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
-	$(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
-	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
-	$(ACLOCAL_M4)
-mkinstalldirs = $(install_sh) -d
-CONFIG_HEADER = $(top_builddir)/config.h
-CONFIG_CLEAN_FILES =
-CONFIG_CLEAN_VPATH_FILES =
-LTLIBRARIES = $(noinst_LTLIBRARIES)
-am__DEPENDENCIES_1 =
-libvpn_manager_la_DEPENDENCIES =  \
-	$(top_builddir)/src/generated/libnm-generated.la \
-	$(top_builddir)/src/logging/libnm-logging.la \
-	$(top_builddir)/src/posix-signals/libnm-posix-signals.la \
-	$(top_builddir)/libnm-util/libnm-util.la $(am__DEPENDENCIES_1) \
-	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
-am_libvpn_manager_la_OBJECTS = libvpn_manager_la-nm-vpn-manager.lo \
-	libvpn_manager_la-nm-vpn-service.lo \
-	libvpn_manager_la-nm-vpn-connection.lo
-libvpn_manager_la_OBJECTS = $(am_libvpn_manager_la_OBJECTS)
-AM_V_lt = $(am__v_lt_@AM_V@)
-am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
-am__v_lt_0 = --silent
-am__v_lt_1 = 
-AM_V_P = $(am__v_P_@AM_V@)
-am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
-am__v_P_0 = false
-am__v_P_1 = :
-AM_V_GEN = $(am__v_GEN_@AM_V@)
-am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
-am__v_GEN_0 = @echo "  GEN     " $@;
-am__v_GEN_1 = 
-AM_V_at = $(am__v_at_@AM_V@)
-am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
-am__v_at_0 = @
-am__v_at_1 = 
-DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
-depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
-am__depfiles_maybe = depfiles
-am__mv = mv -f
-COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
-	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
-	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
-	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
-	$(AM_CFLAGS) $(CFLAGS)
-AM_V_CC = $(am__v_CC_@AM_V@)
-am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
-am__v_CC_0 = @echo "  CC      " $@;
-am__v_CC_1 = 
-CCLD = $(CC)
-LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
-	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
-	$(AM_LDFLAGS) $(LDFLAGS) -o $@
-AM_V_CCLD = $(am__v_CCLD_@AM_V@)
-am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
-am__v_CCLD_0 = @echo "  CCLD    " $@;
-am__v_CCLD_1 = 
-SOURCES = $(libvpn_manager_la_SOURCES)
-DIST_SOURCES = $(libvpn_manager_la_SOURCES)
-am__can_run_installinfo = \
-  case $$AM_UPDATE_INFO_DIR in \
-    n|no|NO) false;; \
-    *) (install-info --version) >/dev/null 2>&1;; \
-  esac
-am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
-# Read a list of newline-separated strings from the standard input,
-# and print each of them once, without duplicates.  Input order is
-# *not* preserved.
-am__uniquify_input = $(AWK) '\
-  BEGIN { nonempty = 0; } \
-  { items[$$0] = 1; nonempty = 1; } \
-  END { if (nonempty) { for (i in items) print i; }; } \
-'
-# Make sure the list of sources is unique.  This is necessary because,
-# e.g., the same source file might be shared among _SOURCES variables
-# for different programs/libraries.
-am__define_uniq_tagged_files = \
-  list='$(am__tagged_files)'; \
-  unique=`for i in $$list; do \
-    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-  done | $(am__uniquify_input)`
-ETAGS = etags
-CTAGS = ctags
-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
-ACLOCAL = @ACLOCAL@
-ALL_LINGUAS = @ALL_LINGUAS@
-AMTAR = @AMTAR@
-AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
-AR = @AR@
-AUTOCONF = @AUTOCONF@
-AUTOHEADER = @AUTOHEADER@
-AUTOMAKE = @AUTOMAKE@
-AWK = @AWK@
-CC = @CC@
-CCDEPMODE = @CCDEPMODE@
-CFLAGS = @CFLAGS@
-CKDB_PATH = @CKDB_PATH@
-CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@
-CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
-CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@
-CPP = @CPP@
-CPPFLAGS = @CPPFLAGS@
-CXX = @CXX@
-CXXCPP = @CXXCPP@
-CXXDEPMODE = @CXXDEPMODE@
-CXXFLAGS = @CXXFLAGS@
-CYGPATH_W = @CYGPATH_W@
-DATADIRNAME = @DATADIRNAME@
-DBUS_CFLAGS = @DBUS_CFLAGS@
-DBUS_LIBS = @DBUS_LIBS@
-DBUS_SYS_DIR = @DBUS_SYS_DIR@
-DEFS = @DEFS@
-DEPDIR = @DEPDIR@
-DHCLIENT_PATH = @DHCLIENT_PATH@
-DHCPCD_PATH = @DHCPCD_PATH@
-DLLTOOL = @DLLTOOL@
-DNSMASQ_PATH = @DNSMASQ_PATH@
-DSYMUTIL = @DSYMUTIL@
-DUMPBIN = @DUMPBIN@
-ECHO_C = @ECHO_C@
-ECHO_N = @ECHO_N@
-ECHO_T = @ECHO_T@
-EGREP = @EGREP@
-EXEEXT = @EXEEXT@
-FGREP = @FGREP@
-GENHTML = @GENHTML@
-GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
-GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
-GLIB_CFLAGS = @GLIB_CFLAGS@
-GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
-GLIB_LIBS = @GLIB_LIBS@
-GLIB_MAKEFILE = @GLIB_MAKEFILE@
-GLIB_MKENUMS = @GLIB_MKENUMS@
-GMSGFMT = @GMSGFMT@
-GMSGFMT_015 = @GMSGFMT_015@
-GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
-GNUTLS_LIBS = @GNUTLS_LIBS@
-GREP = @GREP@
-GTKDOC_CHECK = @GTKDOC_CHECK@
-GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
-GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
-GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
-GTKDOC_MKPDF = @GTKDOC_MKPDF@
-GTKDOC_REBASE = @GTKDOC_REBASE@
-GUDEV_CFLAGS = @GUDEV_CFLAGS@
-GUDEV_LIBS = @GUDEV_LIBS@
-HTML_DIR = @HTML_DIR@
-INSTALL = @INSTALL@
-INSTALL_DATA = @INSTALL_DATA@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@
-INSTALL_SCRIPT = @INSTALL_SCRIPT@
-INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
-INTLLIBS = @INTLLIBS@
-INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
-INTLTOOL_MERGE = @INTLTOOL_MERGE@
-INTLTOOL_PERL = @INTLTOOL_PERL@
-INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
-INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
-INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
-INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
-INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
-INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
-INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@
-INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@
-INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@
-INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@
-INTROSPECTION_LIBS = @INTROSPECTION_LIBS@
-INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
-INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
-INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
-IPTABLES_PATH = @IPTABLES_PATH@
-IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@
-IWMX_SDK_LIBS = @IWMX_SDK_LIBS@
-KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
-LCOV = @LCOV@
-LD = @LD@
-LDFLAGS = @LDFLAGS@
-LIBDL = @LIBDL@
-LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
-LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
-LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
-LIBICONV = @LIBICONV@
-LIBINTL = @LIBINTL@
-LIBM = @LIBM@
-LIBNL_CFLAGS = @LIBNL_CFLAGS@
-LIBNL_LIBS = @LIBNL_LIBS@
-LIBOBJS = @LIBOBJS@
-LIBS = @LIBS@
-LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
-LIBSOUP_LIBS = @LIBSOUP_LIBS@
-LIBTOOL = @LIBTOOL@
-LIPO = @LIPO@
-LN_S = @LN_S@
-LTLIBICONV = @LTLIBICONV@
-LTLIBINTL = @LTLIBINTL@
-LTLIBOBJS = @LTLIBOBJS@
-MAINT = @MAINT@
-MAKEINFO = @MAKEINFO@
-MANIFEST_TOOL = @MANIFEST_TOOL@
-MKDIR_P = @MKDIR_P@
-MM_GLIB_CFLAGS = @MM_GLIB_CFLAGS@
-MM_GLIB_LIBS = @MM_GLIB_LIBS@
-MOC = @MOC@
-MSGFMT = @MSGFMT@
-MSGFMT_015 = @MSGFMT_015@
-MSGMERGE = @MSGMERGE@
-NM = @NM@
-NMEDIT = @NMEDIT@
-NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
-NM_MICRO_VERSION = @NM_MICRO_VERSION@
-NM_MINOR_VERSION = @NM_MINOR_VERSION@
-NM_MODIFY_SYSTEM_POLICY = @NM_MODIFY_SYSTEM_POLICY@
-NM_VERSION = @NM_VERSION@
-NSS_CFLAGS = @NSS_CFLAGS@
-NSS_LIBS = @NSS_LIBS@
-OBJDUMP = @OBJDUMP@
-OBJEXT = @OBJEXT@
-OTOOL = @OTOOL@
-OTOOL64 = @OTOOL64@
-PACKAGE = @PACKAGE@
-PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
-PACKAGE_NAME = @PACKAGE_NAME@
-PACKAGE_STRING = @PACKAGE_STRING@
-PACKAGE_TARNAME = @PACKAGE_TARNAME@
-PACKAGE_URL = @PACKAGE_URL@
-PACKAGE_VERSION = @PACKAGE_VERSION@
-PATH_SEPARATOR = @PATH_SEPARATOR@
-PKG_CONFIG = @PKG_CONFIG@
-PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
-PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
-POLKIT_CFLAGS = @POLKIT_CFLAGS@
-POLKIT_LIBS = @POLKIT_LIBS@
-POSUB = @POSUB@
-PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
-QT_CFLAGS = @QT_CFLAGS@
-QT_LIBS = @QT_LIBS@
-RANLIB = @RANLIB@
-SED = @SED@
-SET_MAKE = @SET_MAKE@
-SHELL = @SHELL@
-STRIP = @STRIP@
-SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@
-SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@
-SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@
-SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
-SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
-SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
-SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
-UDEV_BASE_DIR = @UDEV_BASE_DIR@
-USE_NLS = @USE_NLS@
-UUID_CFLAGS = @UUID_CFLAGS@
-UUID_LIBS = @UUID_LIBS@
-VAPIGEN = @VAPIGEN@
-VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@
-VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@
-VERSION = @VERSION@
-XGETTEXT = @XGETTEXT@
-XGETTEXT_015 = @XGETTEXT_015@
-XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
-abs_builddir = @abs_builddir@
-abs_srcdir = @abs_srcdir@
-abs_top_builddir = @abs_top_builddir@
-abs_top_srcdir = @abs_top_srcdir@
-ac_ct_AR = @ac_ct_AR@
-ac_ct_CC = @ac_ct_CC@
-ac_ct_CXX = @ac_ct_CXX@
-ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
-am__include = @am__include@
-am__leading_dot = @am__leading_dot@
-am__quote = @am__quote@
-am__tar = @am__tar@
-am__untar = @am__untar@
-bindir = @bindir@
-build = @build@
-build_alias = @build_alias@
-build_cpu = @build_cpu@
-build_os = @build_os@
-build_vendor = @build_vendor@
-builddir = @builddir@
-datadir = @datadir@
-datarootdir = @datarootdir@
-docdir = @docdir@
-dvidir = @dvidir@
-exec_prefix = @exec_prefix@
-host = @host@
-host_alias = @host_alias@
-host_cpu = @host_cpu@
-host_os = @host_os@
-host_vendor = @host_vendor@
-htmldir = @htmldir@
-includedir = @includedir@
-infodir = @infodir@
-install_sh = @install_sh@
-intltool__v_merge_options_ = @intltool__v_merge_options_@
-intltool__v_merge_options_0 = @intltool__v_merge_options_0@
-libdir = @libdir@
-libexecdir = @libexecdir@
-localedir = @localedir@
-localstatedir = @localstatedir@
-mandir = @mandir@
-mkdir_p = @mkdir_p@
-nmbinary = @nmbinary@
-nmconfdir = @nmconfdir@
-nmdatadir = @nmdatadir@
-nmrundir = @nmrundir@
-nmstatedir = @nmstatedir@
-oldincludedir = @oldincludedir@
-pdfdir = @pdfdir@
-prefix = @prefix@
-program_transform_name = @program_transform_name@
-psdir = @psdir@
-sbindir = @sbindir@
-sharedstatedir = @sharedstatedir@
-srcdir = @srcdir@
-sysconfdir = @sysconfdir@
-systemdsystemunitdir = @systemdsystemunitdir@
-target_alias = @target_alias@
-top_build_prefix = @top_build_prefix@
-top_builddir = @top_builddir@
-top_srcdir = @top_srcdir@
-with_dhclient = @with_dhclient@
-with_dhcpcd = @with_dhcpcd@
-with_netconfig = @with_netconfig@
-with_resolvconf = @with_resolvconf@
-noinst_LTLIBRARIES = libvpn-manager.la
-libvpn_manager_la_SOURCES = \
-	nm-vpn-manager.c \
-	nm-vpn-manager.h \
-	nm-vpn-service.c \
-	nm-vpn-service.h \
-	nm-vpn-connection.c \
-	nm-vpn-connection.h
-
-libvpn_manager_la_CPPFLAGS = \
-	-I${top_srcdir} \
-	-I${top_builddir}/include \
-	-I${top_srcdir}/include \
-	-I${top_builddir}/libnm-util \
-	-I${top_srcdir}/libnm-util \
-	-I${top_builddir}/src/generated \
-	-I${top_srcdir}/src/generated \
-	-I${top_srcdir}/src/logging \
-	-I${top_srcdir}/src/posix-signals \
-	-I${top_srcdir}/src \
-	-I${top_srcdir}/src/dns-manager \
-	$(LIBNL_CFLAGS) \
-	$(DBUS_CFLAGS) \
-	$(GLIB_CFLAGS) \
-	-DVPN_NAME_FILES_DIR=\""$(sysconfdir)/NetworkManager/VPN"\"
-
-libvpn_manager_la_LIBADD = \
-	$(top_builddir)/src/generated/libnm-generated.la \
-	$(top_builddir)/src/logging/libnm-logging.la \
-	$(top_builddir)/src/posix-signals/libnm-posix-signals.la \
-	$(top_builddir)/libnm-util/libnm-util.la \
-	$(LIBNL_LIBS) \
-	$(DBUS_LIBS) \
-	$(GLIB_LIBS)
-
-BUILT_SOURCES = \
-	nm-vpn-connection-glue.h \
-	nm-vpn-plugin-bindings.h
-
-CLEANFILES = $(BUILT_SOURCES)
-all: $(BUILT_SOURCES)
-	$(MAKE) $(AM_MAKEFLAGS) all-am
-
-.SUFFIXES:
-.SUFFIXES: .c .lo .o .obj
-$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
-	@for dep in $?; do \
-	  case '$(am__configure_deps)' in \
-	    *$$dep*) \
-	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
-	        && { if test -f $@; then exit 0; else break; fi; }; \
-	      exit 1;; \
-	  esac; \
-	done; \
-	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/vpn-manager/Makefile'; \
-	$(am__cd) $(top_srcdir) && \
-	  $(AUTOMAKE) --gnu src/vpn-manager/Makefile
-.PRECIOUS: Makefile
-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
-	@case '$?' in \
-	  *config.status*) \
-	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
-	  *) \
-	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
-	esac;
-
-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-
-$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
-	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(am__aclocal_m4_deps):
-
-clean-noinstLTLIBRARIES:
-	-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
-	@list='$(noinst_LTLIBRARIES)'; \
-	locs=`for p in $$list; do echo $$p; done | \
-	      sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
-	      sort -u`; \
-	test -z "$$locs" || { \
-	  echo rm -f $${locs}; \
-	  rm -f $${locs}; \
-	}
-
-libvpn-manager.la: $(libvpn_manager_la_OBJECTS) $(libvpn_manager_la_DEPENDENCIES) $(EXTRA_libvpn_manager_la_DEPENDENCIES) 
-	$(AM_V_CCLD)$(LINK)  $(libvpn_manager_la_OBJECTS) $(libvpn_manager_la_LIBADD) $(LIBS)
-
-mostlyclean-compile:
-	-rm -f *.$(OBJEXT)
-
-distclean-compile:
-	-rm -f *.tab.c
-
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvpn_manager_la-nm-vpn-connection.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvpn_manager_la-nm-vpn-manager.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libvpn_manager_la-nm-vpn-service.Plo@am__quote@
-
-.c.o:
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
-
-.c.obj:
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
-
-.c.lo:
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
-
-libvpn_manager_la-nm-vpn-manager.lo: nm-vpn-manager.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libvpn_manager_la-nm-vpn-manager.lo -MD -MP -MF $(DEPDIR)/libvpn_manager_la-nm-vpn-manager.Tpo -c -o libvpn_manager_la-nm-vpn-manager.lo `test -f 'nm-vpn-manager.c' || echo '$(srcdir)/'`nm-vpn-manager.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libvpn_manager_la-nm-vpn-manager.Tpo $(DEPDIR)/libvpn_manager_la-nm-vpn-manager.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='nm-vpn-manager.c' object='libvpn_manager_la-nm-vpn-manager.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libvpn_manager_la-nm-vpn-manager.lo `test -f 'nm-vpn-manager.c' || echo '$(srcdir)/'`nm-vpn-manager.c
-
-libvpn_manager_la-nm-vpn-service.lo: nm-vpn-service.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libvpn_manager_la-nm-vpn-service.lo -MD -MP -MF $(DEPDIR)/libvpn_manager_la-nm-vpn-service.Tpo -c -o libvpn_manager_la-nm-vpn-service.lo `test -f 'nm-vpn-service.c' || echo '$(srcdir)/'`nm-vpn-service.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libvpn_manager_la-nm-vpn-service.Tpo $(DEPDIR)/libvpn_manager_la-nm-vpn-service.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='nm-vpn-service.c' object='libvpn_manager_la-nm-vpn-service.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libvpn_manager_la-nm-vpn-service.lo `test -f 'nm-vpn-service.c' || echo '$(srcdir)/'`nm-vpn-service.c
-
-libvpn_manager_la-nm-vpn-connection.lo: nm-vpn-connection.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libvpn_manager_la-nm-vpn-connection.lo -MD -MP -MF $(DEPDIR)/libvpn_manager_la-nm-vpn-connection.Tpo -c -o libvpn_manager_la-nm-vpn-connection.lo `test -f 'nm-vpn-connection.c' || echo '$(srcdir)/'`nm-vpn-connection.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libvpn_manager_la-nm-vpn-connection.Tpo $(DEPDIR)/libvpn_manager_la-nm-vpn-connection.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='nm-vpn-connection.c' object='libvpn_manager_la-nm-vpn-connection.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libvpn_manager_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libvpn_manager_la-nm-vpn-connection.lo `test -f 'nm-vpn-connection.c' || echo '$(srcdir)/'`nm-vpn-connection.c
-
-mostlyclean-libtool:
-	-rm -f *.lo
-
-clean-libtool:
-	-rm -rf .libs _libs
-
-ID: $(am__tagged_files)
-	$(am__define_uniq_tagged_files); mkid -fID $$unique
-tags: tags-am
-TAGS: tags
-
-tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
-	set x; \
-	here=`pwd`; \
-	$(am__define_uniq_tagged_files); \
-	shift; \
-	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
-	  test -n "$$unique" || unique=$$empty_fix; \
-	  if test $$# -gt 0; then \
-	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
-	      "$$@" $$unique; \
-	  else \
-	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
-	      $$unique; \
-	  fi; \
-	fi
-ctags: ctags-am
-
-CTAGS: ctags
-ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
-	$(am__define_uniq_tagged_files); \
-	test -z "$(CTAGS_ARGS)$$unique" \
-	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
-	     $$unique
-
-GTAGS:
-	here=`$(am__cd) $(top_builddir) && pwd` \
-	  && $(am__cd) $(top_srcdir) \
-	  && gtags -i $(GTAGS_ARGS) "$$here"
-cscopelist: cscopelist-am
-
-cscopelist-am: $(am__tagged_files)
-	list='$(am__tagged_files)'; \
-	case "$(srcdir)" in \
-	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
-	  *) sdir=$(subdir)/$(srcdir) ;; \
-	esac; \
-	for i in $$list; do \
-	  if test -f "$$i"; then \
-	    echo "$(subdir)/$$i"; \
-	  else \
-	    echo "$$sdir/$$i"; \
-	  fi; \
-	done >> $(top_builddir)/cscope.files
-
-distclean-tags:
-	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-
-distdir: $(DISTFILES)
-	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
-	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
-	list='$(DISTFILES)'; \
-	  dist_files=`for file in $$list; do echo $$file; done | \
-	  sed -e "s|^$$srcdirstrip/||;t" \
-	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
-	case $$dist_files in \
-	  */*) $(MKDIR_P) `echo "$$dist_files" | \
-			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
-			   sort -u` ;; \
-	esac; \
-	for file in $$dist_files; do \
-	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
-	  if test -d $$d/$$file; then \
-	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
-	    if test -d "$(distdir)/$$file"; then \
-	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
-	    fi; \
-	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
-	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
-	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
-	    fi; \
-	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
-	  else \
-	    test -f "$(distdir)/$$file" \
-	    || cp -p $$d/$$file "$(distdir)/$$file" \
-	    || exit 1; \
-	  fi; \
-	done
-check-am: all-am
-check: $(BUILT_SOURCES)
-	$(MAKE) $(AM_MAKEFLAGS) check-am
-all-am: Makefile $(LTLIBRARIES)
-installdirs:
-install: $(BUILT_SOURCES)
-	$(MAKE) $(AM_MAKEFLAGS) install-am
-install-exec: install-exec-am
-install-data: install-data-am
-uninstall: uninstall-am
-
-install-am: all-am
-	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
-
-installcheck: installcheck-am
-install-strip:
-	if test -z '$(STRIP)'; then \
-	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
-	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
-	      install; \
-	else \
-	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
-	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
-	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
-	fi
-mostlyclean-generic:
-
-clean-generic:
-	-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
-
-distclean-generic:
-	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
-
-maintainer-clean-generic:
-	@echo "This command is intended for maintainers to use"
-	@echo "it deletes files that may require special tools to rebuild."
-	-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
-clean: clean-am
-
-clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
-	mostlyclean-am
-
-distclean: distclean-am
-	-rm -rf ./$(DEPDIR)
-	-rm -f Makefile
-distclean-am: clean-am distclean-compile distclean-generic \
-	distclean-tags
-
-dvi: dvi-am
-
-dvi-am:
-
-html: html-am
-
-html-am:
-
-info: info-am
-
-info-am:
-
-install-data-am:
-
-install-dvi: install-dvi-am
-
-install-dvi-am:
-
-install-exec-am:
-
-install-html: install-html-am
-
-install-html-am:
-
-install-info: install-info-am
-
-install-info-am:
-
-install-man:
-
-install-pdf: install-pdf-am
-
-install-pdf-am:
-
-install-ps: install-ps-am
-
-install-ps-am:
-
-installcheck-am:
-
-maintainer-clean: maintainer-clean-am
-	-rm -rf ./$(DEPDIR)
-	-rm -f Makefile
-maintainer-clean-am: distclean-am maintainer-clean-generic
-
-mostlyclean: mostlyclean-am
-
-mostlyclean-am: mostlyclean-compile mostlyclean-generic \
-	mostlyclean-libtool
-
-pdf: pdf-am
-
-pdf-am:
-
-ps: ps-am
-
-ps-am:
-
-uninstall-am:
-
-.MAKE: all check install install-am install-strip
-
-.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
-	clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \
-	ctags-am distclean distclean-compile distclean-generic \
-	distclean-libtool distclean-tags distdir dvi dvi-am html \
-	html-am info info-am install install-am install-data \
-	install-data-am install-dvi install-dvi-am install-exec \
-	install-exec-am install-html install-html-am install-info \
-	install-info-am install-man install-pdf install-pdf-am \
-	install-ps install-ps-am install-strip installcheck \
-	installcheck-am installdirs maintainer-clean \
-	maintainer-clean-generic mostlyclean mostlyclean-compile \
-	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
-	tags tags-am uninstall uninstall-am
-
-
-nm-vpn-connection-glue.h: $(top_srcdir)/introspection/nm-vpn-connection.xml
-	$(AM_V_GEN) dbus-binding-tool --prefix=nm_vpn_connection --mode=glib-server --output=$@ $<
-
-nm-vpn-plugin-bindings.h: $(top_srcdir)/introspection/nm-vpn-plugin.xml
-	$(AM_V_GEN) dbus-binding-tool --prefix=nm_vpn_plugin --mode=glib-client --output=$@ $<
-
-# Tell versions [3.59,3.63) of GNU make to not export all variables.
-# Otherwise a system limit (for SysV at least) may be exceeded.
-.NOEXPORT:
diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c
index f2e31a8d..d66dcfdb 100644
--- a/src/vpn-manager/nm-vpn-connection.c
+++ b/src/vpn-manager/nm-vpn-connection.c
@@ -15,7 +15,7 @@
  * 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 - 2012 Red Hat, Inc.
+ * Copyright (C) 2005 - 2013 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
@@ -34,20 +34,16 @@
 #include "nm-setting-vpn.h"
 #include "nm-setting-ip4-config.h"
 #include "nm-dbus-manager.h"
-#include "nm-system.h"
+#include "nm-platform.h"
 #include "nm-logging.h"
 #include "nm-utils.h"
-#include "nm-vpn-plugin-bindings.h"
-#include "nm-marshal.h"
 #include "nm-active-connection.h"
-#include "nm-properties-changed-signal.h"
 #include "nm-dbus-glib-types.h"
 #include "NetworkManagerUtils.h"
-#include "nm-netlink-monitor.h"
-#include "nm-netlink-utils.h"
 #include "nm-glib-compat.h"
 #include "settings/nm-settings-connection.h"
 #include "nm-dispatcher.h"
+#include "nm-agent-manager.h"
 
 #include "nm-vpn-connection-glue.h"
 
@@ -60,28 +56,41 @@ typedef enum {
 	SECRETS_REQ_EXISTING = 1,
 	/* New secrets required; ask an agent */
 	SECRETS_REQ_NEW = 2,
+	/* Plugin requests secrets interactively */
+	SECRETS_REQ_INTERACTIVE = 3,
 	/* Placeholder for bounds checking */
 	SECRETS_REQ_LAST
 } SecretsReq;
 
-typedef struct {
-	gboolean disposed;
+/* Internal VPN states, private to NMVPNConnection */
+typedef enum {
+	STATE_UNKNOWN = 0,
+	STATE_WAITING,
+	STATE_PREPARE,
+	STATE_NEED_AUTH,
+	STATE_CONNECT,
+	STATE_IP_CONFIG_GET,
+	STATE_PRE_UP,
+	STATE_ACTIVATED,
+	STATE_DEACTIVATING,
+	STATE_DISCONNECTED,
+	STATE_FAILED,
+} VpnState;
 
+typedef struct {
 	NMConnection *connection;
 
 	guint32 secrets_id;
 	SecretsReq secrets_idx;
 	char *username;
 
-	NMDevice *parent_dev;
-	gulong device_monitor;
-	gulong device_ip4;
-	gulong device_ip6;
-
-	NMVPNConnectionState vpn_state;
+	VpnState vpn_state;
+	guint dispatcher_id;
 	NMVPNConnectionStateReason failure_reason;
+
 	DBusGProxy *proxy;
-	guint ipconfig_timeout;
+	GHashTable *connect_hash;
+	guint connect_timeout;
 	gboolean has_ip4;
 	NMIP4Config *ip4_config;
 	guint32 ip4_internal_gw;
@@ -94,14 +103,11 @@ typedef struct {
 	int ip_ifindex;
 	char *banner;
 	guint32 mtu;
-
-	struct rtnl_route *gw_route;
 } NMVPNConnectionPrivate;
 
 #define NM_VPN_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_CONNECTION, NMVPNConnectionPrivate))
 
 enum {
-	PROPERTIES_CHANGED,
 	VPN_STATE_CHANGED,
 	INTERNAL_STATE_CHANGED,
 
@@ -114,27 +120,81 @@ enum {
 	PROP_0,
 	PROP_VPN_STATE,
 	PROP_BANNER,
+	PROP_IP4_CONFIG,
+	PROP_IP6_CONFIG,
 	PROP_MASTER = 2000,
 
 	LAST_PROP
 };
 
-static void get_secrets (NMVPNConnection *self, SecretsReq secrets_idx);
+static void get_secrets (NMVPNConnection *self,
+                         SecretsReq secrets_idx,
+                         const char **hints);
+
+static void plugin_interactive_secrets_required (DBusGProxy *proxy,
+                                                 const char *message,
+                                                 const char **secrets,
+                                                 gpointer user_data);
+
+static void _set_vpn_state (NMVPNConnection *connection,
+                            VpnState vpn_state,
+                            NMVPNConnectionStateReason reason,
+                            gboolean quitting);
+
+/*********************************************************************/
+
+static NMVPNConnectionState
+_state_to_nm_vpn_state (VpnState state)
+{
+	switch (state) {
+	case STATE_WAITING:
+	case STATE_PREPARE:
+		return NM_VPN_CONNECTION_STATE_PREPARE;
+	case STATE_NEED_AUTH:
+		return NM_VPN_CONNECTION_STATE_NEED_AUTH;
+	case STATE_CONNECT:
+		return NM_VPN_CONNECTION_STATE_CONNECT;
+	case STATE_IP_CONFIG_GET:
+	case STATE_PRE_UP:
+		return NM_VPN_CONNECTION_STATE_IP_CONFIG_GET;
+	case STATE_ACTIVATED:
+		return NM_VPN_CONNECTION_STATE_ACTIVATED;
+	case STATE_DEACTIVATING: {
+		/* Map DEACTIVATING to ACTIVATED to preserve external API behavior,
+		 * since our API has no DEACTIVATING state of its own.  Since this can
+		 * take some time, and the VPN isn't actually disconnected until it
+		 * hits the DISCONNECTED state, to clients it should still appear
+		 * connected.
+		 */
+		return NM_VPN_CONNECTION_STATE_ACTIVATED;
+	}
+	case STATE_DISCONNECTED:
+		return NM_VPN_CONNECTION_STATE_DISCONNECTED;
+	case STATE_FAILED:
+		return NM_VPN_CONNECTION_STATE_FAILED;
+	default:
+		return STATE_UNKNOWN;
+	}
+}
 
 static NMActiveConnectionState
-ac_state_from_vpn_state (NMVPNConnectionState vpn_state)
+_state_to_ac_state (VpnState vpn_state)
 {
 	/* Set the NMActiveConnection state based on VPN state */
 	switch (vpn_state) {
-	case NM_VPN_CONNECTION_STATE_PREPARE:
-	case NM_VPN_CONNECTION_STATE_NEED_AUTH:
-	case NM_VPN_CONNECTION_STATE_CONNECT:
-	case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
+	case STATE_WAITING:
+	case STATE_PREPARE:
+	case STATE_NEED_AUTH:
+	case STATE_CONNECT:
+	case STATE_IP_CONFIG_GET:
+	case STATE_PRE_UP:
 		return NM_ACTIVE_CONNECTION_STATE_ACTIVATING;
-	case NM_VPN_CONNECTION_STATE_ACTIVATED:
+	case STATE_ACTIVATED:
 		return NM_ACTIVE_CONNECTION_STATE_ACTIVATED;
-	case NM_VPN_CONNECTION_STATE_FAILED:
-	case NM_VPN_CONNECTION_STATE_DISCONNECTED:
+	case STATE_DEACTIVATING:
+		return NM_ACTIVE_CONNECTION_STATE_DEACTIVATING;
+	case STATE_DISCONNECTED:
+	case STATE_FAILED:
 		return NM_ACTIVE_CONNECTION_STATE_DEACTIVATED;
 	default:
 		break;
@@ -149,10 +209,12 @@ call_plugin_disconnect (NMVPNConnection *self)
 	GError *error = NULL;
 
 	if (priv->proxy) {
-		org_freedesktop_NetworkManager_VPN_Plugin_disconnect (priv->proxy, &error);
-		if (error)
+		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_clear_error (&error);
+			g_error_free (error);
+		}
 
 		g_object_unref (priv->proxy);
 		priv->proxy = NULL;
@@ -160,21 +222,18 @@ call_plugin_disconnect (NMVPNConnection *self)
 }
 
 static void
-vpn_cleanup (NMVPNConnection *connection)
+vpn_cleanup (NMVPNConnection *connection, NMDevice *parent_dev)
 {
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
 
 	if (priv->ip_ifindex) {
-		nm_system_iface_set_up (priv->ip_ifindex, FALSE, NULL);
-		nm_system_iface_flush_routes (priv->ip_ifindex, AF_UNSPEC);
-		nm_system_iface_flush_addresses (priv->ip_ifindex, AF_UNSPEC);
+		nm_platform_link_set_down (priv->ip_ifindex);
+		nm_platform_route_flush (priv->ip_ifindex);
+		nm_platform_address_flush (priv->ip_ifindex);
 	}
 
-	if (priv->gw_route) {
-		nm_netlink_route_delete (priv->gw_route);
-		rtnl_route_put (priv->gw_route);
-		priv->gw_route = NULL;
-	}
+	nm_device_set_vpn4_config (parent_dev, NULL);
+	nm_device_set_vpn6_config (parent_dev, NULL);
 
 	g_free (priv->banner);
 	priv->banner = NULL;
@@ -191,12 +250,46 @@ vpn_cleanup (NMVPNConnection *connection)
 }
 
 static void
-nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
-                                 NMVPNConnectionState vpn_state,
-                                 NMVPNConnectionStateReason reason)
+dispatcher_pre_down_done (guint call_id, gpointer user_data)
+{
+	NMVPNConnection *self = NM_VPN_CONNECTION (user_data);
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+	priv->dispatcher_id = 0;
+	_set_vpn_state (self, STATE_DISCONNECTED, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+}
+
+static void
+dispatcher_pre_up_done (guint call_id, gpointer user_data)
+{
+	NMVPNConnection *self = NM_VPN_CONNECTION (user_data);
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+	priv->dispatcher_id = 0;
+	_set_vpn_state (self, STATE_ACTIVATED, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+}
+
+static void
+dispatcher_cleanup (NMVPNConnection *self)
+{
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+	if (priv->dispatcher_id) {
+		nm_dispatcher_call_cancel (priv->dispatcher_id);
+		priv->dispatcher_id = 0;
+	}
+}
+
+static void
+_set_vpn_state (NMVPNConnection *connection,
+                VpnState vpn_state,
+                NMVPNConnectionStateReason reason,
+                gboolean quitting)
 {
 	NMVPNConnectionPrivate *priv;
-	NMVPNConnectionState old_vpn_state;
+	VpnState old_vpn_state;
+	NMVPNConnectionState new_external_state, old_external_state;
+	NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (connection));
 
 	g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
 
@@ -208,16 +301,23 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
 	old_vpn_state = priv->vpn_state;
 	priv->vpn_state = vpn_state;
 
+	/* The device gets destroyed by active connection when it enters
+	 * the deactivated state, so we need to ref it for usage below.
+	 */
+	if (parent_dev)
+		g_object_ref (parent_dev);
+
 	/* Update active connection base class state */
 	nm_active_connection_set_state (NM_ACTIVE_CONNECTION (connection),
-	                                ac_state_from_vpn_state (vpn_state));
+	                                _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;
 	}
-	priv->secrets_idx = SECRETS_REQ_SYSTEM;
+
+	dispatcher_cleanup (connection);
 
 	/* 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
@@ -225,144 +325,254 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
 	 */
 	g_object_ref (connection);
 
-	g_signal_emit (connection, signals[VPN_STATE_CHANGED], 0, vpn_state, reason);
-	g_signal_emit (connection, signals[INTERNAL_STATE_CHANGED], 0, vpn_state, old_vpn_state, reason);
-	g_object_notify (G_OBJECT (connection), NM_VPN_CONNECTION_VPN_STATE);
+	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,
+		               new_external_state,
+		               old_external_state,
+		               reason);
+		g_object_notify (G_OBJECT (connection), NM_VPN_CONNECTION_VPN_STATE);
+	}
 
 	switch (vpn_state) {
-	case NM_VPN_CONNECTION_STATE_NEED_AUTH:
-		/* Kick off the secrets requests; first we get existing system secrets
-		 * and ask the plugin if these are sufficient, next we get all existing
-		 * secrets from system and from user agents and ask the plugin again,
-		 * and last we ask the user for new secrets if required.
+	case STATE_NEED_AUTH:
+		/* Do nothing; not part of 'default' because we don't want to touch
+		 * priv->secrets_req as NEED_AUTH is re-entered during interactive
+		 * secrets.
 		 */
-		get_secrets (connection, SECRETS_REQ_SYSTEM);
 		break;
-	case NM_VPN_CONNECTION_STATE_ACTIVATED:
+	case STATE_PRE_UP:
+		if (!nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_PRE_UP,
+		                             priv->connection,
+		                             parent_dev,
+		                             priv->ip_iface,
+		                             priv->ip4_config,
+		                             priv->ip6_config,
+		                             dispatcher_pre_up_done,
+		                             connection,
+		                             &priv->dispatcher_id)) {
+			/* Just proceed on errors */
+			dispatcher_pre_up_done (0, connection);
+		}
+		break;
+	case STATE_ACTIVATED:
 		/* Secrets no longer needed now that we're connected */
 		nm_connection_clear_secrets (priv->connection);
 
 		/* Let dispatcher scripts know we're up and running */
 		nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_UP,
 		                        priv->connection,
-		                        priv->parent_dev,
+		                        parent_dev,
 		                        priv->ip_iface,
 		                        priv->ip4_config,
 		                        priv->ip6_config,
 		                        NULL,
+		                        NULL,
 		                        NULL);
 		break;
-	case NM_VPN_CONNECTION_STATE_FAILED:
-	case NM_VPN_CONNECTION_STATE_DISCONNECTED:
-		if (old_vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED) {
+	case STATE_DEACTIVATING:
+		if (quitting) {
+			nm_dispatcher_call_vpn_sync (DISPATCHER_ACTION_VPN_PRE_DOWN,
+			                             priv->connection,
+			                             parent_dev,
+			                             priv->ip_iface,
+			                             priv->ip4_config,
+			                             priv->ip6_config);
+		} else {
+			if (!nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_PRE_DOWN,
+			                             priv->connection,
+			                             parent_dev,
+			                             priv->ip_iface,
+			                             priv->ip4_config,
+			                             priv->ip6_config,
+			                             dispatcher_pre_down_done,
+			                             connection,
+			                             &priv->dispatcher_id)) {
+				/* Just proceed on errors */
+				dispatcher_pre_down_done (0, connection);
+			}
+		}
+		break;
+	case STATE_FAILED:
+	case STATE_DISCONNECTED:
+		if (   old_vpn_state >= STATE_ACTIVATED
+		    && old_vpn_state <= STATE_DEACTIVATING) {
 			/* Let dispatcher scripts know we're about to go down */
-			nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_DOWN,
-			                        priv->connection,
-			                        priv->parent_dev,
-			                        priv->ip_iface,
-			                        NULL,
-			                        NULL,
-			                        NULL,
-			                        NULL);
+			if (quitting) {
+				nm_dispatcher_call_vpn_sync (DISPATCHER_ACTION_VPN_DOWN,
+				                             priv->connection,
+				                             parent_dev,
+				                             priv->ip_iface,
+				                             NULL,
+				                             NULL);
+			} else {
+				nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_DOWN,
+				                        priv->connection,
+				                        parent_dev,
+				                        priv->ip_iface,
+				                        NULL,
+				                        NULL,
+				                        NULL,
+				                        NULL,
+				                        NULL);
+			}
 		}
 
 		/* Tear down and clean up the connection */
 		call_plugin_disconnect (connection);
-		vpn_cleanup (connection);
-		break;
+		vpn_cleanup (connection, parent_dev);
+		/* Fall through */
 	default:
+		priv->secrets_idx = SECRETS_REQ_SYSTEM;
 		break;
 	}
 
 	g_object_unref (connection);
+	if (parent_dev)
+		g_object_unref (parent_dev);
 }
 
 static void
-device_state_changed (NMDevice *device,
+device_state_changed (NMActiveConnection *active,
+                      NMDevice *device,
                       NMDeviceState new_state,
-                      NMDeviceState old_state,
-                      NMDeviceStateReason reason,
-                      gpointer user_data)
+                      NMDeviceState old_state)
 {
-	NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
-
 	if (new_state <= NM_DEVICE_STATE_DISCONNECTED) {
-		nm_vpn_connection_set_vpn_state (connection,
-		                                 NM_VPN_CONNECTION_STATE_DISCONNECTED,
-		                                 NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED);
+		_set_vpn_state (NM_VPN_CONNECTION (active),
+		                STATE_DISCONNECTED,
+		                NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED,
+		                FALSE);
 	} else if (new_state == NM_DEVICE_STATE_FAILED) {
-		nm_vpn_connection_set_vpn_state (connection,
-		                                 NM_VPN_CONNECTION_STATE_FAILED,
-		                                 NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED);
+		_set_vpn_state (NM_VPN_CONNECTION (active),
+		                STATE_FAILED,
+		                NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED,
+		                FALSE);
 	}
+
+	/* FIXME: map device DEACTIVATING state to VPN DEACTIVATING state and
+	 * block device deactivation on VPN deactivation.
+	 */
 }
 
 static void
-device_ip4_config_changed (NMDevice *device,
-                           GParamSpec *pspec,
-                           gpointer user_data)
+add_ip4_vpn_gateway_route (NMIP4Config *config, NMDevice *parent_device, guint32 vpn_gw)
 {
-	NMVPNConnection *vpn = NM_VPN_CONNECTION (user_data);
-	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (vpn);
+	NMIP4Config *parent_config;
+	guint32 parent_gw;
+	NMPlatformIP4Route route;
+
+	g_return_if_fail (NM_IS_IP4_CONFIG (config));
+	g_return_if_fail (NM_IS_DEVICE (parent_device));
+	g_return_if_fail (vpn_gw != 0);
 
-	if (   (priv->vpn_state != NM_VPN_CONNECTION_STATE_ACTIVATED)
-	    || !nm_device_get_ip4_config (device))
+	/* Set up a route to the VPN gateway's public IP address through the default
+	 * network device if the VPN gateway is on a different subnet.
+	 */
+
+	parent_config = nm_device_get_ip4_config (parent_device);
+	g_return_if_fail (parent_config != NULL);
+	parent_gw = nm_ip4_config_get_gateway (parent_config);
+	if (!parent_gw)
 		return;
 
-	/* Re-add the VPN gateway route */
-	if (priv->ip4_external_gw) {
-		if (priv->gw_route)
-			rtnl_route_put (priv->gw_route);
-		priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev,
-		                                                      priv->ip4_external_gw);
-	}
+	memset (&route, 0, sizeof (route));
+	route.network = vpn_gw;
+	route.plen = 32;
+	route.gateway = parent_gw;
+
+	/* If the VPN gateway is in the same subnet as one of the parent device's
+	 * IP addresses, don't add the host route to it, but a route through the
+	 * parent device.
+	 */
+	if (nm_ip4_config_destination_is_direct (parent_config, vpn_gw, 32))
+		route.gateway = 0;
+
+	route.source = NM_PLATFORM_SOURCE_VPN;
+	route.metric = nm_device_get_priority (parent_device);
+	nm_ip4_config_add_route (config, &route);
+
+	/* Ensure there's a route to the parent device's gateway through the
+	 * parent device, since if the VPN claims the default route and the VPN
+	 * routes include a subnet that matches the parent device's subnet,
+	 * the parent device's gateway would get routed through the VPN and fail.
+	 */
+	memset (&route, 0, sizeof (route));
+	route.network = parent_gw;
+	route.plen = 32;
+	route.source = NM_PLATFORM_SOURCE_VPN;
+	route.metric = nm_device_get_priority (parent_device);
+
+	nm_ip4_config_add_route (config, &route);
 }
 
 static void
-device_ip6_config_changed (NMDevice *device,
-                           GParamSpec *pspec,
-                           gpointer user_data)
+add_ip6_vpn_gateway_route (NMIP6Config *config,
+                           NMDevice *parent_device,
+                           const struct in6_addr *vpn_gw)
 {
-	NMVPNConnection *vpn = NM_VPN_CONNECTION (user_data);
-	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (vpn);
-
-	if (   (priv->vpn_state != NM_VPN_CONNECTION_STATE_ACTIVATED)
-	    || !nm_device_get_ip6_config (device))
+	NMIP6Config *parent_config;
+	const struct in6_addr *parent_gw;
+	NMPlatformIP6Route route;
+
+	g_return_if_fail (NM_IS_IP6_CONFIG (config));
+	g_return_if_fail (NM_IS_DEVICE (parent_device));
+	g_return_if_fail (vpn_gw != NULL);
+
+	parent_config = nm_device_get_ip6_config (parent_device);
+	g_return_if_fail (parent_config != NULL);
+	parent_gw = nm_ip6_config_get_gateway (parent_config);
+	if (!parent_gw)
 		return;
 
-	/* Re-add the VPN gateway route */
-	if (priv->ip6_external_gw) {
-		if (priv->gw_route)
-			rtnl_route_put (priv->gw_route);
-		priv->gw_route = nm_system_add_ip6_vpn_gateway_route (priv->parent_dev,
-		                                                      priv->ip6_external_gw);
-	}
+	memset (&route, 0, sizeof (route));
+	route.network = *vpn_gw;
+	route.plen = 128;
+	route.gateway = *parent_gw;
+
+	/* If the VPN gateway is in the same subnet as one of the parent device's
+	 * IP addresses, don't add the host route to it, but a route through the
+	 * parent device.
+	 */
+	if (nm_ip6_config_destination_is_direct (parent_config, vpn_gw, 128))
+		route.gateway = in6addr_any;
+
+	route.source = NM_PLATFORM_SOURCE_VPN;
+	route.metric = nm_device_get_priority (parent_device);
+	nm_ip6_config_add_route (config, &route);
+
+	/* Ensure there's a route to the parent device's gateway through the
+	 * parent device, since if the VPN claims the default route and the VPN
+	 * routes include a subnet that matches the parent device's subnet,
+	 * the parent device's gateway would get routed through the VPN and fail.
+	 */
+	memset (&route, 0, sizeof (route));
+	route.network = *parent_gw;
+	route.plen = 128;
+	route.source = NM_PLATFORM_SOURCE_VPN;
+	route.metric = nm_device_get_priority (parent_device);
+
+	nm_ip6_config_add_route (config, &route);
 }
 
 NMVPNConnection *
 nm_vpn_connection_new (NMConnection *connection,
                        NMDevice *parent_device,
                        const char *specific_object,
-                       gboolean user_requested,
-                       gulong user_uid)
+                       NMAuthSubject *subject)
 {
-	NMVPNConnection *self;
-
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 	g_return_val_if_fail (NM_IS_DEVICE (parent_device), NULL);
 
-	self = (NMVPNConnection *) g_object_new (NM_TYPE_VPN_CONNECTION,
+	return (NMVPNConnection *) g_object_new (NM_TYPE_VPN_CONNECTION,
 	                                         NM_ACTIVE_CONNECTION_INT_CONNECTION, connection,
 	                                         NM_ACTIVE_CONNECTION_INT_DEVICE, parent_device,
 	                                         NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, specific_object,
-	                                         NM_ACTIVE_CONNECTION_INT_USER_REQUESTED, user_requested,
-	                                         NM_ACTIVE_CONNECTION_INT_USER_UID, user_uid,
+	                                         NM_ACTIVE_CONNECTION_INT_SUBJECT, subject,
 	                                         NM_ACTIVE_CONNECTION_VPN, TRUE,
 	                                         NULL);
-	if (self)
-		nm_active_connection_export (NM_ACTIVE_CONNECTION (self));
-
-	return self;
 }
 
 static const char *
@@ -375,14 +585,31 @@ nm_vpn_connection_get_service (NMVPNConnection *connection)
 	return nm_setting_vpn_get_service_type (s_vpn);
 }
 
+static const char *
+vpn_plugin_failure_to_string (NMVPNPluginFailure failure)
+{
+	switch (failure) {
+	case NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED:
+		return "login-failed";
+	case NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED:
+		return "connect-failed";
+	case NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG:
+		return "bad-ip-config";
+	default:
+		break;
+	}
+	return "unknown";
+}
+
 static void
 plugin_failed (DBusGProxy *proxy,
-			   NMVPNPluginFailure plugin_failure,
-			   gpointer user_data)
+               NMVPNPluginFailure plugin_failure,
+               gpointer user_data)
 {
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (user_data);
 
-	nm_log_warn (LOGD_VPN, "VPN plugin failed: %d", plugin_failure);
+	nm_log_warn (LOGD_VPN, "VPN plugin failed: %s (%d)",
+	             vpn_plugin_failure_to_string (plugin_failure), plugin_failure);
 
 	switch (plugin_failure) {
 	case NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED:
@@ -397,7 +624,7 @@ plugin_failed (DBusGProxy *proxy,
 }
 
 static const char *
-vpn_state_to_string (NMVPNServiceState state)
+vpn_service_state_to_string (NMVPNServiceState state)
 {
 	switch (state) {
 	case NM_VPN_SERVICE_STATE_INIT:
@@ -418,6 +645,60 @@ vpn_state_to_string (NMVPNServiceState state)
 	return "unknown";
 }
 
+static const char *state_table[] = {
+	[STATE_UNKNOWN]       = "unknown",
+	[STATE_WAITING]       = "waiting",
+	[STATE_PREPARE]       = "prepare",
+	[STATE_NEED_AUTH]     = "need-auth",
+	[STATE_CONNECT]       = "connect",
+	[STATE_IP_CONFIG_GET] = "ip-config-get",
+	[STATE_PRE_UP]        = "pre-up",
+	[STATE_ACTIVATED]     = "activated",
+	[STATE_DEACTIVATING]  = "deactivating",
+	[STATE_DISCONNECTED]  = "disconnected",
+	[STATE_FAILED]        = "failed",
+};
+
+static const char *
+vpn_state_to_string (VpnState state)
+{
+	if (state >= 0 && state < G_N_ELEMENTS (state_table))
+		return state_table[state];
+	return "unknown";
+}
+
+static const char *
+vpn_reason_to_string (NMVPNConnectionStateReason reason)
+{
+	switch (reason) {
+	case NM_VPN_CONNECTION_STATE_REASON_NONE:
+		return "none";
+	case NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED:
+		return "user-disconnected";
+	case NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED:
+		return "device-disconnected";
+	case NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED:
+		return "service-stopped";
+	case NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID:
+		return "ip-config-invalid";
+	case NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT:
+		return "connect-timeout";
+	case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT:
+		return "service-start-timeout";
+	case NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED:
+		return "service-start-failed";
+	case NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS:
+		return "no-secrets";
+	case NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED:
+		return "login-failed";
+	case NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED:
+		return "connection-removed";
+	default:
+		break;
+	}
+	return "unknown";
+}
+
 static void
 plugin_state_changed (DBusGProxy *proxy,
                       NMVPNServiceState state,
@@ -427,7 +708,7 @@ plugin_state_changed (DBusGProxy *proxy,
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
 
 	nm_log_info (LOGD_VPN, "VPN plugin state changed: %s (%d)",
-	             vpn_state_to_string (state), state);
+	             vpn_service_state_to_string (state), state);
 
 	if (state == NM_VPN_SERVICE_STATE_STOPPED) {
 		/* Clear connection secrets to ensure secrets get requested each time the
@@ -435,107 +716,66 @@ plugin_state_changed (DBusGProxy *proxy,
 		 */
 		nm_connection_clear_secrets (priv->connection);
 
-		switch (nm_vpn_connection_get_vpn_state (connection)) {
-		case NM_VPN_CONNECTION_STATE_PREPARE:
-		case NM_VPN_CONNECTION_STATE_NEED_AUTH:
-		case NM_VPN_CONNECTION_STATE_CONNECT:
-		case NM_VPN_CONNECTION_STATE_IP_CONFIG_GET:
-		case NM_VPN_CONNECTION_STATE_ACTIVATED:
-			nm_log_info (LOGD_VPN, "VPN plugin state change reason: %d", priv->failure_reason);
-			nm_vpn_connection_set_vpn_state (connection,
-			                                 NM_VPN_CONNECTION_STATE_FAILED,
-											 priv->failure_reason);
+		if ((priv->vpn_state >= STATE_WAITING) && (priv->vpn_state <= STATE_ACTIVATED)) {
+			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);
 
 			/* Reset the failure reason */
 			priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_UNKNOWN;
-			break;
-		default:
-			break;
 		}
 	}
 }
 
-static char addr_to_string_buf[INET6_ADDRSTRLEN + 1];
-
-static const char *
-ip_address_to_string (guint32 numeric)
-{
-	struct in_addr temp_addr;
-
-	memset (&addr_to_string_buf, '\0', sizeof (addr_to_string_buf));
-	temp_addr.s_addr = numeric;
-
-	if (inet_ntop (AF_INET, &temp_addr, addr_to_string_buf, INET_ADDRSTRLEN)) {
-		return addr_to_string_buf;
-	} else {
-		nm_log_warn (LOGD_VPN, "error converting IP4 address 0x%X",
-		             ntohl (temp_addr.s_addr));
-		return NULL;
-	}
-}
-
-static const char *
-ip6_address_to_string (const struct in6_addr *addr)
-{
-	memset (addr_to_string_buf, '\0', sizeof (addr_to_string_buf));
-	if (inet_ntop (AF_INET6, addr, addr_to_string_buf, INET6_ADDRSTRLEN)) {
-		return addr_to_string_buf;
-	} else {
-		nm_log_warn (LOGD_VPN, "error converting IP6 address");
-		return NULL;
-	}
-}
-
 static void
 print_vpn_config (NMVPNConnection *connection)
 {
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
-	NMIP4Address *addr;
-	NMIP6Address *addr6;
+	const NMPlatformIP4Address *address4;
+	const NMPlatformIP6Address *address6;
 	char *dns_domain = NULL;
 	guint32 num, i;
+	char buf[NM_UTILS_INET_ADDRSTRLEN];
 
 	if (priv->ip4_external_gw) {
 		nm_log_info (LOGD_VPN, "VPN Gateway: %s",
-		             ip_address_to_string (priv->ip4_external_gw));
+		             nm_utils_inet4_ntop (priv->ip4_external_gw, NULL));
 	} else if (priv->ip6_external_gw) {
 		nm_log_info (LOGD_VPN, "VPN Gateway: %s",
-		             ip6_address_to_string (priv->ip6_external_gw));
-	} 
+		             nm_utils_inet6_ntop (priv->ip6_external_gw, NULL));
+	}
 
-	nm_log_info (LOGD_VPN, "Tunnel Device: %s", priv->ip_iface);
+	nm_log_info (LOGD_VPN, "Tunnel Device: %s", priv->ip_iface ? priv->ip_iface : "(none)");
 
 	if (priv->ip4_config) {
 		nm_log_info (LOGD_VPN, "IPv4 configuration:");
 
-		addr = nm_ip4_config_get_address (priv->ip4_config, 0);
+		address4 = nm_ip4_config_get_address (priv->ip4_config, 0);
 
 		if (priv->ip4_internal_gw)
-			nm_log_info (LOGD_VPN, "  Internal Gateway: %s", ip_address_to_string (priv->ip4_internal_gw));
-		nm_log_info (LOGD_VPN, "  Internal Address: %s", ip_address_to_string (nm_ip4_address_get_address (addr)));
-		nm_log_info (LOGD_VPN, "  Internal Prefix: %d", nm_ip4_address_get_prefix (addr));
-		nm_log_info (LOGD_VPN, "  Internal Point-to-Point Address: %s",
-					 ip_address_to_string (nm_ip4_config_get_ptp_address (priv->ip4_config)));
+			nm_log_info (LOGD_VPN, "  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));
 
 		num = nm_ip4_config_get_num_routes (priv->ip4_config);
 		for (i = 0; i < num; i++) {
-			NMIP4Route *route;
+			const NMPlatformIP4Route *route = nm_ip4_config_get_route (priv->ip4_config, i);
 
-			route = nm_ip4_config_get_route (priv->ip4_config, i);
 			nm_log_info (LOGD_VPN, "  Static Route: %s/%d   Next Hop: %s",
-						 ip_address_to_string (nm_ip4_route_get_dest (route)),
-						 nm_ip4_route_get_prefix (route),
-						 ip_address_to_string (nm_ip4_route_get_next_hop (route)));
+			             nm_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");
+		             nm_ip4_config_get_never_default (priv->ip4_config) ? "yes" : "no");
 
 		num = nm_ip4_config_get_num_nameservers (priv->ip4_config);
 		for (i = 0; i < num; i++) {
 			nm_log_info (LOGD_VPN, "  Internal DNS: %s",
-						 ip_address_to_string (nm_ip4_config_get_nameserver (priv->ip4_config, i)));
+			             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)
@@ -548,34 +788,32 @@ print_vpn_config (NMVPNConnection *connection)
 	if (priv->ip6_config) {
 		nm_log_info (LOGD_VPN, "IPv6 configuration:");
 
-		addr6 = nm_ip6_config_get_address (priv->ip6_config, 0);
+		address6 = nm_ip6_config_get_address (priv->ip6_config, 0);
 
 		if (priv->ip6_internal_gw)
-			nm_log_info (LOGD_VPN, "  Internal Gateway: %s", ip6_address_to_string (priv->ip6_internal_gw));
-		nm_log_info (LOGD_VPN, "  Internal Address: %s", ip6_address_to_string (nm_ip6_address_get_address (addr6)));
-		nm_log_info (LOGD_VPN, "  Internal Prefix: %d", nm_ip6_address_get_prefix (addr6));
-		nm_log_info (LOGD_VPN, "  Internal Point-to-Point Address: %s",
-					 ip6_address_to_string (nm_ip6_config_get_ptp_address (priv->ip6_config)));
+			nm_log_info (LOGD_VPN, "  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));
 
 		num = nm_ip6_config_get_num_routes (priv->ip6_config);
 		for (i = 0; i < num; i++) {
-			NMIP6Route *route;
+			const NMPlatformIP6Route *route = nm_ip6_config_get_route (priv->ip6_config, i);
 
-			route = nm_ip6_config_get_route (priv->ip6_config, i);
 			nm_log_info (LOGD_VPN, "  Static Route: %s/%d   Next Hop: %s",
-						 ip6_address_to_string (nm_ip6_route_get_dest (route)),
-						 nm_ip6_route_get_prefix (route),
-						 ip6_address_to_string (nm_ip6_route_get_next_hop (route)));
+			             nm_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");
+		             nm_ip6_config_get_never_default (priv->ip6_config) ? "yes" : "no");
 
 		num = nm_ip6_config_get_num_nameservers (priv->ip6_config);
 		for (i = 0; i < num; i++) {
 			nm_log_info (LOGD_VPN, "  Internal DNS: %s",
-						 ip6_address_to_string (nm_ip6_config_get_nameserver (priv->ip6_config, i)));
+			             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)
@@ -597,38 +835,59 @@ static gboolean
 nm_vpn_connection_apply_config (NMVPNConnection *connection)
 {
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (connection));
+	NMIP4Config *vpn4_parent_config = NULL;
+	NMIP6Config *vpn6_parent_config = NULL;
 
-	nm_system_iface_set_up (priv->ip_ifindex, TRUE, NULL);
+	if (priv->ip_ifindex > 0) {
+		nm_platform_link_set_up (priv->ip_ifindex);
 
-	if (priv->ip4_config) {
-		if (!nm_system_apply_ip4_config (priv->ip_ifindex, priv->ip4_config,
-		                                 0, NM_IP4_COMPARE_FLAG_ALL))
-			return FALSE;
+		if (priv->ip4_config) {
+			if (!nm_ip4_config_commit (priv->ip4_config, priv->ip_ifindex))
+				return FALSE;
+		}
+
+		if (priv->ip6_config) {
+			if (!nm_ip6_config_commit (priv->ip6_config, priv->ip_ifindex))
+				return FALSE;
+		}
+
+		if (priv->ip4_config)
+			vpn4_parent_config = nm_ip4_config_new ();
+		if (priv->ip6_config)
+			vpn6_parent_config = nm_ip6_config_new ();
+	} else {
+		/* If the VPN didn't return a network interface, it is a route-based
+		 * VPN (like kernel IPSec) and all IP addressing and routing should
+		 * be done on the parent interface instead.
+		 */
+
+		if (priv->ip4_config)
+			vpn4_parent_config = g_object_ref (priv->ip4_config);
+		if (priv->ip6_config)
+			vpn6_parent_config = g_object_ref (priv->ip6_config);
 	}
 
-	if (priv->ip6_config) {
-		if (!nm_system_apply_ip6_config (priv->ip_ifindex, priv->ip6_config,
-		                                 0, NM_IP6_COMPARE_FLAG_ALL))
-			/* FIXME: remove ip4 config */
-			return FALSE;
+	if (vpn4_parent_config) {
+		/* Add any explicit route to the VPN gateway through the parent device */
+		if (priv->ip4_external_gw)
+			add_ip4_vpn_gateway_route (vpn4_parent_config, parent_dev, priv->ip4_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);
 
-	/* Add any explicit route to the VPN gateway through the parent device */
-	if (priv->ip4_external_gw) {
-		priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev,
-		                                                      priv->ip4_external_gw);
-	} else if (priv->ip6_external_gw) {
-		priv->gw_route = nm_system_add_ip6_vpn_gateway_route (priv->parent_dev,
-		                                                      priv->ip6_external_gw);
-	} else {
-		priv->gw_route = NULL;
+		nm_device_set_vpn6_config (parent_dev, vpn6_parent_config);
+		g_object_unref (vpn6_parent_config);
 	}
 
 	nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.",
 	             nm_connection_get_id (priv->connection));
-	nm_vpn_connection_set_vpn_state (connection,
-	                                 NM_VPN_CONNECTION_STATE_ACTIVATED,
-	                                 NM_VPN_CONNECTION_STATE_REASON_NONE);
+	_set_vpn_state (connection, STATE_PRE_UP, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
 	return TRUE;
 }
 
@@ -638,7 +897,7 @@ nm_vpn_connection_config_maybe_complete (NMVPNConnection *connection,
 {
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
 
-	if (priv->ipconfig_timeout == 0) {
+	if (priv->connect_timeout == 0) {
 		/* config_complete() was already called with an error;
 		 * ignore further calls.
 		 */
@@ -653,8 +912,8 @@ nm_vpn_connection_config_maybe_complete (NMVPNConnection *connection,
 		}
 	}
 
-	g_source_remove (priv->ipconfig_timeout);
-	priv->ipconfig_timeout = 0;
+	g_source_remove (priv->connect_timeout);
+	priv->connect_timeout = 0;
 
 	if (success) {
 		print_vpn_config (connection);
@@ -668,11 +927,13 @@ nm_vpn_connection_config_maybe_complete (NMVPNConnection *connection,
 
 	nm_log_warn (LOGD_VPN, "VPN connection '%s' did not receive valid IP config information.",
 	             nm_connection_get_id (priv->connection));
-	nm_vpn_connection_set_vpn_state (connection,
-	                                 NM_VPN_CONNECTION_STATE_FAILED,
-	                                 NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID);
+	_set_vpn_state (connection, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID, FALSE);
 }
 
+#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
 process_generic_config (NMVPNConnection *connection,
                         GHashTable *config_hash)
@@ -680,41 +941,51 @@ process_generic_config (NMVPNConnection *connection,
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
 	GValue *val;
 
+	g_clear_pointer (&priv->ip_iface, g_free);
 	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_TUNDEV);
-	if (val)
-		priv->ip_iface = g_strdup (g_value_get_string (val));
-	else {
-		nm_log_err (LOGD_VPN, "invalid or missing tunnel device received!");
-		nm_vpn_connection_config_maybe_complete (connection, FALSE);
-		return FALSE;
+	if (val) {
+		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);
 	}
 
-	/* Grab the interface index for address/routing operations */
-	priv->ip_ifindex = nm_netlink_iface_to_index (priv->ip_iface);
-	if (priv->ip_ifindex <= 0) {
-		nm_log_err (LOGD_VPN, "(%s): failed to look up VPN interface index", priv->ip_iface);
-		nm_vpn_connection_config_maybe_complete (connection, FALSE);
-		return FALSE;
+	if (priv->ip_iface) {
+		/* Grab the interface index for address/routing operations */
+		priv->ip_ifindex = nm_platform_link_get_ifindex (priv->ip_iface);
+		if (!priv->ip_ifindex) {
+			nm_log_err (LOGD_VPN, "(%s): failed to look up VPN interface index", priv->ip_iface);
+			nm_vpn_connection_config_maybe_complete (connection, FALSE);
+			return FALSE;
+		}
 	}
 
+	g_clear_pointer (&priv->banner, g_free);
 	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_BANNER);
 	if (val) {
-		g_free (priv->banner);
-		priv->banner = g_strdup (g_value_get_string (val));
+		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);
 	}
 
 	/* External world-visible address of the VPN server */
 	priv->ip4_external_gw = 0;
-	priv->ip6_external_gw = NULL;
+	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)) {
-			GByteArray *ba = g_value_get_boxed (val);
-
-			if (ba->len == sizeof (struct in6_addr))
-				priv->ip6_external_gw = g_memdup (ba->data, ba->len);
+		} else 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);
@@ -726,11 +997,14 @@ process_generic_config (NMVPNConnection *connection,
 	 * like it's IP4-specific. So we store it for now and retrieve it
 	 * later in ip4_config_get.
 	 */
+	priv->mtu = 0;
 	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_MTU);
-	if (val)
-		priv->mtu = g_value_get_uint (val);
-	else
-		priv->mtu = 0;
+	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);
+	}
 
 	return TRUE;
 }
@@ -747,19 +1021,48 @@ nm_vpn_connection_config_get (DBusGProxy *proxy,
 	nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) reply received.",
 	             nm_connection_get_id (priv->connection));
 
+	if (priv->vpn_state == STATE_CONNECT)
+		_set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+
 	if (!process_generic_config (connection, config_hash))
 		return;
 
 	/* Note whether to expect IPv4 and IPv6 configs */
 	val = g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_HAS_IP4);
-	priv->has_ip4 = val ? g_value_get_boolean (val) : FALSE;
+	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);
 
 	val = g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_HAS_IP6);
-	priv->has_ip6 = val ? g_value_get_boolean (val) : FALSE;
+	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);
 }
 
+static guint
+vpn_routing_metric (NMVPNConnection *connection)
+{
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+
+	if (priv->ip_ifindex)
+		return NM_PLATFORM_ROUTE_METRIC_DEFAULT;
+	else {
+		NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (connection));
+
+		return nm_device_get_priority (parent_dev);
+	}
+}
+
 static void
 nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
                                   GHashTable *config_hash,
@@ -767,12 +1070,14 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
 {
 	NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
-	NMSettingIP4Config *s_ip4;
-	NMIP4Address *addr;
+	NMPlatformIP4Address address;
 	NMIP4Config *config;
 	GValue *val;
 	int i;
 
+	if (priv->vpn_state == STATE_CONNECT)
+		_set_vpn_state (connection, 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));
@@ -798,10 +1103,10 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
 
 	config = nm_ip4_config_new ();
 
-	addr = nm_ip4_address_new ();
-	nm_ip4_address_set_prefix (addr, 24); /* default to class C */
+	memset (&address, 0, sizeof (address));
+	address.plen = 24;
 	if (priv->ip4_external_gw)
-		nm_ip4_address_set_gateway (addr, priv->ip4_external_gw);
+		nm_ip4_config_set_gateway (config, priv->ip4_external_gw);
 
 	/* Internal address of the VPN subnet's gateway */
 	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_INT_GATEWAY);
@@ -810,21 +1115,21 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
 
 	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_ADDRESS);
 	if (val)
-		nm_ip4_address_set_address (addr, g_value_get_uint (val));
+		address.address = g_value_get_uint (val);
 
 	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_PTP);
 	if (val)
-		nm_ip4_config_set_ptp_address (config, g_value_get_uint (val));
+		address.peer_address = g_value_get_uint (val);
 
 	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX);
 	if (val)
-		nm_ip4_address_set_prefix (addr, g_value_get_uint (val));
+		address.plen = g_value_get_uint (val);
 
-	if (nm_ip4_address_get_address (addr) && nm_ip4_address_get_prefix (addr)) {
-		nm_ip4_config_take_address (config, addr);
+	if (address.address && address.plen) {
+		address.source = NM_PLATFORM_SOURCE_VPN;
+		nm_ip4_config_add_address (config, &address);
 	} else {
 		nm_log_err (LOGD_VPN, "invalid IP4 config received!");
-		nm_ip4_address_unref (addr);
 		g_object_unref (config);
 		nm_vpn_connection_config_maybe_complete (connection, FALSE);
 		return;
@@ -873,25 +1178,29 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
 
 		routes = nm_utils_ip4_routes_from_gvalue (val);
 		for (iter = routes; iter; iter = iter->next) {
-			NMIP4Route *route = iter->data;
+			NMIP4Route *item = iter->data;
+			NMPlatformIP4Route route;
+
+			memset (&route, 0, sizeof (route));
+			route.network = nm_ip4_route_get_dest (item);
+			route.plen = nm_ip4_route_get_prefix (item);
+			route.gateway = nm_ip4_route_get_next_hop (item);
+			route.source = NM_PLATFORM_SOURCE_VPN;
+			route.metric = vpn_routing_metric (connection);
 
 			/* 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
-			    && nm_ip4_route_get_dest (route) == priv->ip4_external_gw
-			    && nm_ip4_route_get_prefix (route) == 32) {
-				nm_ip4_route_unref (route);
+			if (priv->ip4_external_gw && route.network == priv->ip4_external_gw && route.plen == 32)
 				continue;
-			}
 
 			/* Otherwise accept the VPN-provided route */
-			nm_ip4_config_take_route (config, route);
+			nm_ip4_config_add_route (config, &route);
 		}
 
-		g_slist_free (routes);
+		g_slist_free_full (routes, (GDestroyNotify) nm_ip4_route_unref);
 	}
 
 	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT);
@@ -899,10 +1208,13 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
 		nm_ip4_config_set_never_default (config, g_value_get_boolean (val));
 
 	/* Merge in user overrides from the NMConnection's IPv4 setting */
-	s_ip4 = nm_connection_get_setting_ip4_config (priv->connection);
-	nm_utils_merge_ip4_config (config, s_ip4);
+	nm_ip4_config_merge_setting (config,
+	                             nm_connection_get_setting_ip4_config (priv->connection),
+	                             vpn_routing_metric (connection));
 
 	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);
 }
 
@@ -913,8 +1225,7 @@ nm_vpn_connection_ip6_config_get (DBusGProxy *proxy,
 {
 	NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
-	NMSettingIP6Config *s_ip6;
-	NMIP6Address *addr;
+	NMPlatformIP6Address address;
 	NMIP6Config *config;
 	GValue *val;
 	int i;
@@ -922,6 +1233,9 @@ nm_vpn_connection_ip6_config_get (DBusGProxy *proxy,
 	nm_log_info (LOGD_VPN, "VPN connection '%s' (IP6 Config Get) reply received.",
 	             nm_connection_get_id (priv->connection));
 
+	if (priv->vpn_state == STATE_CONNECT)
+		_set_vpn_state (connection, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+
 	if (g_hash_table_size (config_hash) == 0) {
 		priv->has_ip6 = FALSE;
 		nm_vpn_connection_config_maybe_complete (connection, TRUE);
@@ -930,12 +1244,13 @@ nm_vpn_connection_ip6_config_get (DBusGProxy *proxy,
 
 	config = nm_ip6_config_new ();
 
-	addr = nm_ip6_address_new ();
-	nm_ip6_address_set_prefix (addr, 128); /* default to class C */
+	memset (&address, 0, sizeof (address));
+	address.plen = 128;
 	if (priv->ip6_external_gw)
-		nm_ip6_address_set_gateway (addr, priv->ip6_external_gw);
+		nm_ip6_config_set_gateway (config, priv->ip6_external_gw);
 
 	/* 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);
@@ -949,7 +1264,7 @@ nm_vpn_connection_ip6_config_get (DBusGProxy *proxy,
 		GByteArray *ba = g_value_get_boxed (val);
 
 		if (ba->len == sizeof (struct in6_addr))
-			nm_ip6_address_set_address (addr, (struct in6_addr *)ba->data);
+			address.address = *(struct in6_addr *) ba->data;
 	}
 
 	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_PTP);
@@ -957,18 +1272,18 @@ nm_vpn_connection_ip6_config_get (DBusGProxy *proxy,
 		GByteArray *ba = g_value_get_boxed (val);
 
 		if (ba->len == sizeof (struct in6_addr))
-			nm_ip6_config_set_ptp_address (config, (struct in6_addr *)ba->data);
+			address.peer_address = *(struct in6_addr *) ba->data;
 	}
 
 	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_PREFIX);
 	if (val)
-		nm_ip6_address_set_prefix (addr, g_value_get_uint (val));
+		address.plen = g_value_get_uint (val);
 
-	if (nm_ip6_address_get_address (addr) && nm_ip6_address_get_prefix (addr)) {
-		nm_ip6_config_take_address (config, addr);
+	if (!IN6_IS_ADDR_UNSPECIFIED (&address.address) && address.plen) {
+		address.source = NM_PLATFORM_SOURCE_VPN;
+		nm_ip6_config_add_address (config, &address);
 	} else {
 		nm_log_err (LOGD_VPN, "invalid IP6 config received!");
-		nm_ip6_address_unref (addr);
 		g_object_unref (config);
 		nm_vpn_connection_config_maybe_complete (connection, FALSE);
 	}
@@ -1009,26 +1324,29 @@ nm_vpn_connection_ip6_config_get (DBusGProxy *proxy,
 
 		routes = nm_utils_ip6_routes_from_gvalue (val);
 		for (iter = routes; iter; iter = iter->next) {
-			NMIP6Route *route = iter->data;
+			NMIP6Route *item = iter->data;
+			NMPlatformIP6Route route;
+
+			memset (&route, 0, sizeof (route));
+			route.network = *nm_ip6_route_get_dest (item);
+			route.plen = nm_ip6_route_get_prefix (item);
+			route.gateway = *nm_ip6_route_get_next_hop (item);
+			route.source = NM_PLATFORM_SOURCE_VPN;
+			route.metric = vpn_routing_metric (connection);
 
 			/* Ignore host routes to the VPN gateway since NM adds one itself
 			 * below.  Since NM knows more about the routing situation than
 			 * the VPN server, we want to use the NM created route instead of
 			 * whatever the server provides.
 			 */
-			if (   priv->ip6_external_gw
-			    && nm_ip6_route_get_prefix (route) == 128
-				&& memcmp (nm_ip6_route_get_dest (route), priv->ip6_external_gw,
-				           sizeof (struct in6_addr)) == 0) {
-				nm_ip6_route_unref (route);
+			if (priv->ip6_external_gw && IN6_ARE_ADDR_EQUAL (&route.network, priv->ip6_external_gw) && route.plen == 128)
 				continue;
-			}
 
 			/* Otherwise accept the VPN-provided route */
-			nm_ip6_config_take_route (config, route);
+			nm_ip6_config_add_route (config, &route);
 		}
 
-		g_slist_free (routes);
+		g_slist_free_full (routes, (GDestroyNotify) nm_ip6_route_unref);
 	}
 
 	val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_NEVER_DEFAULT);
@@ -1036,57 +1354,96 @@ nm_vpn_connection_ip6_config_get (DBusGProxy *proxy,
 		nm_ip6_config_set_never_default (config, g_value_get_boolean (val));
 
 	/* Merge in user overrides from the NMConnection's IPv6 setting */
-	s_ip6 = nm_connection_get_setting_ip6_config (priv->connection);
-	nm_utils_merge_ip6_config (config, s_ip6);
+	nm_ip6_config_merge_setting (config,
+	                             nm_connection_get_setting_ip6_config (priv->connection),
+	                             vpn_routing_metric (connection));
 
 	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);
 }
 
 static gboolean
-nm_vpn_connection_ip_config_timeout (gpointer user_data)
+connect_timeout_cb (gpointer user_data)
 {
 	NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
 
-	priv->ipconfig_timeout = 0;
+	priv->connect_timeout = 0;
 
-	/* If the activation request's state is still IP_CONFIG_GET and we're
-	 * in this timeout, cancel activation because it's taken too long.
-	 */
-	if (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_IP_CONFIG_GET) {
-		nm_log_warn (LOGD_VPN, "VPN connection '%s' (IP Config Get) timeout exceeded.",
+	/* 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));
-		nm_vpn_connection_set_vpn_state (connection,
-		                                 NM_VPN_CONNECTION_STATE_FAILED,
-		                                 NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT);
+		_set_vpn_state (connection, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT, FALSE);
 	}
 
 	return FALSE;
 }
 
 static void
-nm_vpn_connection_connect_cb (DBusGProxy *proxy, GError *err, gpointer user_data)
+connect_success (NMVPNConnection *connection)
 {
-	NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
 
+	/* 40 second timeout waiting for IP config signal from VPN service */
+	priv->connect_timeout = g_timeout_add_seconds (40, connect_timeout_cb, connection);
+
+	g_hash_table_destroy (priv->connect_hash);
+	priv->connect_hash = NULL;
+}
+
+static void
+connect_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *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));
 
-	if (err) {
-		nm_log_warn (LOGD_VPN, "VPN connection '%s' failed to connect: '%s'.", 
-		             nm_connection_get_id (priv->connection), err->message);
-		nm_vpn_connection_set_vpn_state (connection,
-		                                 NM_VPN_CONNECTION_STATE_FAILED,
-		                                 NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED);
+	dbus_g_proxy_end_call (proxy, call, &err, G_TYPE_INVALID);
+	if (!err) {
+		connect_success (self);
+		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);
+}
+
+static void
+connect_interactive_cb (DBusGProxy *proxy, DBusGProxyCall *call, void *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));
+
+	dbus_g_proxy_end_call (proxy, call, &err, G_TYPE_INVALID);
+	if (!err) {
+		connect_success (self);
+		return;
+	}
+
+	if (dbus_g_error_has_name (err, NM_DBUS_VPN_ERROR_PREFIX "." NM_DBUS_VPN_INTERACTIVE_NOT_SUPPORTED)) {
+		/* 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_vpn_connection_set_vpn_state (connection,
-		                                 NM_VPN_CONNECTION_STATE_IP_CONFIG_GET,
-		                                 NM_VPN_CONNECTION_STATE_REASON_NONE);
-		
-		/* 40 second timeout waiting for IP config signal from VPN service */
-		priv->ipconfig_timeout = g_timeout_add_seconds (40, nm_vpn_connection_ip_config_timeout, connection);
+		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);
+		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE);
 	}
 }
 
@@ -1122,74 +1479,90 @@ static void
 really_activate (NMVPNConnection *connection, const char *username)
 {
 	NMVPNConnectionPrivate *priv;
-	GHashTable *hash;
+	NMAgentManager *agent_mgr;
+	GHashTable *details;
 
 	g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
-	g_return_if_fail (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_NEED_AUTH);
 
 	priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	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);
+	                                   G_TYPE_NONE, G_TYPE_VALUE, G_TYPE_INVALID);
 
-	/* Config signal */
-	dbus_g_proxy_add_signal (priv->proxy, "Config",
-						DBUS_TYPE_G_MAP_OF_VARIANT,
-						G_TYPE_INVALID);
+	dbus_g_proxy_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);
+	                             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_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);
+	                             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_add_signal (priv->proxy, "Ip6Config", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID);
 	dbus_g_proxy_connect_signal (priv->proxy, "Ip6Config",
-						    G_CALLBACK (nm_vpn_connection_ip6_config_get),
-						    connection, NULL);
-
-	hash = _hash_with_username (priv->connection, username);
-	org_freedesktop_NetworkManager_VPN_Plugin_connect_async (priv->proxy,
-	                                                         hash,
-	                                                         nm_vpn_connection_connect_cb,
-	                                                         connection);
-	g_hash_table_destroy (hash);
-
-	nm_vpn_connection_set_vpn_state (connection,
-	                                 NM_VPN_CONNECTION_STATE_CONNECT,
-	                                 NM_VPN_CONNECTION_STATE_REASON_NONE);
+	                             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);
+
+	/* 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
+	 * from the VPN plugin's interactive secrets requests to the VPN authentication
+	 * dialog and we won't get the secrets we need.  In this case fall back to
+	 * the old Connect() call.
+	 */
+	agent_mgr = nm_agent_manager_get ();
+	if (nm_agent_manager_all_agents_have_capability (agent_mgr,
+	                                                 nm_active_connection_get_subject (NM_ACTIVE_CONNECTION (connection)),
+	                                                 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);
+	} 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);
+	}
+	g_object_unref (agent_mgr);
+	g_hash_table_destroy (details);
+
+	_set_vpn_state (connection, STATE_CONNECT, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
 }
 
 void
 nm_vpn_connection_activate (NMVPNConnection *connection)
 {
 	NMVPNConnectionPrivate *priv;
-	NMDBusManager *dbus_mgr;
+	DBusGConnection *bus;
 
 	g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
-	g_return_if_fail (nm_vpn_connection_get_vpn_state (connection) == NM_VPN_CONNECTION_STATE_PREPARE);
 
 	priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
 
-	dbus_mgr = nm_dbus_manager_get ();
-	priv->proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (dbus_mgr),
+	_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);
-	g_object_unref (dbus_mgr);
 
 	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);
+	                             G_CALLBACK (plugin_failed),
+	                             connection, NULL);
 
 	/* StateChanged signal */
 	dbus_g_proxy_add_signal (priv->proxy, "StateChanged", G_TYPE_UINT, G_TYPE_INVALID);
@@ -1197,9 +1570,21 @@ nm_vpn_connection_activate (NMVPNConnection *connection)
 	                             G_CALLBACK (plugin_state_changed),
 	                             connection, NULL);
 
-	nm_vpn_connection_set_vpn_state (connection,
-	                                 NM_VPN_CONNECTION_STATE_NEED_AUTH,
-	                                 NM_VPN_CONNECTION_STATE_REASON_NONE);
+	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);
 }
 
 NMConnection *
@@ -1215,7 +1600,7 @@ nm_vpn_connection_get_vpn_state (NMVPNConnection *connection)
 {
 	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NM_VPN_CONNECTION_STATE_UNKNOWN);
 
-	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->vpn_state;
+	return _state_to_nm_vpn_state (NM_VPN_CONNECTION_GET_PRIVATE (connection)->vpn_state);
 }
 
 const char *
@@ -1258,14 +1643,6 @@ nm_vpn_connection_get_ip_ifindex (NMVPNConnection *connection)
 	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip_ifindex;
 }
 
-NMDevice *
-nm_vpn_connection_get_parent_device (NMVPNConnection *connection)
-{
-	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL);
-
-	return NM_VPN_CONNECTION_GET_PRIVATE (connection)->parent_dev;
-}
-
 guint32
 nm_vpn_connection_get_ip4_internal_gateway (NMVPNConnection *connection)
 {
@@ -1283,38 +1660,46 @@ nm_vpn_connection_get_ip6_internal_gateway (NMVPNConnection *connection)
 }
 
 void
-nm_vpn_connection_fail (NMVPNConnection *connection,
-                        NMVPNConnectionStateReason reason)
+nm_vpn_connection_disconnect (NMVPNConnection *connection,
+                              NMVPNConnectionStateReason reason,
+                              gboolean quitting)
 {
 	g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
 
-	nm_vpn_connection_set_vpn_state (connection,
-	                                 NM_VPN_CONNECTION_STATE_FAILED,
-	                                 reason);
+	_set_vpn_state (connection, STATE_DISCONNECTED, reason, quitting);
 }
 
-void
-nm_vpn_connection_disconnect (NMVPNConnection *connection,
-                              NMVPNConnectionStateReason reason)
+gboolean
+nm_vpn_connection_deactivate (NMVPNConnection *connection,
+                              NMVPNConnectionStateReason reason,
+                              gboolean quitting)
 {
-	g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
+	NMVPNConnectionPrivate *priv;
+	gboolean success = FALSE;
 
-	nm_vpn_connection_set_vpn_state (connection,
-	                                 NM_VPN_CONNECTION_STATE_DISCONNECTED,
-	                                 reason);
+	g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), FALSE);
+
+	priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	if (priv->vpn_state > STATE_UNKNOWN && priv->vpn_state <= STATE_DEACTIVATING) {
+		_set_vpn_state (connection, STATE_DEACTIVATING, reason, quitting);
+		success = TRUE;
+	}
+	return success;
 }
 
 /******************************************************************************/
 
 static void
-plugin_need_secrets_cb  (DBusGProxy *proxy,
-                         char *setting_name,
-                         GError *error,
-                         gpointer user_data)
+plugin_need_secrets_cb  (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
 {
 	NMVPNConnection *self = NM_VPN_CONNECTION (user_data);
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+	GError *error = NULL;
+	char *setting_name;
 
+	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),
@@ -1322,7 +1707,8 @@ plugin_need_secrets_cb  (DBusGProxy *proxy,
 		            priv->secrets_idx + 1,
 		            g_quark_to_string (error->domain),
 		            error->message);
-		nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
+		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
+		g_error_free (error);
 		return;
 	}
 
@@ -1333,13 +1719,13 @@ plugin_need_secrets_cb  (DBusGProxy *proxy,
 			nm_log_err (LOGD_VPN, "(%s/%s) final secrets request failed to provide sufficient secrets",
 			            nm_connection_get_uuid (priv->connection),
 			            nm_connection_get_id (priv->connection));
-			nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
+			_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);
+			get_secrets (self, priv->secrets_idx + 1, NULL);
 		}
 		return;
 	}
@@ -1353,6 +1739,24 @@ plugin_need_secrets_cb  (DBusGProxy *proxy,
 }
 
 static void
+plugin_new_secrets_cb  (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
+{
+	NMVPNConnection *self = NM_VPN_CONNECTION (user_data);
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+	GError *error = NULL;
+
+	if (!dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID)) {
+		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);
+		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
+		g_error_free (error);
+	}
+}
+
+static void
 get_secrets_cb (NMSettingsConnection *connection,
                 guint32 call_id,
                 const char *agent_username,
@@ -1372,41 +1776,54 @@ get_secrets_cb (NMSettingsConnection *connection,
 	if (error) {
 		nm_log_err (LOGD_VPN, "Failed to request VPN secrets #%d: (%d) %s",
 		            priv->secrets_idx + 1, error->code, error->message);
-		nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
+		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
 	} 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));
-
 		/* Cache the username for later */
 		if (agent_username) {
 			g_free (priv->username);
 			priv->username = g_strdup (agent_username);
 		}
 
-		/* Ask the VPN service if more secrets are required */
 		hash = _hash_with_username (priv->connection, priv->username);
-		org_freedesktop_NetworkManager_VPN_Plugin_need_secrets_async (priv->proxy,
-		                                                              hash,
-		                                                              plugin_need_secrets_cb,
-		                                                              self);
+
+		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));
+
+			/* 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);
+		}
+
 		g_hash_table_destroy (hash);
 	}
 }
 
 static void
-get_secrets (NMVPNConnection *self, SecretsReq secrets_idx)
+get_secrets (NMVPNConnection *self,
+             SecretsReq secrets_idx,
+             const char **hints)
 {
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 	NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_NONE;
 	GError *error = NULL;
-	gboolean filter_by_uid;
 
 	g_return_if_fail (secrets_idx < SECRETS_REQ_LAST);
 	priv->secrets_idx = secrets_idx;
 
-	filter_by_uid = nm_active_connection_get_user_requested (NM_ACTIVE_CONNECTION (self));
-
 	nm_log_dbg (LOGD_VPN, "(%s/%s) requesting VPN secrets pass #%d",
 	            nm_connection_get_uuid (priv->connection),
 	            nm_connection_get_id (priv->connection),
@@ -1415,12 +1832,12 @@ get_secrets (NMVPNConnection *self, SecretsReq secrets_idx)
 	switch (priv->secrets_idx) {
 	case SECRETS_REQ_SYSTEM:
 		flags = NM_SETTINGS_GET_SECRETS_FLAG_ONLY_SYSTEM;
-		filter_by_uid = FALSE;
 		break;
 	case SECRETS_REQ_EXISTING:
 		flags = NM_SETTINGS_GET_SECRETS_FLAG_NONE;
 		break;
 	case SECRETS_REQ_NEW:
+	case SECRETS_REQ_INTERACTIVE:
 		flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION;
 		break;
 	default:
@@ -1431,11 +1848,10 @@ get_secrets (NMVPNConnection *self, SecretsReq secrets_idx)
 		flags |= NM_SETTINGS_GET_SECRETS_FLAG_USER_REQUESTED;
 
 	priv->secrets_id = nm_settings_connection_get_secrets (NM_SETTINGS_CONNECTION (priv->connection),
-	                                                       filter_by_uid,
-	                                                       nm_active_connection_get_user_uid (NM_ACTIVE_CONNECTION (self)),
+	                                                       nm_active_connection_get_subject (NM_ACTIVE_CONNECTION (self)),
 	                                                       NM_SETTING_VPN_SETTING_NAME,
 	                                                       flags,
-	                                                       NULL,
+	                                                       hints,
 	                                                       get_secrets_cb,
 	                                                       self,
 	                                                       &error);
@@ -1444,46 +1860,63 @@ get_secrets (NMVPNConnection *self, SecretsReq secrets_idx)
 			nm_log_err (LOGD_VPN, "failed to request VPN secrets #%d: (%d) %s",
 			            priv->secrets_idx + 1, error->code, error->message);
 		}
-		nm_vpn_connection_fail (self, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS);
+		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
 		g_clear_error (&error);
 	}
 }
 
+static void
+plugin_interactive_secrets_required (DBusGProxy *proxy,
+                                     const char *message,
+                                     const char **secrets,
+                                     gpointer user_data)
+{
+	NMVPNConnection *connection = NM_VPN_CONNECTION (user_data);
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection);
+	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);
+
+	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);
+
+	/* Copy hints and add message to the end */
+	hints = g_malloc0 (sizeof (char *) * (secrets_len + 2));
+	for (i = 0; i < secrets_len; i++)
+		hints[i] = g_strdup (secrets[i]);
+	if (message)
+		hints[i] = g_strdup_printf ("x-vpn-message:%s", message);
+
+	get_secrets (connection, SECRETS_REQ_INTERACTIVE, (const char **) hints);
+	g_strfreev (hints);
+}
+
 /******************************************************************************/
 
 static void
 nm_vpn_connection_init (NMVPNConnection *self)
 {
-	NM_VPN_CONNECTION_GET_PRIVATE (self)->vpn_state = NM_VPN_CONNECTION_STATE_PREPARE;
+	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
+
+	priv->vpn_state = STATE_WAITING;
+	priv->secrets_idx = SECRETS_REQ_SYSTEM;
 }
 
 static void
 constructed (GObject *object)
 {
-	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object);
 	NMConnection *connection;
-	NMDevice *device;
 
 	G_OBJECT_CLASS (nm_vpn_connection_parent_class)->constructed (object);
 
 	connection = nm_active_connection_get_connection (NM_ACTIVE_CONNECTION (object));
-	priv->connection = g_object_ref (connection);
-
-	device = (NMDevice *) nm_active_connection_get_device (NM_ACTIVE_CONNECTION (object));
-	g_assert (device);
-
-	priv->parent_dev = g_object_ref (device);
-
-	priv->device_monitor = g_signal_connect (device, "state-changed",
-	                                         G_CALLBACK (device_state_changed),
-	                                         object);
-
-	priv->device_ip4 = g_signal_connect (device, "notify::" NM_DEVICE_IP4_CONFIG,
-	                                     G_CALLBACK (device_ip4_config_changed),
-	                                     object);
-	priv->device_ip6 = g_signal_connect (device, "notify::" NM_DEVICE_IP6_CONFIG,
-	                                     G_CALLBACK (device_ip6_config_changed),
-	                                     object);
+	NM_VPN_CONNECTION_GET_PRIVATE (object)->connection = g_object_ref (connection);
 }
 
 static void
@@ -1491,47 +1924,28 @@ dispose (GObject *object)
 {
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object);
 
-	if (priv->disposed) {
-		G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object);
-		return;
+	if (priv->connect_hash) {
+		g_hash_table_destroy (priv->connect_hash);
+		priv->connect_hash = NULL;
 	}
-	priv->disposed = TRUE;
-
-	if (priv->gw_route)
-		rtnl_route_put (priv->gw_route);
-	if (priv->ip6_internal_gw)
-		g_free (priv->ip6_internal_gw);
-	if (priv->ip6_external_gw)
-		g_free (priv->ip6_external_gw);
-
-	if (priv->device_ip4)
-		g_signal_handler_disconnect (priv->parent_dev, priv->device_ip4);
-	if (priv->device_ip6)
-		g_signal_handler_disconnect (priv->parent_dev, priv->device_ip6);
 
-	if (priv->device_monitor)
-		g_signal_handler_disconnect (priv->parent_dev, priv->device_monitor);
-
-	g_clear_object (&priv->parent_dev);
-
-	if (priv->ip4_config)
-		g_object_unref (priv->ip4_config);
-	if (priv->ip6_config)
-		g_object_unref (priv->ip6_config);
-
-	if (priv->ipconfig_timeout)
-		g_source_remove (priv->ipconfig_timeout);
+	if (priv->connect_timeout) {
+		g_source_remove (priv->connect_timeout);
+		priv->connect_timeout = 0;
+	}
 
-	if (priv->proxy)
-		g_object_unref (priv->proxy);
+	dispatcher_cleanup (NM_VPN_CONNECTION (object));
 
 	if (priv->secrets_id) {
 		nm_settings_connection_cancel_secrets (NM_SETTINGS_CONNECTION (priv->connection),
 		                                       priv->secrets_id);
+		priv->secrets_id = 0;
 	}
 
+	g_clear_object (&priv->ip4_config);
+	g_clear_object (&priv->ip6_config);
+	g_clear_object (&priv->proxy);
 	g_clear_object (&priv->connection);
-	g_free (priv->username);
 
 	G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object);
 }
@@ -1543,25 +1957,48 @@ finalize (GObject *object)
 
 	g_free (priv->banner);
 	g_free (priv->ip_iface);
+	g_free (priv->username);
+	g_free (priv->ip6_internal_gw);
+	g_free (priv->ip6_external_gw);
 
 	G_OBJECT_CLASS (nm_vpn_connection_parent_class)->finalize (object);
 }
 
+static gboolean
+ip_config_valid (VpnState state)
+{
+	return (state == STATE_PRE_UP || state == STATE_ACTIVATED);
+}
+
 static void
 get_property (GObject *object, guint prop_id,
 		    GValue *value, GParamSpec *pspec)
 {
 	NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (object);
+	NMDevice *parent_dev;
 
 	switch (prop_id) {
 	case PROP_VPN_STATE:
-		g_value_set_uint (value, priv->vpn_state);
+		g_value_set_uint (value, _state_to_nm_vpn_state (priv->vpn_state));
 		break;
 	case PROP_BANNER:
 		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, "/");
+		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, "/");
+		break;
 	case PROP_MASTER:
-		g_value_set_boxed (value, nm_device_get_path (priv->parent_dev));
+		parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (object));
+		g_value_set_boxed (value, parent_dev ? nm_device_get_path (parent_dev) : "/");
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1573,6 +2010,7 @@ static void
 nm_vpn_connection_class_init (NMVPNConnectionClass *connection_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (connection_class);
+	NMActiveConnectionClass *active_class = NM_ACTIVE_CONNECTION_CLASS (connection_class);
 
 	g_type_class_add_private (connection_class, sizeof (NMVPNConnectionPrivate));
 
@@ -1581,6 +2019,7 @@ nm_vpn_connection_class_init (NMVPNConnectionClass *connection_class)
 	object_class->constructed = constructed;
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
+	active_class->device_state_changed = device_state_changed;
 
 	g_object_class_override_property (object_class, PROP_MASTER, NM_ACTIVE_CONNECTION_MASTER);
 
@@ -1601,24 +2040,28 @@ nm_vpn_connection_class_init (NMVPNConnectionClass *connection_class)
 		                     NULL,
 		                     G_PARAM_READABLE));
 
+	g_object_class_override_property (object_class, PROP_IP4_CONFIG,
+	                                  NM_ACTIVE_CONNECTION_IP4_CONFIG);
+	g_object_class_override_property (object_class, PROP_IP6_CONFIG,
+	                                  NM_ACTIVE_CONNECTION_IP6_CONFIG);
+
 	/* signals */
 	signals[VPN_STATE_CHANGED] =
 		g_signal_new ("vpn-state-changed",
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
-		              0, NULL, NULL,
-		              _nm_marshal_VOID__UINT_UINT,
+		              0, NULL, NULL, NULL,
 		              G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
 
 	signals[INTERNAL_STATE_CHANGED] =
 		g_signal_new (NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED,
 		              G_OBJECT_CLASS_TYPE (object_class),
 		              G_SIGNAL_RUN_FIRST,
-		              0, NULL, NULL,
-		              _nm_marshal_VOID__UINT_UINT_UINT,
+		              0, NULL, NULL, NULL,
 		              G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
 
-	dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (object_class),
-	                                 &dbus_glib_nm_vpn_connection_object_info);
+	nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
+	                                        G_TYPE_FROM_CLASS (object_class),
+	                                        &dbus_glib_nm_vpn_connection_object_info);
 }
 
diff --git a/src/vpn-manager/nm-vpn-connection.h b/src/vpn-manager/nm-vpn-connection.h
index bf543345..c9c88cda 100644
--- a/src/vpn-manager/nm-vpn-connection.h
+++ b/src/vpn-manager/nm-vpn-connection.h
@@ -26,6 +26,7 @@
 #include <glib-object.h>
 #include "NetworkManagerVPN.h"
 #include "nm-device.h"
+#include "nm-auth-subject.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))
@@ -67,22 +68,24 @@ GType nm_vpn_connection_get_type (void);
 NMVPNConnection * nm_vpn_connection_new (NMConnection *connection,
                                          NMDevice *parent_device,
                                          const char *specific_object,
-                                         gboolean user_requested,
-                                         gulong user_uid);
+                                         NMAuthSubject *subject);
 
 void                 nm_vpn_connection_activate        (NMVPNConnection *connection);
 NMConnection *       nm_vpn_connection_get_connection  (NMVPNConnection *connection);
 NMVPNConnectionState nm_vpn_connection_get_vpn_state   (NMVPNConnection *connection);
 const char *         nm_vpn_connection_get_banner      (NMVPNConnection *connection);
-void                 nm_vpn_connection_fail            (NMVPNConnection *connection,
-                                                        NMVPNConnectionStateReason reason);
+
+gboolean             nm_vpn_connection_deactivate      (NMVPNConnection *connection,
+                                                        NMVPNConnectionStateReason reason,
+                                                        gboolean quitting);
 void                 nm_vpn_connection_disconnect      (NMVPNConnection *connection,
-                                                        NMVPNConnectionStateReason reason);
+                                                        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);
-NMDevice *           nm_vpn_connection_get_parent_device (NMVPNConnection *connection);
 guint32              nm_vpn_connection_get_ip4_internal_gateway (NMVPNConnection *connection);
 struct in6_addr *    nm_vpn_connection_get_ip6_internal_gateway (NMVPNConnection *connection);
 
diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c
index 7743fddd..5550d4d6 100644
--- a/src/vpn-manager/nm-vpn-manager.c
+++ b/src/vpn-manager/nm-vpn-manager.c
@@ -28,17 +28,16 @@
 #include "nm-setting-vpn.h"
 #include "nm-dbus-manager.h"
 #include "NetworkManagerVPN.h"
-#include "nm-marshal.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 {
-	gboolean disposed;
-
 	GHashTable *services;
 	GFileMonitor *monitor;
 	guint monitor_id;
@@ -76,93 +75,47 @@ get_service_by_namefile (NMVPNManager *self, const char *namefile)
 	return NULL;
 }
 
-static NMVPNConnection *
-find_active_vpn_connection_by_connection (NMVPNManager *self, NMConnection *connection)
-{
-	NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
-	GHashTableIter iter;
-	gpointer data;
-	const GSList *active, *aiter;
-	NMVPNConnection *found = NULL;
-
-	g_return_val_if_fail (connection, NULL);
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-
-	g_hash_table_iter_init (&iter, priv->services);
-	while (g_hash_table_iter_next (&iter, NULL, &data) && (found == NULL)) {
-		active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
-		for (aiter = active; aiter; aiter = g_slist_next (aiter)) {
-			NMVPNConnection *vpn = NM_VPN_CONNECTION (aiter->data);
-
-			if (nm_vpn_connection_get_connection (vpn) == connection) {
-				found = vpn;
-				break;
-			}
-		}
-	}
-	return found;
-}
-
-NMActiveConnection *
+gboolean
 nm_vpn_manager_activate_connection (NMVPNManager *manager,
-                                    NMConnection *connection,
-                                    NMDevice *device,
-                                    const char *specific_object,
-                                    gboolean user_requested,
-                                    gulong user_uid,
+                                    NMVPNConnection *vpn,
                                     GError **error)
 {
-	NMSettingVPN *vpn_setting;
+	NMConnection *connection;
+	NMSettingVPN *s_vpn;
 	NMVPNService *service;
-	NMVPNConnection *vpn = NULL;
 	const char *service_name;
+	NMDevice *device;
 
-	g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL);
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
-	g_return_val_if_fail (error != NULL, NULL);
-	g_return_val_if_fail (*error == NULL, NULL);
+	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);
 
+	device = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn));
+	g_assert (device);
 	if (   nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED
 	    && nm_device_get_state (device) != NM_DEVICE_STATE_SECONDARIES) {
-		g_set_error (error,
-		             NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_DEVICE_NOT_ACTIVE,
-		             "%s", "The base device for the VPN connection was not active.");
-		return NULL;
-	}
-
-	vpn_setting = nm_connection_get_setting_vpn (connection);
-	if (!vpn_setting) {
-		g_set_error (error,
-		             NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_CONNECTION_INVALID,
-		             "%s", "The connection was not a VPN connection.");
-		return NULL;
+		g_set_error_literal (error, NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_DEVICE_NOT_ACTIVE,
+		                     "The base device for the VPN connection was not active.");
+		return FALSE;
 	}
 
-	vpn = find_active_vpn_connection_by_connection (manager, connection);
-	if (vpn) {
-		nm_vpn_connection_disconnect (vpn, NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED);
-		vpn = NULL;
-	}
+	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_setting_vpn_get_service_type (vpn_setting);
+	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) {
-		g_set_error (error,
-		             NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_INVALID,
+		g_set_error (error, NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_INVALID,
 		             "The VPN service '%s' was not installed.",
 		             service_name);
-		return NULL;
+		return FALSE;
 	}
 
-	return (NMActiveConnection *) nm_vpn_service_activate (service,
-	                                                       connection,
-	                                                       device,
-	                                                       specific_object,
-	                                                       user_requested,
-	                                                       user_uid,
-	                                                       error);
+	return nm_vpn_service_activate (service, vpn, error);
 }
 
 gboolean
@@ -170,51 +123,7 @@ nm_vpn_manager_deactivate_connection (NMVPNManager *self,
                                       NMVPNConnection *connection,
                                       NMVPNConnectionStateReason reason)
 {
-	NMVPNManagerPrivate *priv;
-	GHashTableIter iter;
-	gpointer data;
-	const GSList *active, *aiter;
-	gboolean success = FALSE;
-
-	g_return_val_if_fail (self, FALSE);
-	g_return_val_if_fail (NM_IS_VPN_MANAGER (self), FALSE);
-	g_return_val_if_fail (connection != NULL, FALSE);
-
-	priv = NM_VPN_MANAGER_GET_PRIVATE (self);
-	g_hash_table_iter_init (&iter, priv->services);
-	while (g_hash_table_iter_next (&iter, NULL, &data) && (success == FALSE)) {
-		active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
-		for (aiter = active; aiter; aiter = g_slist_next (aiter)) {
-			NMVPNConnection *candidate = aiter->data;
-
-			if (connection == candidate) {
-				nm_vpn_connection_disconnect (connection, reason);
-				success = TRUE;
-				break;
-			}
-		}
-	}
-
-	return success;
-}
-
-static char *
-service_name_from_file (const char *path)
-{
-	GKeyFile *kf = NULL;
-	char *service_name = NULL;
-
-	g_return_val_if_fail (g_path_is_absolute (path), NULL);
-
-	if (!g_str_has_suffix (path, ".name"))
-		return NULL;
-
-	kf = g_key_file_new ();
-	if (g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, NULL))
-		service_name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", NULL);
-
-	g_key_file_free (kf);
-	return service_name;
+	return nm_vpn_connection_deactivate (connection, reason, FALSE);
 }
 
 static void
@@ -222,34 +131,32 @@ try_add_service (NMVPNManager *self, const char *namefile)
 {
 	NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
 	NMVPNService *service = NULL;
+	GHashTableIter iter;
 	GError *error = NULL;
 	const char *service_name;
-	char *tmp;
 
 	g_return_if_fail (g_path_is_absolute (namefile));
 
 	/* Make sure we don't add dupes */
-	tmp = service_name_from_file (namefile);
-	if (tmp)
-		service = g_hash_table_lookup (priv->services, tmp);
-	g_free (tmp);
-	if (service)
-		return;
+	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;
+	}
 
-	/* New service, add it */
+	/* New service */
 	service = nm_vpn_service_new (namefile, &error);
-	if (!service) {
+	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);
-		return;
 	}
-
-	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);
 }
 
 static void
@@ -279,7 +186,7 @@ vpn_dir_changed (GFileMonitor *monitor,
 			const char *service_name = nm_vpn_service_get_dbus_service (service);
 
 			/* Stop active VPN connections and destroy the service */
-			nm_vpn_service_connections_stop (service, TRUE,
+			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);
@@ -351,21 +258,36 @@ nm_vpn_manager_init (NMVPNManager *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);
+	}
+}
+
+static void
 dispose (GObject *object)
 {
 	NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (object);
 
-	if (!priv->disposed) {
-		priv->disposed = TRUE;
-
-		if (priv->monitor) {
-			if (priv->monitor_id)
-				g_signal_handler_disconnect (priv->monitor, priv->monitor_id);
-			g_file_monitor_cancel (priv->monitor);
-			g_object_unref (priv->monitor);
-		}
+	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->services) {
+		stop_all_services (NM_VPN_MANAGER (object));
 		g_hash_table_destroy (priv->services);
+		priv->services = NULL;
 	}
 
 	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 1d398ea2..57f947b0 100644
--- a/src/vpn-manager/nm-vpn-manager.h
+++ b/src/vpn-manager/nm-vpn-manager.h
@@ -59,13 +59,9 @@ GType nm_vpn_manager_get_type (void);
 
 NMVPNManager *nm_vpn_manager_get (void);
 
-NMActiveConnection *nm_vpn_manager_activate_connection (NMVPNManager *manager,
-                                                        NMConnection *connection,
-                                                        NMDevice *device,
-                                                        const char *specific_object,
-                                                        gboolean user_requested,
-                                                        gulong user_uid,
-                                                        GError **error);
+gboolean nm_vpn_manager_activate_connection (NMVPNManager *manager,
+                                             NMVPNConnection *vpn,
+                                             GError **error);
 
 gboolean nm_vpn_manager_deactivate_connection (NMVPNManager *manager,
                                                NMVPNConnection *connection,
diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c
index 668cbf2e..824543fa 100644
--- a/src/vpn-manager/nm-vpn-service.c
+++ b/src/vpn-manager/nm-vpn-service.c
@@ -15,7 +15,7 @@
  * 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 - 2012 Red Hat, Inc.
+ * Copyright (C) 2005 - 2014 Red Hat, Inc.
  * Copyright (C) 2005 - 2008 Novell, Inc.
  */
 
@@ -38,30 +38,30 @@
 G_DEFINE_TYPE (NMVPNService, nm_vpn_service, G_TYPE_OBJECT)
 
 typedef struct {
-	gboolean disposed;
-
-	NMDBusManager *dbus_mgr;
 	char *name;
 	char *dbus_service;
 	char *program;
 	char *namefile;
 
-	GPid pid;
-	GSList *connections;
+	NMVPNConnection *active;
+	GSList *pending;
+
 	guint start_timeout;
-	guint quit_timeout;
-	guint child_watch;
-	gulong name_owner_id;
+	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);
+
 NMVPNService *
 nm_vpn_service_new (const char *namefile, GError **error)
 {
-	NMVPNService *self = NULL;
+	NMVPNService *self;
+	NMVPNServicePrivate *priv;
 	GKeyFile *kf;
-	char *dbus_service = NULL, *program = NULL, *name = NULL;
 
 	g_return_val_if_fail (namefile != NULL, NULL);
 	g_return_val_if_fail (g_path_is_absolute (namefile), NULL);
@@ -72,41 +72,31 @@ nm_vpn_service_new (const char *namefile, GError **error)
 		return NULL;
 	}
 
-	dbus_service = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", NULL);
-	if (!dbus_service) {
-		g_set_error (error, 0, 0, "VPN service file %s had no 'service' key", namefile);
-		goto out;
-	}
+	self = (NMVPNService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL);
+	priv = NM_VPN_SERVICE_GET_PRIVATE (self);
+	priv->namefile = g_strdup (namefile);
 
-	program = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "program", NULL);
-	if (!program) {
-		g_set_error (error, 0, 0, "VPN service file %s had no 'program' key", namefile);
-		goto out;
-	}
+	priv->dbus_service = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", error);
+	if (!priv->dbus_service)
+		goto error;
 
-	name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "name", NULL);
-	if (!name) {
-		g_set_error (error, 0, 0, "VPN service file %s had no 'name' key", namefile);
-		goto out;
-	}
+	priv->program = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "program", error);
+	if (!priv->program)
+		goto error;
 
-	self = (NMVPNService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL);
-	if (!self) {
-		g_set_error (error, 0, 0, "out of memory creating VPN service object");
-		goto out;
-	}
+	priv->name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "name", error);
+	if (!priv->name)
+		goto error;
 
-	NM_VPN_SERVICE_GET_PRIVATE (self)->name = g_strdup (name);
-	NM_VPN_SERVICE_GET_PRIVATE (self)->dbus_service = g_strdup (dbus_service);
-	NM_VPN_SERVICE_GET_PRIVATE (self)->program = g_strdup (program);
-	NM_VPN_SERVICE_GET_PRIVATE (self)->namefile = g_strdup (namefile);
+	priv->service_running = nm_dbus_manager_name_has_owner (nm_dbus_manager_get (), priv->dbus_service);
 
- out:
 	g_key_file_free (kf);
-	g_free (dbus_service);
-	g_free (program);
-	g_free (name);
 	return self;
+
+error:
+	g_object_unref (self);
+	g_key_file_free (kf);
+	return NULL;
 }
 
 const char *
@@ -125,46 +115,60 @@ nm_vpn_service_get_name_file (NMVPNService *service)
 	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);
+		} else
+			priv->pending = g_slist_remove (priv->pending, connection);
+		g_object_unref (connection);
+	}
+}
+
 void
-nm_vpn_service_connections_stop (NMVPNService *service,
-                                 gboolean fail,
+nm_vpn_service_stop_connections (NMVPNService *service,
+                                 gboolean quitting,
                                  NMVPNConnectionStateReason reason)
 {
 	NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
-	GSList *iter, *copy;
+	GSList *iter;
 
-	/* Copy because stopping the connection may remove it from the list
-	 * in the the NMVPNService objects' VPN connection state handler.
+	/* Just add priv->active to the beginning of priv->pending,
+	 * since we're going to clear priv->pending immediately anyway.
 	 */
-	copy = g_slist_copy (priv->connections);
-	for (iter = copy; iter; iter = iter->next) {
-		if (fail)
-			nm_vpn_connection_fail (NM_VPN_CONNECTION (iter->data), reason);
-		else
-			nm_vpn_connection_disconnect (NM_VPN_CONNECTION (iter->data), reason);
+	if (priv->active) {
+		priv->pending = g_slist_prepend (priv->pending, priv->active);
+		priv->active = NULL;
 	}
-	g_slist_free (copy);
-}
 
-static void
-clear_quit_timeout (NMVPNService *self)
-{
-	NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
+	for (iter = priv->pending; iter; iter = iter->next) {
+		NMVPNConnection *vpn = NM_VPN_CONNECTION (iter->data);
 
-	if (priv->quit_timeout) {
-		g_source_remove (priv->quit_timeout);
-		priv->quit_timeout = 0;
+		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);
 }
 
-/*
- * nm_vpn_service_child_setup
- *
- * Set the process group ID of the newly forked process
- *
- */
 static void
-nm_vpn_service_child_setup (gpointer user_data G_GNUC_UNUSED)
+_daemon_setup (gpointer user_data G_GNUC_UNUSED)
 {
 	/* We are in the child process at this point */
 	pid_t pid = getpid ();
@@ -177,73 +181,37 @@ nm_vpn_service_child_setup (gpointer user_data G_GNUC_UNUSED)
 	nm_unblock_posix_signals (NULL);
 }
 
-static void
-vpn_service_watch_cb (GPid pid, gint status, gpointer user_data)
-{
-	NMVPNService *service = NM_VPN_SERVICE (user_data);
-	NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
-
-	if (WIFEXITED (status)) {
-		guint err = WEXITSTATUS (status);
-
-		if (err != 0) {
-			nm_log_warn (LOGD_VPN, "VPN service '%s' exited with error: %d",
-			             priv->name, WSTOPSIG (status));
-		}
-	} else if (WIFSTOPPED (status)) {
-		nm_log_warn (LOGD_VPN, "VPN service '%s' stopped unexpectedly with signal %d",
-		             priv->name, WSTOPSIG (status));
-	} else if (WIFSIGNALED (status)) {
-		nm_log_warn (LOGD_VPN, "VPN service '%s' died with signal %d",
-		             priv->name, WTERMSIG (status));
-	} else {
-		nm_log_warn (LOGD_VPN, "VPN service '%s' died from an unknown cause", 
-		             priv->name);
-	}
-
-	priv->pid = 0;
-	priv->child_watch = 0;
-	clear_quit_timeout (service);
-
-	nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
-}
-
 static gboolean
-nm_vpn_service_timeout (gpointer data)
+_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_connections_stop (self, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT);
-	return FALSE;
+	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);
-	g_return_val_if_fail (error != NULL, FALSE);
-	g_return_val_if_fail (*error == NULL, FALSE);
 
 	vpn_argv[0] = priv->program;
 	vpn_argv[1] = NULL;
 
-	success = g_spawn_async (NULL, vpn_argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
-	                         nm_vpn_service_child_setup, NULL, &priv->pid,
-	                         &spawn_error);
+	success = g_spawn_async (NULL, vpn_argv, NULL, 0, _daemon_setup, NULL, &pid, &spawn_error);
 	if (success) {
-		nm_log_info (LOGD_VPN, "VPN service '%s' started (%s), PID %d", 
-		             priv->name, priv->dbus_service, priv->pid);
-
-		priv->child_watch = g_child_watch_add (priv->pid, vpn_service_watch_cb, service);
-		priv->start_timeout = g_timeout_add_seconds (5, nm_vpn_service_timeout, service);
+		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,
@@ -254,7 +222,7 @@ nm_vpn_service_daemon_exec (NMVPNService *service, GError **error)
 		             NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_START_FAILED,
 		             "%s", spawn_error ? spawn_error->message : "unknown g_spawn_async() error");
 
-		nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED);
+		nm_vpn_service_stop_connections (service, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED);
 		if (spawn_error)
 			g_error_free (spawn_error);
 	}
@@ -263,132 +231,88 @@ nm_vpn_service_daemon_exec (NMVPNService *service, GError **error)
 }
 
 static gboolean
-ensure_killed (gpointer data)
+start_active_vpn (NMVPNService *self, GError **error)
 {
-	int pid = GPOINTER_TO_INT (data);
+	NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
 
-	if (kill (pid, 0) == 0)
-		kill (pid, SIGKILL);
+	if (!priv->active)
+		return TRUE;
 
-	/* ensure the child is reaped */
-	nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", pid);
-	waitpid (pid, NULL, 0);
-	nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", pid);
+	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);
+	}
 
-	return FALSE;
+	/* Already started VPN service, waiting for it to appear on D-Bus */
+	return TRUE;
 }
 
 static gboolean
-service_quit (gpointer user_data)
+start_pending_vpn (NMVPNService *self)
 {
-	NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (user_data);
-
-	if (priv->pid) {
-		if (kill (priv->pid, SIGTERM) == 0)
-			g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid));
-		else {
-			kill (priv->pid, SIGKILL);
-
-			/* ensure the child is reaped */
-			nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", priv->pid);
-			waitpid (priv->pid, NULL, 0);
-			nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", priv->pid);
-		}
-		priv->pid = 0;
-	}
-	priv->quit_timeout = 0;
+	NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
 
-	return FALSE;
-}
+	g_assert (priv->active == NULL);
 
-static void
-connection_vpn_state_changed (NMVPNConnection *connection,
-                              NMVPNConnectionState new_state,
-                              NMVPNConnectionState old_state,
-                              NMVPNConnectionStateReason reason,
-                              gpointer user_data)
-{
-	NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (user_data);
+	if (!priv->pending)
+		return TRUE;
 
-	switch (new_state) {
-	case NM_VPN_CONNECTION_STATE_FAILED:
-	case NM_VPN_CONNECTION_STATE_DISCONNECTED:
-		/* Remove the connection from our list */
-		priv->connections = g_slist_remove (priv->connections, connection);
-		g_object_unref (connection);
+	/* Make next VPN active */
+	priv->active = g_slist_nth_data (priv->pending, 0);
+	priv->pending = g_slist_remove (priv->pending, priv->active);
 
-		if (priv->connections == NULL) {
-			/* Tell the service to quit in a few seconds */
-			if (!priv->quit_timeout)
-				priv->quit_timeout = g_timeout_add_seconds (5, service_quit, user_data);
-		}
-		break;
-	default:
-		break;
-	}
+	return start_active_vpn (self, NULL);
 }
 
-NMVPNConnection *
+gboolean
 nm_vpn_service_activate (NMVPNService *service,
-                         NMConnection *connection,
-                         NMDevice *device,
-                         const char *specific_object,
-                         gboolean user_requested,
-                         gulong user_uid,
+                         NMVPNConnection *vpn,
                          GError **error)
 {
-	NMVPNConnection *vpn;
 	NMVPNServicePrivate *priv;
 
-	g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL);
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-	g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
-	g_return_val_if_fail (error != NULL, NULL);
-	g_return_val_if_fail (*error == NULL, NULL);
+	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);
 
-	clear_quit_timeout (service);
-
-	vpn = nm_vpn_connection_new (connection, device, specific_object, user_requested, user_uid);
 	g_signal_connect (vpn, NM_VPN_CONNECTION_INTERNAL_STATE_CHANGED,
 	                  G_CALLBACK (connection_vpn_state_changed),
 	                  service);
 
-	priv->connections = g_slist_prepend (priv->connections, g_object_ref (vpn));
+	/* Queue up the new VPN connection */
+	priv->pending = g_slist_append (priv->pending, g_object_ref (vpn));
 
-	if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, priv->dbus_service)) {
-		// FIXME: fill in error when errors happen
-		nm_vpn_connection_activate (vpn);
-	} else if (priv->start_timeout == 0) {
-		nm_log_info (LOGD_VPN, "Starting VPN service '%s'...", priv->name);
-		if (!nm_vpn_service_daemon_exec (service, error))
-			vpn = NULL;
+	/* 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;
 	}
 
-	return vpn;
-}
-
-const GSList *
-nm_vpn_service_get_active_connections (NMVPNService *service)
-{
-	g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL);
-
-	return NM_VPN_SERVICE_GET_PRIVATE (service)->connections;
+	/* Otherwise start the next VPN */
+	return start_pending_vpn (service);
 }
 
 static void
-nm_vpn_service_name_owner_changed (NMDBusManager *mgr,
-							const char *name,
-							const char *old,
-							const char *new,
-							gpointer user_data)
+_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;
-	gboolean new_owner_good;
-	GSList *iter;
+	gboolean old_owner_good, new_owner_good, success;
 
 	if (strcmp (name, priv->dbus_service))
 		return;
@@ -399,20 +323,21 @@ nm_vpn_service_name_owner_changed (NMDBusManager *mgr,
 		priv->start_timeout = 0;
 	}
 
-	old_owner_good = (old && (strlen (old) > 0));
-	new_owner_good = (new && (strlen (new) > 0));
+	old_owner_good = (old && old[0]);
+	new_owner_good = (new && new[0]);
 
 	if (!old_owner_good && new_owner_good) {
-		/* service just appeared */
+		/* service appeared */
+		priv->service_running = TRUE;
 		nm_log_info (LOGD_VPN, "VPN service '%s' appeared; activating connections", priv->name);
-		clear_quit_timeout (service);
-
-		for (iter = priv->connections; iter; iter = iter->next)
-			nm_vpn_connection_activate (NM_VPN_CONNECTION (iter->data));
+		/* 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_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
+		nm_vpn_service_stop_connections (service, FALSE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
 	}
 }
 
@@ -421,13 +346,10 @@ nm_vpn_service_name_owner_changed (NMDBusManager *mgr,
 static void
 nm_vpn_service_init (NMVPNService *self)
 {
-	NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
-
-	priv->dbus_mgr = nm_dbus_manager_get ();
-	priv->name_owner_id = g_signal_connect (priv->dbus_mgr,
-	                                        NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
-	                                        G_CALLBACK (nm_vpn_service_name_owner_changed),
-	                                        self);
+	g_signal_connect (nm_dbus_manager_get (),
+	                  NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
+	                  G_CALLBACK (_name_owner_changed),
+	                  self);
 }
 
 static void
@@ -436,34 +358,33 @@ dispose (GObject *object)
 	NMVPNService *self = NM_VPN_SERVICE (object);
 	NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
 
-	if (priv->disposed)
-		goto out;
-	priv->disposed = TRUE;
-
-	if (priv->start_timeout)
+	if (priv->start_timeout) {
 		g_source_remove (priv->start_timeout);
+		priv->start_timeout = 0;
+	}
 
-	nm_vpn_service_connections_stop (NM_VPN_SERVICE (object),
-	                                 FALSE,
-	                                 NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
-
-	g_signal_handler_disconnect (priv->dbus_mgr, priv->name_owner_id);
+	/* VPNService owner is required to stop connections before releasing */
+	g_assert (priv->active == NULL);
+	g_assert (priv->pending == NULL);
 
-	if (priv->child_watch)
-		g_source_remove (priv->child_watch);
+	g_signal_handlers_disconnect_by_func (nm_dbus_manager_get (),
+	                                      G_CALLBACK (_name_owner_changed),
+	                                      self);
 
-	clear_quit_timeout (self);
-	service_quit (self);
+	G_OBJECT_CLASS (nm_vpn_service_parent_class)->dispose (object);
+}
 
-	g_object_unref (priv->dbus_mgr);
+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);
 
-out:
-	G_OBJECT_CLASS (nm_vpn_service_parent_class)->dispose (object);
+	G_OBJECT_CLASS (nm_vpn_service_parent_class)->finalize (object);
 }
 
 static void
@@ -475,4 +396,5 @@ nm_vpn_service_class_init (NMVPNServiceClass *service_class)
 
 	/* 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
index e883d55f..4545d1f0 100644
--- a/src/vpn-manager/nm-vpn-service.h
+++ b/src/vpn-manager/nm-vpn-service.h
@@ -34,8 +34,6 @@
 #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))
 
-#define VPN_CONNECTION_GROUP "VPN Connection"
-
 typedef struct {
 	GObject parent;
 } NMVPNService;
@@ -54,18 +52,12 @@ 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);
 
-NMVPNConnection * nm_vpn_service_activate (NMVPNService *service,
-                                           NMConnection *connection,
-                                           NMDevice *device,
-                                           const char *specific_object,
-                                           gboolean user_requested,
-                                           gulong user_uid,
-                                           GError **error);
-
-const GSList *nm_vpn_service_get_active_connections (NMVPNService *service);
+gboolean nm_vpn_service_activate (NMVPNService *service,
+                                  NMVPNConnection *vpn,
+                                  GError **error);
 
-void nm_vpn_service_connections_stop (NMVPNService *service,
-                                      gboolean fail,
+void nm_vpn_service_stop_connections (NMVPNService *service,
+                                      gboolean quitting,
                                       NMVPNConnectionStateReason reason);
 
 #endif  /* NM_VPN_VPN_SERVICE_H */