[PATCH v3 05/42] vpn: New vpn daemon that handles vpn connections and clients

Jukka Rissanen jukka.rissanen at linux.intel.com
Wed Nov 7 03:48:31 PST 2012


---
 .gitignore                     |    4 +
 Makefile.am                    |   92 ++-
 Makefile.plugins               |  120 +--
 configure.ac                   |    9 +-
 include/vpn-dbus.h             |   57 ++
 plugins/l2tp.c                 |  532 -------------
 plugins/openconnect.c          |  281 -------
 plugins/openvpn.c              |  327 --------
 plugins/pptp.c                 |  338 --------
 plugins/vpn.c                  |  534 -------------
 plugins/vpn.h                  |   48 --
 plugins/vpnc.c                 |  343 --------
 src/plugin.c                   |    2 +-
 vpn/connman-vpn.service.in     |   12 +
 vpn/main.c                     |  257 ++++++
 vpn/net.connman.vpn.service.in |    5 +
 vpn/plugins/l2tp.c             |  533 +++++++++++++
 vpn/plugins/openconnect.c      |  282 +++++++
 vpn/plugins/openvpn.c          |  328 ++++++++
 vpn/plugins/pptp.c             |  339 ++++++++
 vpn/plugins/vpn.c              |  535 +++++++++++++
 vpn/plugins/vpn.h              |   63 ++
 vpn/plugins/vpnc.c             |  345 +++++++++
 vpn/vpn-dbus.conf              |   15 +
 vpn/vpn-ipconfig.c             |  450 +++++++++++
 vpn/vpn-manager.c              |  142 ++++
 vpn/vpn-polkit.conf            |   11 +
 vpn/vpn-polkit.policy          |   29 +
 vpn/vpn-provider.c             | 1680 ++++++++++++++++++++++++++++++++++++++++
 vpn/vpn-provider.h             |  121 +++
 vpn/vpn-rtnl.c                 | 1185 ++++++++++++++++++++++++++++
 vpn/vpn-rtnl.h                 |   65 ++
 vpn/vpn.h                      |   98 +++
 vpn/vpn.ver                    |    8 +
 34 files changed, 6733 insertions(+), 2457 deletions(-)
 create mode 100644 include/vpn-dbus.h
 delete mode 100644 plugins/l2tp.c
 delete mode 100644 plugins/openconnect.c
 delete mode 100644 plugins/openvpn.c
 delete mode 100644 plugins/pptp.c
 delete mode 100644 plugins/vpn.c
 delete mode 100644 plugins/vpn.h
 delete mode 100644 plugins/vpnc.c
 create mode 100644 vpn/connman-vpn.service.in
 create mode 100644 vpn/main.c
 create mode 100644 vpn/net.connman.vpn.service.in
 create mode 100644 vpn/plugins/l2tp.c
 create mode 100644 vpn/plugins/openconnect.c
 create mode 100644 vpn/plugins/openvpn.c
 create mode 100644 vpn/plugins/pptp.c
 create mode 100644 vpn/plugins/vpn.c
 create mode 100644 vpn/plugins/vpn.h
 create mode 100644 vpn/plugins/vpnc.c
 create mode 100644 vpn/vpn-dbus.conf
 create mode 100644 vpn/vpn-ipconfig.c
 create mode 100644 vpn/vpn-manager.c
 create mode 100644 vpn/vpn-polkit.conf
 create mode 100644 vpn/vpn-polkit.policy
 create mode 100644 vpn/vpn-provider.c
 create mode 100644 vpn/vpn-provider.h
 create mode 100644 vpn/vpn-rtnl.c
 create mode 100644 vpn/vpn-rtnl.h
 create mode 100644 vpn/vpn.h
 create mode 100644 vpn/vpn.ver

diff --git a/.gitignore b/.gitignore
index b8f5d3c..94b3b17 100644
--- a/.gitignore
+++ b/.gitignore
@@ -66,3 +66,7 @@ doc/*.sgml
 doc/version.xml
 doc/xml
 doc/html
+
+vpn/builtin.h
+vpn/connman-vpnd
+vpn/connman-vpn.service
diff --git a/Makefile.am b/Makefile.am
index a489c85..eddb644 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -14,7 +14,7 @@ nodist_include_HEADERS = include/version.h
 
 noinst_HEADERS = include/rtnl.h include/task.h \
 			include/dbus.h include/option.h \
-			include/provider.h \
+			include/provider.h include/vpn-dbus.h \
 			include/utsname.h include/timeserver.h include/proxy.h \
 			include/technology.h include/setting.h
 
@@ -46,10 +46,20 @@ dbusconfdir = @DBUS_CONFDIR@
 
 dbusconf_DATA = src/connman.conf $(nmcompat_conf)
 
+if VPN
+dbusconf_DATA += vpn/connman-vpn-dbus.conf
+dbusservicedir = @DBUS_DATADIR@
+dbusservice_DATA = vpn/net.connman.vpn.service
+endif
+
 if SYSTEMD
 systemdunitdir = @SYSTEMD_UNITDIR@
 
 systemdunit_DATA = src/connman.service
+
+if VPN
+systemdunit_DATA += vpn/connman-vpn.service
+endif
 endif
 endif
 
@@ -93,12 +103,47 @@ src_connmand_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ \
 src_connmand_LDFLAGS = -Wl,--export-dynamic \
 				-Wl,--version-script=$(srcdir)/src/connman.ver
 
+if VPN
+vpn_plugin_LTLIBRARIES =
+
+vpn_plugin_objects =
+
+builtin_vpn_modules =
+builtin_vpn_sources =
+builtin_vpn_libadd =
+builtin_vpn_cflags =
+
+sbin_PROGRAMS += vpn/connman-vpnd
+
+vpn_connman_vpnd_SOURCES = $(gdbus_sources) $(builtin_vpn_sources) \
+			$(gweb_sources) vpn/vpn.ver vpn/main.c vpn/vpn.h \
+			src/log.c src/error.c src/plugin.c src/task.c \
+			vpn/vpn-manager.c vpn/vpn-provider.c \
+			vpn/vpn-provider.h vpn/vpn-rtnl.h \
+			vpn/vpn-ipconfig.c src/inet.c vpn/vpn-rtnl.c \
+			src/dbus.c src/storage.c src/ipaddress.c
+
+vpn_connman_vpnd_LDADD = $(builtin_vpn_libadd) @GLIB_LIBS@ @DBUS_LIBS@ \
+				@GNUTLS_LIBS@ -lresolv -ldl
+
+vpn_connman_vpnd_LDFLAGS = -Wl,--export-dynamic \
+				-Wl,--version-script=$(srcdir)/vpn/vpn.ver
+endif
+
 BUILT_SOURCES = $(local_headers) src/builtin.h
 
+if VPN
+BUILT_SOURCES += vpn/builtin.h
+endif
+
 CLEANFILES = src/connman.conf $(BUILT_SOURCES)
 
 statedir = $(localstatedir)/run/connman
 
+if VPN
+vpn_plugindir = $(libdir)/connman/plugins-vpn
+endif
+
 plugindir = $(libdir)/connman/plugins
 
 scriptdir = $(libdir)/connman/scripts
@@ -108,11 +153,17 @@ storagedir = $(localstatedir)/lib/connman
 configdir = ${sysconfdir}/connman
 
 if MAINTAINER_MODE
+if VPN
+build_vpn_plugindir = $(abs_top_srcdir)/vpn/plugins/.libs
+endif
 build_plugindir = $(abs_top_srcdir)/plugins/.libs
 build_scriptdir = $(abs_top_srcdir)/scripts
 else
 build_plugindir = $(plugindir)
 build_scriptdir = $(scriptdir)
+if VPN
+build_vpn_plugindir = $(vpn_plugindir)
+endif
 endif
 
 AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @XTABLES_CFLAGS@ \
@@ -124,11 +175,38 @@ AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @XTABLES_CFLAGS@ \
 				-DSTORAGEDIR=\""$(storagedir)\"" \
 				-DCONFIGDIR=\""$(configdir)\""
 
+if VPN
+AM_CPPFLAGS = -I$(builddir)/include -I$(srcdir)/gdbus
+else
 AM_CPPFLAGS = -I$(builddir)/include -I$(builddir)/src -I$(srcdir)/gdbus
+endif
+
+src_connmand_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ @XTABLES_CFLAGS@ \
+				@GNUTLS_CFLAGS@ $(builtin_cflags) \
+				-DCONNMAN_PLUGIN_BUILTIN \
+				-DSTATEDIR=\""$(statedir)"\" \
+				-DPLUGINDIR=\""$(build_plugindir)"\" \
+				-DSCRIPTDIR=\""$(build_scriptdir)"\" \
+				-DSTORAGEDIR=\""$(storagedir)\"" \
+				-DCONFIGDIR=\""$(configdir)\"" \
+				-I$(builddir)/src
 
 EXTRA_DIST = src/genbuiltin src/connman-dbus.conf src/connman-polkit.conf \
 						plugins/connman-nmcompat.conf
 
+if VPN
+vpn_connman_vpnd_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ \
+				$(builtin_vpn_cflags) \
+				-DCONNMAN_PLUGIN_BUILTIN \
+				-DSTATEDIR=\""$(statedir)"\" \
+				-DPLUGINDIR=\""$(build_vpn_plugindir)"\" \
+				-DSCRIPTDIR=\""$(build_scriptdir)"\" \
+				-DSTORAGEDIR=\""$(storagedir)\"" \
+				-DCONFIGDIR=\""$(configdir)\"" \
+				-I$(srcdir)/vpn
+
+EXTRA_DIST += vpn/vpn-dbus.conf vpn/vpn-polkit.conf vpn/net.connman.vpn.service
+endif
 
 script_DATA =
 script_PROGRAMS =
@@ -275,6 +353,9 @@ MAINTAINERCLEANFILES = Makefile.in \
 src/builtin.h: src/genbuiltin $(builtin_sources)
 	$(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_modules) > $@
 
+vpn/builtin.h: src/genbuiltin $(builtin_vpn_sources)
+	$(AM_V_GEN)$(srcdir)/src/genbuiltin $(builtin_vpn_modules) > $@
+
 src/connman.conf: src/connman-dbus.conf src/connman-polkit.conf
 if POLKIT
 	$(AM_V_GEN)cp $(srcdir)/src/connman-polkit.conf $@
@@ -282,6 +363,15 @@ else
 	$(AM_V_GEN)cp $(srcdir)/src/connman-dbus.conf $@
 endif
 
+if VPN
+vpn/connman-vpn-dbus.conf: vpn/vpn-dbus.conf vpn/vpn-polkit.conf
+if POLKIT
+	$(AM_V_GEN)cp $(srcdir)/vpn/vpn-polkit.conf $@
+else
+	$(AM_V_GEN)cp $(srcdir)/vpn/vpn-dbus.conf $@
+endif
+endif
+
 include/connman/version.h: include/version.h
 	$(AM_V_at)$(MKDIR_P) include/connman
 	$(AM_V_GEN)$(LN_S) $(abs_top_builddir)/$< $@
diff --git a/Makefile.plugins b/Makefile.plugins
index a6cda48..2c5d66d 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -51,94 +51,96 @@ builtin_modules += dundee
 builtin_sources += plugins/dundee.c
 endif
 
+if VPN
 if OPENCONNECT
 if OPENCONNECT_BUILTIN
-builtin_modules += openconnect
-builtin_sources += plugins/openconnect.c
-builtin_vpn_sources = plugins/vpn.c plugins/vpn.h
-builtin_cflags += -DOPENCONNECT=\"@OPENCONNECT@\"
+builtin_vpn_modules += openconnect
+builtin_vpn_sources += vpn/plugins/openconnect.c
+builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
+builtin_vpn_cflags += -DOPENCONNECT=\"@OPENCONNECT@\"
 else
-plugin_LTLIBRARIES += plugins/openconnect.la
-plugin_objects += $(plugins_openconnect_la_OBJECTS)
-plugins_openconnect_la_SOURCES = plugins/vpn.h plugins/vpn.c \
-						plugins/openconnect.c
-plugins_openconnect_la_CFLAGS = $(plugin_cflags) -DOPENCONNECT=\"@OPENCONNECT@\" \
+vpn_plugin_LTLIBRARIES += vpn/plugins/openconnect.la
+vpn_plugin_objects += $(plugins_openconnect_la_OBJECTS)
+vpn_plugins_openconnect_la_SOURCES = vpn/plugins/vpn.h vpn/plugins/vpn.c \
+						vpn/plugins/openconnect.c
+vpn_plugins_openconnect_la_CFLAGS = $(plugin_cflags) \
+					-DOPENCONNECT=\"@OPENCONNECT@\" \
 					-DSTATEDIR=\""$(statedir)"\" \
 					-DSCRIPTDIR=\""$(build_scriptdir)"\"
-plugins_openconnect_la_LDFLAGS = $(plugin_ldflags)
+vpn_plugins_openconnect_la_LDFLAGS = $(plugin_ldflags)
 endif
 endif
 
 if OPENVPN
 if OPENVPN_BUILTIN
-builtin_modules += openvpn
-builtin_sources += plugins/openvpn.c
-builtin_vpn_sources = plugins/vpn.c plugins/vpn.h
-builtin_cflags += -DOPENVPN=\"@OPENVPN@\"
+builtin_vpn_modules += openvpn
+builtin_vpn_sources += vpn/plugins/openvpn.c
+builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
+builtin_vpn_cflags += -DOPENVPN=\"@OPENVPN@\"
 else
-plugin_LTLIBRARIES += plugins/openvpn.la
-plugin_objects += $(plugins_openvpn_la_OBJECTS)
-plugins_openvpn_la_SOURCES = plugins/vpn.h plugins/vpn.c \
-						plugins/openvpn.c
-plugins_openvpn_la_CFLAGS = $(plugin_cflags) -DOPENVPN=\"@OPENVPN@\" \
+vpn_plugin_LTLIBRARIES += vpn/plugins/openvpn.la
+vpn_plugin_objects += $(plugins_openvpn_la_OBJECTS)
+vpn_plugins_openvpn_la_SOURCES = vpn/plugins/vpn.h vpn/plugins/vpn.c \
+						vpn/plugins/openvpn.c
+vpn_plugins_openvpn_la_CFLAGS = $(plugin_cflags) -DOPENVPN=\"@OPENVPN@\" \
 					-DSTATEDIR=\""$(statedir)"\" \
 					-DSCRIPTDIR=\""$(build_scriptdir)"\"
-plugins_openvpn_la_LDFLAGS = $(plugin_ldflags)
+vpn_plugins_openvpn_la_LDFLAGS = $(plugin_ldflags)
 endif
 endif
 
 if VPNC
 if VPNC_BUILTIN
-builtin_modules += vpnc
-builtin_sources += plugins/vpnc.c
-builtin_vpn_sources = plugins/vpn.c plugins/vpn.h
-builtin_cflags += -DVPNC=\"@VPNC@\"
+builtin_vpn_modules += vpnc
+builtin_vpn_sources += vpn/plugins/vpnc.c
+builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
+builtin_vpn_cflags += -DVPNC=\"@VPNC@\"
 else
-plugin_LTLIBRARIES += plugins/vpnc.la
-plugin_objects += $(plugins_vpnc_la_OBJECTS)
-plugins_vpnc_la_SOURCES = plugins/vpn.h plugins/vpn.c \
-						plugins/vpnc.c
-plugins_vpnc_la_CFLAGS = $(plugin_cflags) -DVPNC=\"@VPNC@\" \
+vpn_plugin_LTLIBRARIES += vpn/plugins/vpnc.la
+vpn_plugin_objects += $(plugins_vpnc_la_OBJECTS)
+vpn_plugins_vpnc_la_SOURCES = vpn/plugins/vpn.h vpn/plugins/vpn.c \
+						vpn/plugins/vpnc.c
+vpn_plugins_vpnc_la_CFLAGS = $(plugin_cflags) -DVPNC=\"@VPNC@\" \
 					-DSTATEDIR=\""$(statedir)"\" \
 					-DSCRIPTDIR=\""$(build_scriptdir)"\"
-plugins_vpnc_la_LDFLAGS = $(plugin_ldflags)
+vpn_plugins_vpnc_la_LDFLAGS = $(plugin_ldflags)
 endif
 endif
 
 if L2TP
 if L2TP_BUILTIN
-builtin_modules += l2tp
-builtin_sources += plugins/l2tp.c
-builtin_vpn_sources = plugins/vpn.c plugins/vpn.h
-builtin_cflags += -DL2TP=\"@L2TP@\"
+builtin_vpn_modules += l2tp
+builtin_vpn_sources += vpn/plugins/l2tp.c
+builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
+builtin_vpn_cflags += -DL2TP=\"@L2TP@\"
 else
-plugin_LTLIBRARIES += plugins/l2tp.la
-plugin_objects += $(plugins_l2tp_la_OBJECTS)
-plugins_l2tp_la_SOURCES = plugins/vpn.h plugins/vpn.c \
-						plugins/l2tp.c
-plugins_l2tp_la_CFLAGS = $(plugin_cflags) -DL2TP=\"@L2TP@\" \
+vpn_plugin_LTLIBRARIES += vpn/plugins/l2tp.la
+vpn_plugin_objects += $(plugins_l2tp_la_OBJECTS)
+vpn_plugins_l2tp_la_SOURCES = vpn/plugins/vpn.h vpn/plugins/vpn.c \
+						vpn/plugins/l2tp.c
+vpn_plugins_l2tp_la_CFLAGS = $(plugin_cflags) -DL2TP=\"@L2TP@\" \
 					-DSTATEDIR=\""$(statedir)"\" \
 					-DSCRIPTDIR=\""$(build_scriptdir)"\"
-plugins_l2tp_la_LDFLAGS = $(plugin_ldflags)
+vpn_plugins_l2tp_la_LDFLAGS = $(plugin_ldflags)
 endif
 endif
 
 if PPTP
 if PPTP_BUILTIN
-builtin_modules += pptp
-builtin_sources += plugins/pptp.c
-builtin_vpn_sources = plugins/vpn.c plugins/vpn.h
-builtin_cflags += -DPPPD=\"@PPPD@\" -DPPTP=\"@PPTP@\"
+builtin_vpn_modules += pptp
+builtin_vpn_sources += vpn/plugins/pptp.c
+builtin_vpn_source = vpn/plugins/vpn.c vpn/plugins/vpn.h
+builtin_vpn_cflags += -DPPPD=\"@PPPD@\" -DPPTP=\"@PPTP@\"
 else
-plugin_LTLIBRARIES += plugins/pptp.la
-plugin_objects += $(plugins_pptp_la_OBJECTS)
-plugins_pptp_la_SOURCES = plugins/vpn.h plugins/vpn.c \
-						plugins/pptp.c
-plugins_pptp_la_CFLAGS = $(plugin_cflags) -DPPPD=\"@PPPD@\" \
+vpn_plugin_LTLIBRARIES += vpn/plugins/pptp.la
+vpn_plugin_objects += $(plugins_pptp_la_OBJECTS)
+vpn_plugins_pptp_la_SOURCES = vpn/plugins/vpn.h vpn/plugins/vpn.c \
+						vpn/plugins/pptp.c
+vpn_plugins_pptp_la_CFLAGS = $(plugin_cflags) -DPPPD=\"@PPPD@\" \
 					-DPPTP=\"@PPTP@\" \
 					-DSTATEDIR=\""$(statedir)"\" \
 					-DSCRIPTDIR=\""$(build_scriptdir)"\"
-plugins_pptp_la_LDFLAGS = $(plugin_ldflags)
+vpn_plugins_pptp_la_LDFLAGS = $(plugin_ldflags)
 endif
 endif
 
@@ -154,7 +156,10 @@ scripts_libppp_plugin_la_LIBADD = @DBUS_LIBS@
 endif
 endif
 
-builtin_sources += $(builtin_vpn_sources)
+if VPN
+builtin_vpn_sources += $(builtin_vpn_source)
+endif
+endif
 
 if PACRUNNER
 builtin_modules += pacrunner
@@ -169,6 +174,10 @@ if DATAFILES
 policydir = @POLKIT_DATADIR@
 
 policy_DATA = plugins/net.connman.policy
+
+if VPN
+policy_DATA += vpn/net.connman.vpn.policy
+endif
 endif
 endif
 
@@ -241,3 +250,12 @@ plugins/net.connman.policy: plugins/polkit.policy
 if POLKIT
 	$(AM_V_GEN)cp $< $@
 endif
+
+if VPN
+EXTRA_DIST += vpn/vpn-polkit.policy
+
+vpn/net.connman.vpn.policy: vpn/vpn-polkit.policy
+if POLKIT
+	$(AM_V_GEN)cp $< $@
+endif
+endif
diff --git a/configure.ac b/configure.ac
index 286b71c..51df9d6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -426,5 +426,12 @@ if (test "${enable_client}" != "no"); then
 		AC_MSG_ERROR(readline header files are required))
 fi
 
+AM_CONDITIONAL(VPN, test "${enable_openconnect}" != "no" -o \
+			"${enable_openvpn}" != "no" -o \
+			"${enable_vpnc}" != "no" -o \
+			"${enable_l2tp}" != "no" -o \
+			"${enable_pptp}" != "no")
+
 AC_OUTPUT(Makefile include/version.h src/connman.service
-				scripts/connman doc/version.xml connman.pc)
+		vpn/connman-vpn.service	vpn/net.connman.vpn.service
+		scripts/connman	doc/version.xml connman.pc)
diff --git a/include/vpn-dbus.h b/include/vpn-dbus.h
new file mode 100644
index 0000000..fec925b
--- /dev/null
+++ b/include/vpn-dbus.h
@@ -0,0 +1,57 @@
+/*
+ *
+ *  ConnMan VPN daemon
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __VPN_DBUS_H
+#define __VPN_DBUS_H
+
+#include <dbus/dbus.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define VPN_SERVICE			"net.connman.vpn"
+#define VPN_PATH			"/net/connman/vpn"
+
+#define VPN_ERROR_INTERFACE		VPN_SERVICE ".Error"
+
+#define VPN_MANAGER_INTERFACE		VPN_SERVICE ".Manager"
+#define VPN_MANAGER_PATH		"/"
+
+#define VPN_CONNECTION_INTERFACE	VPN_SERVICE ".Connection"
+#define VPN_TASK_INTERFACE		VPN_SERVICE ".Task"
+
+#define VPN_PRIVILEGE_MODIFY		1
+#define VPN_PRIVILEGE_SECRET		2
+
+#define CONNECTION_ADDED		"ConnectionAdded"
+#define CONNECTION_REMOVED		"ConnectionRemoved"
+#define PROPERTY_CHANGED		"PropertyChanged"
+#define GET_CONNECTIONS			"GetConnections"
+#define VPN_CONNECT			"Connect"
+#define VPN_DISCONNECT			"Disconnect"
+#define VPN_REMOVE			"Remove"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VPN_DBUS_H */
diff --git a/plugins/l2tp.c b/plugins/l2tp.c
deleted file mode 100644
index dfb56d9..0000000
--- a/plugins/l2tp.c
+++ /dev/null
@@ -1,532 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
- *  Copyright (C) 2012  Intel Corporation. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include <stdio.h>
-#include <net/if.h>
-
-#include <glib.h>
-
-#define CONNMAN_API_SUBJECT_TO_CHANGE
-#include <connman/plugin.h>
-#include <connman/provider.h>
-#include <connman/log.h>
-#include <connman/task.h>
-#include <connman/dbus.h>
-#include <connman/inet.h>
-
-#include "vpn.h"
-
-#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
-
-enum {
-	OPT_STRING = 1,
-	OPT_BOOL = 2,
-};
-
-enum {
-	OPT_ALL = 1,
-	OPT_L2G = 2,
-	OPT_L2	= 3,
-	OPT_PPPD = 4,
-};
-
-struct {
-	const char *cm_opt;
-	const char *pppd_opt;
-	int sub;
-	const char *vpn_default;
-	int type;
-} pppd_options[] = {
-	{ "L2TP.User", "name", OPT_ALL, NULL, OPT_STRING },
-	{ "L2TP.BPS", "bps", OPT_L2, NULL, OPT_STRING },
-	{ "L2TP.LengthBit", "length bit", OPT_L2, NULL, OPT_STRING },
-	{ "L2TP.Challenge", "challenge", OPT_L2, NULL, OPT_STRING },
-	{ "L2TP.DefaultRoute", "defaultroute", OPT_L2, NULL, OPT_STRING },
-	{ "L2TP.FlowBit", "flow bit", OPT_L2, NULL, OPT_STRING },
-	{ "L2TP.TunnelRWS", "tunnel rws", OPT_L2, NULL, OPT_STRING },
-	{ "L2TP.Exclusive", "exclusive", OPT_L2, NULL, OPT_STRING },
-	{ "L2TP.Autodial", "autodial", OPT_L2, "yes", OPT_STRING },
-	{ "L2TP.Redial", "redial", OPT_L2, "yes", OPT_STRING },
-	{ "L2TP.RedialTimeout", "redial timeout", OPT_L2, "10", OPT_STRING },
-	{ "L2TP.MaxRedials", "max redials", OPT_L2, NULL, OPT_STRING },
-	{ "L2TP.RequirePAP", "require pap", OPT_L2, "no", OPT_STRING },
-	{ "L2TP.RequireCHAP", "require chap", OPT_L2, "yes", OPT_STRING },
-	{ "L2TP.ReqAuth", "require authentication", OPT_L2, "no", OPT_STRING },
-	{ "L2TP.AccessControl", "access control", OPT_L2G, "yes", OPT_STRING },
-	{ "L2TP.AuthFile", "auth file", OPT_L2G, NULL, OPT_STRING },
-	{ "L2TP.ForceUserSpace", "force userspace", OPT_L2G, NULL, OPT_STRING },
-	{ "L2TP.ListenAddr", "listen-addr", OPT_L2G, NULL, OPT_STRING },
-	{ "L2TP.Rand Source", "rand source", OPT_L2G, NULL, OPT_STRING },
-	{ "L2TP.IPsecSaref", "ipsec saref", OPT_L2G, NULL, OPT_STRING },
-	{ "L2TP.Port", "port", OPT_L2G, NULL, OPT_STRING },
-	{ "L2TP.EchoFailure", "lcp-echo-failure", OPT_PPPD, "0", OPT_STRING },
-	{ "L2TP.EchoInterval", "lcp-echo-interval", OPT_PPPD, "0", OPT_STRING },
-	{ "L2TP.Debug", "debug", OPT_PPPD, NULL, OPT_STRING },
-	{ "L2TP.RefuseEAP", "refuse-eap", OPT_PPPD, NULL, OPT_BOOL },
-	{ "L2TP.RefusePAP", "refuse-pap", OPT_PPPD, NULL, OPT_BOOL },
-	{ "L2TP.RefuseCHAP", "refuse-chap", OPT_PPPD, NULL, OPT_BOOL },
-	{ "L2TP.RefuseMSCHAP", "refuse-mschap", OPT_PPPD, NULL, OPT_BOOL },
-	{ "L2TP.RefuseMSCHAP2", "refuse-mschapv2", OPT_PPPD, NULL, OPT_BOOL },
-	{ "L2TP.NoBSDComp", "nobsdcomp", OPT_PPPD, NULL, OPT_BOOL },
-	{ "L2TP.NoPcomp", "nopcomp", OPT_PPPD, NULL, OPT_BOOL },
-	{ "L2TP.UseAccomp", "accomp", OPT_PPPD, NULL, OPT_BOOL },
-	{ "L2TP.NoDeflate", "nodeflatey", OPT_PPPD, NULL, OPT_BOOL },
-	{ "L2TP.ReqMPPE", "require-mppe", OPT_PPPD, NULL, OPT_BOOL },
-	{ "L2TP.ReqMPPE40", "require-mppe-40", OPT_PPPD, NULL, OPT_BOOL },
-	{ "L2TP.ReqMPPE128", "require-mppe-128", OPT_PPPD, NULL, OPT_BOOL },
-	{ "L2TP.ReqMPPEStateful", "mppe-stateful", OPT_PPPD, NULL, OPT_BOOL },
-	{ "L2TP.NoVJ", "no-vj-comp", OPT_PPPD, NULL, OPT_BOOL },
-};
-
-static DBusConnection *connection;
-
-static DBusMessage *l2tp_get_sec(struct connman_task *task,
-			DBusMessage *msg, void *user_data)
-{
-	const char *user, *passwd;
-	struct connman_provider *provider = user_data;
-
-	if (dbus_message_get_no_reply(msg) == FALSE) {
-		DBusMessage *reply;
-
-		user = connman_provider_get_string(provider, "L2TP.User");
-		passwd = connman_provider_get_string(provider, "L2TP.Password");
-
-		if (user == NULL || strlen(user) == 0 ||
-				passwd == NULL || strlen(passwd) == 0)
-			return NULL;
-
-		reply = dbus_message_new_method_return(msg);
-		if (reply == NULL)
-			return NULL;
-
-		dbus_message_append_args(reply, DBUS_TYPE_STRING, &user,
-						DBUS_TYPE_STRING, &passwd,
-						DBUS_TYPE_INVALID);
-
-		return reply;
-	}
-
-	return NULL;
-}
-
-static int l2tp_notify(DBusMessage *msg, struct connman_provider *provider)
-{
-	DBusMessageIter iter, dict;
-	const char *reason, *key, *value;
-	char *addressv4 = NULL, *netmask = NULL, *gateway = NULL;
-	char *ifname = NULL, *nameservers = NULL;
-	struct connman_ipaddress *ipaddress = NULL;
-
-	dbus_message_iter_init(msg, &iter);
-
-	dbus_message_iter_get_basic(&iter, &reason);
-	dbus_message_iter_next(&iter);
-
-	if (!provider) {
-		connman_error("No provider found");
-		return VPN_STATE_FAILURE;
-	}
-
-	if (strcmp(reason, "auth failed") == 0)
-		return VPN_STATE_AUTH_FAILURE;
-
-	if (strcmp(reason, "connect"))
-		return VPN_STATE_DISCONNECT;
-
-	dbus_message_iter_recurse(&iter, &dict);
-
-	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
-		DBusMessageIter entry;
-
-		dbus_message_iter_recurse(&dict, &entry);
-		dbus_message_iter_get_basic(&entry, &key);
-		dbus_message_iter_next(&entry);
-		dbus_message_iter_get_basic(&entry, &value);
-
-		DBG("%s = %s", key, value);
-
-		if (!strcmp(key, "INTERNAL_IP4_ADDRESS")) {
-			connman_provider_set_string(provider, "Address", value);
-			addressv4 = g_strdup(value);
-		}
-
-		if (!strcmp(key, "INTERNAL_IP4_NETMASK")) {
-			connman_provider_set_string(provider, "Netmask", value);
-			netmask = g_strdup(value);
-		}
-
-		if (!strcmp(key, "INTERNAL_IP4_DNS")) {
-			connman_provider_set_string(provider, "DNS", value);
-			nameservers = g_strdup(value);
-		}
-
-		if (!strcmp(key, "INTERNAL_IFNAME"))
-			ifname = g_strdup(value);
-
-		dbus_message_iter_next(&dict);
-	}
-
-	if (vpn_set_ifname(provider, ifname) < 0) {
-		g_free(ifname);
-		g_free(addressv4);
-		g_free(netmask);
-		g_free(nameservers);
-		return VPN_STATE_FAILURE;
-	}
-
-	if (addressv4 != NULL)
-		ipaddress = connman_ipaddress_alloc(AF_INET);
-
-	g_free(ifname);
-
-	if (ipaddress == NULL) {
-		connman_error("No IP address for provider");
-		g_free(addressv4);
-		g_free(netmask);
-		g_free(nameservers);
-		return VPN_STATE_FAILURE;
-	}
-
-	value = connman_provider_get_string(provider, "HostIP");
-	if (value != NULL) {
-		connman_provider_set_string(provider, "Gateway", value);
-		gateway = g_strdup(value);
-	}
-
-	if (addressv4 != NULL)
-		connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask,
-					gateway);
-
-	connman_provider_set_ipaddress(provider, ipaddress);
-	connman_provider_set_nameservers(provider, nameservers);
-
-	g_free(addressv4);
-	g_free(netmask);
-	g_free(gateway);
-	g_free(nameservers);
-	connman_ipaddress_free(ipaddress);
-
-	return VPN_STATE_CONNECT;
-}
-
-static int l2tp_save(struct connman_provider *provider, GKeyFile *keyfile)
-{
-	const char *option;
-	int i;
-
-	for (i = 0; i < (int)ARRAY_SIZE(pppd_options); i++) {
-		if (strncmp(pppd_options[i].cm_opt, "L2TP.", 5) == 0) {
-			option = connman_provider_get_string(provider,
-							pppd_options[i].cm_opt);
-			if (option == NULL)
-				continue;
-
-			g_key_file_set_string(keyfile,
-					connman_provider_get_save_group(provider),
-					pppd_options[i].cm_opt, option);
-		}
-	}
-	return 0;
-}
-
-static ssize_t full_write(int fd, const void *buf, size_t len)
-{
-	ssize_t byte_write;
-
-	while (len) {
-		byte_write = write(fd, buf, len);
-		if (byte_write < 0) {
-			connman_error("failed to write config to l2tp: %s\n",
-					strerror(errno));
-			return byte_write;
-		}
-		len -= byte_write;
-		buf += byte_write;
-	}
-
-	return 0;
-}
-
-static ssize_t l2tp_write_bool_option(int fd,
-					const char *key, const char *value)
-{
-	gchar *buf;
-	ssize_t ret = 0;
-
-	if (key != NULL && value != NULL) {
-		if (strcasecmp(value, "yes") == 0 ||
-				strcasecmp(value, "true") == 0 ||
-				strcmp(value, "1") == 0) {
-			buf = g_strdup_printf("%s\n", key);
-			ret = full_write(fd, buf, strlen(buf));
-
-			g_free(buf);
-		}
-	}
-
-	return ret;
-}
-
-static int l2tp_write_option(int fd, const char *key, const char *value)
-{
-	gchar *buf;
-	ssize_t ret = 0;
-
-	if (key != NULL) {
-		if (value != NULL)
-			buf = g_strdup_printf("%s %s\n", key, value);
-		else
-			buf = g_strdup_printf("%s\n", key);
-
-		ret = full_write(fd, buf, strlen(buf));
-
-		g_free(buf);
-	}
-
-	return ret;
-}
-
-static int l2tp_write_section(int fd, const char *key, const char *value)
-{
-	gchar *buf;
-	ssize_t ret = 0;
-
-	if (key != NULL && value != NULL) {
-		buf = g_strdup_printf("%s = %s\n", key, value);
-		ret = full_write(fd, buf, strlen(buf));
-
-		g_free(buf);
-	}
-
-	return ret;
-}
-
-static int write_pppd_option(struct connman_provider *provider, int fd)
-{
-	int i;
-	const char *opt_s;
-
-	l2tp_write_option(fd, "nodetach", NULL);
-	l2tp_write_option(fd, "lock", NULL);
-	l2tp_write_option(fd, "usepeerdns", NULL);
-	l2tp_write_option(fd, "noipdefault", NULL);
-	l2tp_write_option(fd, "noauth", NULL);
-	l2tp_write_option(fd, "nodefaultroute", NULL);
-	l2tp_write_option(fd, "ipparam", "l2tp_plugin");
-
-	for (i = 0; i < (int)ARRAY_SIZE(pppd_options); i++) {
-		if (pppd_options[i].sub != OPT_ALL &&
-			pppd_options[i].sub != OPT_PPPD)
-			continue;
-
-		opt_s = connman_provider_get_string(provider,
-					pppd_options[i].cm_opt);
-		if (!opt_s)
-			opt_s = pppd_options[i].vpn_default;
-
-		if (!opt_s)
-			continue;
-
-		if (pppd_options[i].type == OPT_STRING)
-			l2tp_write_option(fd,
-				pppd_options[i].pppd_opt, opt_s);
-		else if (pppd_options[i].type == OPT_BOOL)
-			l2tp_write_bool_option(fd,
-				pppd_options[i].pppd_opt, opt_s);
-	}
-
-	l2tp_write_option(fd, "plugin",
-				SCRIPTDIR "/libppp-plugin.so");
-
-	return 0;
-}
-
-
-static int l2tp_write_fields(struct connman_provider *provider,
-						int fd, int sub)
-{
-	int i;
-	const char *opt_s;
-
-	for (i = 0; i < (int)ARRAY_SIZE(pppd_options); i++) {
-		if (pppd_options[i].sub != sub)
-			continue;
-
-		opt_s = connman_provider_get_string(provider,
-					pppd_options[i].cm_opt);
-		if (!opt_s)
-			opt_s = pppd_options[i].vpn_default;
-
-		if (!opt_s)
-			continue;
-
-		if (pppd_options[i].type == OPT_STRING)
-			l2tp_write_section(fd,
-				pppd_options[i].pppd_opt, opt_s);
-		else if (pppd_options[i].type == OPT_BOOL)
-			l2tp_write_bool_option(fd,
-				pppd_options[i].pppd_opt, opt_s);
-	}
-
-	return 0;
-}
-
-static int l2tp_write_config(struct connman_provider *provider,
-					const char *pppd_name, int fd)
-{
-	const char *option;
-
-	l2tp_write_option(fd, "[global]", NULL);
-	l2tp_write_fields(provider, fd, OPT_L2G);
-
-	l2tp_write_option(fd, "[lac l2tp]", NULL);
-
-	option = connman_provider_get_string(provider, "Host");
-	l2tp_write_option(fd, "lns =", option);
-
-	l2tp_write_fields(provider, fd, OPT_ALL);
-	l2tp_write_fields(provider, fd, OPT_L2);
-
-	l2tp_write_option(fd, "pppoptfile =", pppd_name);
-
-	return 0;
-}
-
-static void l2tp_died(struct connman_task *task, int exit_code, void *user_data)
-{
-	char *conf_file;
-
-	vpn_died(task, exit_code, user_data);
-
-	conf_file = g_strdup_printf("/var/run/connman/connman-xl2tpd.conf");
-	unlink(conf_file);
-	g_free(conf_file);
-
-	conf_file = g_strdup_printf("/var/run/connman/connman-ppp-option.conf");
-	unlink(conf_file);
-	g_free(conf_file);
-}
-
-static int l2tp_connect(struct connman_provider *provider,
-		struct connman_task *task, const char *if_name)
-{
-	const char *host;
-	char *l2tp_name, *pppd_name;
-	int l2tp_fd, pppd_fd;
-	int err;
-
-	if (connman_task_set_notify(task, "getsec",
-					l2tp_get_sec, provider))
-		return -ENOMEM;
-
-	host = connman_provider_get_string(provider, "Host");
-	if (host == NULL) {
-		connman_error("Host not set; cannot enable VPN");
-		return -EINVAL;
-	}
-
-	l2tp_name = g_strdup_printf("/var/run/connman/connman-xl2tpd.conf");
-
-	l2tp_fd = open(l2tp_name, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
-	if (l2tp_fd < 0) {
-		g_free(l2tp_name);
-		connman_error("Error writing l2tp config");
-		return -EIO;
-	}
-
-	pppd_name = g_strdup_printf("/var/run/connman/connman-ppp-option.conf");
-
-	pppd_fd = open(pppd_name, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
-	if (pppd_fd < 0) {
-		connman_error("Error writing pppd config");
-		g_free(l2tp_name);
-		g_free(pppd_name);
-		close(l2tp_fd);
-		return -EIO;
-	}
-
-	l2tp_write_config(provider, pppd_name, l2tp_fd);
-
-	write_pppd_option(provider, pppd_fd);
-
-	connman_task_add_argument(task, "-D", NULL);
-	connman_task_add_argument(task, "-c", l2tp_name);
-
-	g_free(l2tp_name);
-	g_free(pppd_name);
-
-	err = connman_task_run(task, l2tp_died, provider,
-				NULL, NULL, NULL);
-	if (err < 0) {
-		connman_error("l2tp failed to start");
-		return -EIO;
-	}
-
-	return 0;
-}
-
-static int l2tp_error_code(int exit_code)
-{
-	switch (exit_code) {
-	case 1:
-		return CONNMAN_PROVIDER_ERROR_CONNECT_FAILED;
-	default:
-		return CONNMAN_PROVIDER_ERROR_UNKNOWN;
-	}
-}
-
-static struct vpn_driver vpn_driver = {
-	.flags		= VPN_FLAG_NO_TUN,
-	.notify		= l2tp_notify,
-	.connect	= l2tp_connect,
-	.error_code	= l2tp_error_code,
-	.save		= l2tp_save,
-};
-
-static int l2tp_init(void)
-{
-	connection = connman_dbus_get_connection();
-
-	return vpn_register("l2tp", &vpn_driver, L2TP);
-}
-
-static void l2tp_exit(void)
-{
-	vpn_unregister("l2tp");
-
-	dbus_connection_unref(connection);
-}
-
-CONNMAN_PLUGIN_DEFINE(l2tp, "l2tp plugin", VERSION,
-	CONNMAN_PLUGIN_PRIORITY_DEFAULT, l2tp_init, l2tp_exit)
diff --git a/plugins/openconnect.c b/plugins/openconnect.c
deleted file mode 100644
index 70be7ae..0000000
--- a/plugins/openconnect.c
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <net/if.h>
-
-#include <glib.h>
-
-#define CONNMAN_API_SUBJECT_TO_CHANGE
-#include <connman/plugin.h>
-#include <connman/provider.h>
-#include <connman/log.h>
-#include <connman/task.h>
-#include <connman/ipconfig.h>
-
-#include "vpn.h"
-
-static int oc_notify(DBusMessage *msg, struct connman_provider *provider)
-{
-	DBusMessageIter iter, dict;
-	const char *reason, *key, *value;
-	const char *domain = NULL;
-	char *addressv4 = NULL, *addressv6 = NULL;
-	char *netmask = NULL, *gateway = NULL;
-	unsigned char prefix_len = 0;
-	struct connman_ipaddress *ipaddress;
-
-	dbus_message_iter_init(msg, &iter);
-
-	dbus_message_iter_get_basic(&iter, &reason);
-	dbus_message_iter_next(&iter);
-
-	if (!provider) {
-		connman_error("No provider found");
-		return VPN_STATE_FAILURE;
-	}
-
-	if (strcmp(reason, "connect"))
-		return VPN_STATE_DISCONNECT;
-
-	domain = connman_provider_get_string(provider, "VPN.Domain");
-
-	dbus_message_iter_recurse(&iter, &dict);
-
-	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
-		DBusMessageIter entry;
-
-		dbus_message_iter_recurse(&dict, &entry);
-		dbus_message_iter_get_basic(&entry, &key);
-		dbus_message_iter_next(&entry);
-		dbus_message_iter_get_basic(&entry, &value);
-
-		if (strcmp(key, "CISCO_CSTP_OPTIONS"))
-			DBG("%s = %s", key, value);
-
-		if (!strcmp(key, "VPNGATEWAY"))
-			gateway = g_strdup(value);
-
-		if (!strcmp(key, "INTERNAL_IP4_ADDRESS"))
-			addressv4 = g_strdup(value);
-
-		if (!strcmp(key, "INTERNAL_IP6_ADDRESS")) {
-			addressv6 = g_strdup(value);
-			prefix_len = 128;
-		}
-
-		if (!strcmp(key, "INTERNAL_IP4_NETMASK"))
-			netmask = g_strdup(value);
-
-		if (!strcmp(key, "INTERNAL_IP6_NETMASK")) {
-			char *sep;
-
-			/* The netmask contains the address and the prefix */
-			sep = strchr(value, '/');
-			if (sep != NULL) {
-				unsigned char ip_len = sep - value;
-
-				addressv6 = g_strndup(value, ip_len);
-				prefix_len = (unsigned char)
-						strtol(sep + 1, NULL, 10);
-			}
-		}
-
-		if (!strcmp(key, "INTERNAL_IP4_DNS") ||
-				!strcmp(key, "INTERNAL_IP6_DNS"))
-			connman_provider_set_nameservers(provider, value);
-
-		if (!strcmp(key, "CISCO_PROXY_PAC"))
-			connman_provider_set_pac(provider, value);
-
-		if (domain == NULL && !strcmp(key, "CISCO_DEF_DOMAIN"))
-			domain = value;
-
-		if (g_str_has_prefix(key, "CISCO_SPLIT_INC") == TRUE ||
-			g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC") == TRUE)
-			connman_provider_append_route(provider, key, value);
-
-		dbus_message_iter_next(&dict);
-	}
-
-	DBG("%p %p", addressv4, addressv6);
-
-	if (addressv4 != NULL)
-		ipaddress = connman_ipaddress_alloc(AF_INET);
-	else if (addressv6 != NULL)
-		ipaddress = connman_ipaddress_alloc(AF_INET6);
-	else
-		ipaddress = NULL;
-
-	if (ipaddress == NULL) {
-		g_free(addressv4);
-		g_free(addressv6);
-		g_free(netmask);
-		g_free(gateway);
-
-		return VPN_STATE_FAILURE;
-	}
-
-	if (addressv4 != NULL)
-		connman_ipaddress_set_ipv4(ipaddress, addressv4,
-						netmask, gateway);
-	else
-		connman_ipaddress_set_ipv6(ipaddress, addressv6,
-						prefix_len, gateway);
-	connman_provider_set_ipaddress(provider, ipaddress);
-	connman_provider_set_domain(provider, domain);
-
-	g_free(addressv4);
-	g_free(addressv6);
-	g_free(netmask);
-	g_free(gateway);
-	connman_ipaddress_free(ipaddress);
-
-	return VPN_STATE_CONNECT;
-}
-
-static int oc_connect(struct connman_provider *provider,
-			struct connman_task *task, const char *if_name)
-{
-	const char *vpnhost, *vpncookie, *cafile, *certsha1, *mtu;
-	int fd, err;
-
-	vpnhost = connman_provider_get_string(provider, "Host");
-	if (!vpnhost) {
-		connman_error("Host not set; cannot enable VPN");
-		return -EINVAL;
-	}
-
-	vpncookie = connman_provider_get_string(provider, "OpenConnect.Cookie");
-	if (!vpncookie) {
-		connman_error("OpenConnect.Cookie not set; cannot enable VPN");
-		return -EINVAL;
-	}
-
-	certsha1 = connman_provider_get_string(provider,
-						"OpenConnect.ServerCert");
-	if (certsha1)
-		connman_task_add_argument(task, "--servercert",
-							(char *)certsha1);
-
-	cafile = connman_provider_get_string(provider, "OpenConnect.CACert");
-	mtu = connman_provider_get_string(provider, "VPN.MTU");
-
-	if (cafile)
-		connman_task_add_argument(task, "--cafile",
-							(char *)cafile);
-	if (mtu)
-		connman_task_add_argument(task, "--mtu", (char *)mtu);
-
-	connman_task_add_argument(task, "--syslog", NULL);
-	connman_task_add_argument(task, "--cookie-on-stdin", NULL);
-
-	connman_task_add_argument(task, "--script",
-				  SCRIPTDIR "/openconnect-script");
-
-	connman_task_add_argument(task, "--interface", if_name);
-
-	connman_task_add_argument(task, (char *)vpnhost, NULL);
-
-	err = connman_task_run(task, vpn_died, provider,
-			       &fd, NULL, NULL);
-	if (err < 0) {
-		connman_error("openconnect failed to start");
-		return -EIO;
-	}
-
-	if (write(fd, vpncookie, strlen(vpncookie)) !=
-			(ssize_t)strlen(vpncookie) ||
-			write(fd, "\n", 1) != 1) {
-		connman_error("openconnect failed to take cookie on stdin");
-		return -EIO;
-	}
-
-	return 0;
-}
-
-static int oc_save (struct connman_provider *provider, GKeyFile *keyfile)
-{
-	const char *setting;
-
-	setting = connman_provider_get_string(provider,
-					"OpenConnect.ServerCert");
-	if (setting != NULL)
-		g_key_file_set_string(keyfile,
-				connman_provider_get_save_group(provider),
-				"OpenConnect.ServerCert", setting);
-
-	setting = connman_provider_get_string(provider,
-					"OpenConnect.CACert");
-	if (setting != NULL)
-		g_key_file_set_string(keyfile,
-				connman_provider_get_save_group(provider),
-				"OpenConnect.CACert", setting);
-
-	setting = connman_provider_get_string(provider,
-					"VPN.MTU");
-	if (setting != NULL)
-		g_key_file_set_string(keyfile,
-				connman_provider_get_save_group(provider),
-				"VPN.MTU", setting);
-
-	return 0;
-}
-
-static int oc_error_code(int exit_code)
-{
-
-	switch (exit_code) {
-	case 1:
-		return CONNMAN_PROVIDER_ERROR_CONNECT_FAILED;
-	case 2:
-		return CONNMAN_PROVIDER_ERROR_LOGIN_FAILED;
-	default:
-		return CONNMAN_PROVIDER_ERROR_UNKNOWN;
-	}
-}
-
-static struct vpn_driver vpn_driver = {
-	.notify         = oc_notify,
-	.connect	= oc_connect,
-	.error_code	= oc_error_code,
-	.save		= oc_save,
-};
-
-static int openconnect_init(void)
-{
-	return vpn_register("openconnect", &vpn_driver, OPENCONNECT);
-}
-
-static void openconnect_exit(void)
-{
-	vpn_unregister("openconnect");
-}
-
-CONNMAN_PLUGIN_DEFINE(openconnect, "OpenConnect VPN plugin", VERSION,
-	CONNMAN_PLUGIN_PRIORITY_DEFAULT, openconnect_init, openconnect_exit)
diff --git a/plugins/openvpn.c b/plugins/openvpn.c
deleted file mode 100644
index bac1671..0000000
--- a/plugins/openvpn.c
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <net/if.h>
-
-#include <glib.h>
-
-#define CONNMAN_API_SUBJECT_TO_CHANGE
-#include <connman/plugin.h>
-#include <connman/provider.h>
-#include <connman/log.h>
-#include <connman/task.h>
-#include <connman/dbus.h>
-#include <connman/ipconfig.h>
-
-#include "vpn.h"
-
-#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
-
-static DBusConnection *connection;
-
-struct {
-	const char *cm_opt;
-	const char *ov_opt;
-	char       has_value;
-} ov_options[] = {
-	{ "Host", "--remote", 1 },
-	{ "OpenVPN.CACert", "--ca", 1 },
-	{ "OpenVPN.Cert", "--cert", 1 },
-	{ "OpenVPN.Key", "--key", 1 },
-	{ "OpenVPN.MTU", "--mtu", 1 },
-	{ "OpenVPN.NSCertType", "--ns-cert-type", 1 },
-	{ "OpenVPN.Proto", "--proto", 1 },
-	{ "OpenVPN.Port", "--port", 1 },
-	{ "OpenVPN.AuthUserPass", "--auth-user-pass", 1 },
-	{ "OpenVPN.AskPass", "--askpass", 1 },
-	{ "OpenVPN.AuthNoCache", "--auth-nocache", 0 },
-	{ "OpenVPN.TLSRemote", "--tls-remote", 1 },
-	{ "OpenVPN.TLSAuth", NULL, 1 },
-	{ "OpenVPN.TLSAuthDir", NULL, 1 },
-	{ "OpenVPN.Cipher", "--cipher", 1 },
-	{ "OpenVPN.Auth", "--auth", 1 },
-	{ "OpenVPN.CompLZO", "--comp-lzo", 0 },
-	{ "OpenVPN.RemoteCertTls", "--remote-cert-tls", 1 },
-};
-
-static void ov_append_dns_entries(const char *key, const char *value,
-					char **dns_entries)
-{
-	gchar **options;
-
-	if (g_str_has_prefix(key, "foreign_option_") == FALSE)
-		return;
-
-	options = g_strsplit(value, " ", 3);
-	if (options[0] != NULL &&
-		!strcmp(options[0], "dhcp-option") &&
-			options[1] != NULL &&
-			!strcmp(options[1], "DNS") &&
-				options[2] != NULL) {
-
-		if (*dns_entries != NULL) {
-			char *tmp;
-
-			tmp = g_strjoin(" ", *dns_entries,
-						options[2], NULL);
-			g_free(*dns_entries);
-			*dns_entries = tmp;
-		} else {
-			*dns_entries = g_strdup(options[2]);
-		}
-	}
-
-	g_strfreev(options);
-}
-
-static int ov_notify(DBusMessage *msg, struct connman_provider *provider)
-{
-	DBusMessageIter iter, dict;
-	const char *reason, *key, *value;
-	char *nameservers = NULL;
-	char *address = NULL, *gateway = NULL, *peer = NULL;
-	struct connman_ipaddress *ipaddress;
-
-	dbus_message_iter_init(msg, &iter);
-
-	dbus_message_iter_get_basic(&iter, &reason);
-	dbus_message_iter_next(&iter);
-
-	if (!provider) {
-		connman_error("No provider found");
-		return VPN_STATE_FAILURE;
-	}
-
-	if (strcmp(reason, "up"))
-		return VPN_STATE_DISCONNECT;
-
-	dbus_message_iter_recurse(&iter, &dict);
-
-	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
-		DBusMessageIter entry;
-
-		dbus_message_iter_recurse(&dict, &entry);
-		dbus_message_iter_get_basic(&entry, &key);
-		dbus_message_iter_next(&entry);
-		dbus_message_iter_get_basic(&entry, &value);
-
-		DBG("%s = %s", key, value);
-
-		if (!strcmp(key, "trusted_ip")) {
-			connman_provider_set_string(provider, "Gateway", value);
-			gateway = g_strdup(value);
-		}
-
-		if (!strcmp(key, "ifconfig_local")) {
-			connman_provider_set_string(provider, "Address", value);
-			address = g_strdup(value);
-		}
-
-		if (!strcmp(key, "ifconfig_remote")) {
-			connman_provider_set_string(provider, "Peer", value);
-			peer = g_strdup(value);
-		}
-
-		if (g_str_has_prefix(key, "route_") == TRUE)
-			connman_provider_append_route(provider, key, value);
-
-		ov_append_dns_entries(key, value, &nameservers);
-
-		dbus_message_iter_next(&dict);
-	}
-
-	ipaddress = connman_ipaddress_alloc(AF_INET);
-	if (ipaddress == NULL) {
-		g_free(nameservers);
-		g_free(address);
-		g_free(gateway);
-		g_free(peer);
-
-		return VPN_STATE_FAILURE;
-	}
-
-	connman_ipaddress_set_ipv4(ipaddress, address, NULL, gateway);
-	connman_ipaddress_set_peer(ipaddress, peer);
-	connman_provider_set_ipaddress(provider, ipaddress);
-
-	connman_provider_set_nameservers(provider, nameservers);
-
-	g_free(nameservers);
-	g_free(address);
-	g_free(gateway);
-	g_free(peer);
-	connman_ipaddress_free(ipaddress);
-
-	return VPN_STATE_CONNECT;
-}
-
-static int ov_save(struct connman_provider *provider, GKeyFile *keyfile)
-{
-	const char *option;
-	int i;
-
-	for (i = 0; i < (int)ARRAY_SIZE(ov_options); i++) {
-		if (strncmp(ov_options[i].cm_opt, "OpenVPN.", 8) == 0) {
-			option = connman_provider_get_string(provider,
-							ov_options[i].cm_opt);
-			if (option == NULL)
-				continue;
-
-			g_key_file_set_string(keyfile,
-					connman_provider_get_save_group(provider),
-					ov_options[i].cm_opt, option);
-		}
-	}
-	return 0;
-}
-
-static int task_append_config_data(struct connman_provider *provider,
-					struct connman_task *task)
-{
-	const char *option;
-	int i;
-
-	for (i = 0; i < (int)ARRAY_SIZE(ov_options); i++) {
-		if (ov_options[i].ov_opt == NULL)
-			continue;
-
-		option = connman_provider_get_string(provider,
-					ov_options[i].cm_opt);
-		if (option == NULL)
-			continue;
-
-		if (connman_task_add_argument(task,
-					ov_options[i].ov_opt,
-					ov_options[i].has_value ? option : NULL) < 0) {
-			return -EIO;
-		}
-	}
-
-	return 0;
-}
-
-static int ov_connect(struct connman_provider *provider,
-		struct connman_task *task, const char *if_name)
-{
-	const char *option;
-	int err, fd;
-
-	option = connman_provider_get_string(provider, "Host");
-	if (option == NULL) {
-		connman_error("Host not set; cannot enable VPN");
-		return -EINVAL;
-	}
-
-	task_append_config_data(provider, task);
-
-	option = connman_provider_get_string(provider, "OpenVPN.TLSAuth");
-	if (option != NULL) {
-		connman_task_add_argument(task, "--tls-auth", option);
-		option = connman_provider_get_string(provider,
-				"OpenVPN.TLSAuthDir");
-		if (option != NULL)
-			connman_task_add_argument(task, option, NULL);
-	}
-
-	connman_task_add_argument(task, "--syslog", NULL);
-
-	connman_task_add_argument(task, "--script-security", "2");
-
-	connman_task_add_argument(task, "--up",
-					SCRIPTDIR "/openvpn-script");
-	connman_task_add_argument(task, "--up-restart", NULL);
-
-	connman_task_add_argument(task, "--setenv", NULL);
-	connman_task_add_argument(task, "CONNMAN_BUSNAME",
-					dbus_bus_get_unique_name(connection));
-
-	connman_task_add_argument(task, "--setenv", NULL);
-	connman_task_add_argument(task, "CONNMAN_INTERFACE",
-					CONNMAN_TASK_INTERFACE);
-
-	connman_task_add_argument(task, "--setenv", NULL);
-	connman_task_add_argument(task, "CONNMAN_PATH",
-					connman_task_get_path(task));
-
-	connman_task_add_argument(task, "--dev", if_name);
-	connman_task_add_argument(task, "--dev-type", "tun");
-
-	connman_task_add_argument(task, "--tls-client", NULL);
-	connman_task_add_argument(task, "--nobind", NULL);
-	connman_task_add_argument(task, "--persist-key", NULL);
-	connman_task_add_argument(task, "--persist-tun", NULL);
-
-	connman_task_add_argument(task, "--route-noexec", NULL);
-	connman_task_add_argument(task, "--ifconfig-noexec", NULL);
-
-	/*
-	 * Disable client restarts because we can't handle this at the
-	 * moment. The problem is that when OpenVPN decides to switch
-	 * from CONNECTED state to RECONNECTING and then to RESOLVE,
-	 * it is not possible to do a DNS lookup. The DNS server is
-	 * not accessable through the tunnel anymore and so we end up
-	 * trying to resolve the OpenVPN servers address.
-	 */
-	connman_task_add_argument(task, "--ping-restart", "0");
-
-	connman_task_add_argument(task, "--client", NULL);
-
-	fd = fileno(stderr);
-	err = connman_task_run(task, vpn_died, provider,
-			NULL, &fd, &fd);
-	if (err < 0) {
-		connman_error("openvpn failed to start");
-		return -EIO;
-	}
-
-	return 0;
-}
-
-static struct vpn_driver vpn_driver = {
-	.notify	= ov_notify,
-	.connect	= ov_connect,
-	.save		= ov_save,
-};
-
-static int openvpn_init(void)
-{
-	connection = connman_dbus_get_connection();
-
-	return vpn_register("openvpn", &vpn_driver, OPENVPN);
-}
-
-static void openvpn_exit(void)
-{
-	vpn_unregister("openvpn");
-
-	dbus_connection_unref(connection);
-}
-
-CONNMAN_PLUGIN_DEFINE(openvpn, "OpenVPN plugin", VERSION,
-	CONNMAN_PLUGIN_PRIORITY_DEFAULT, openvpn_init, openvpn_exit)
diff --git a/plugins/pptp.c b/plugins/pptp.c
deleted file mode 100644
index 7fafc05..0000000
--- a/plugins/pptp.c
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
- *  Copyright (C) 2012  Intel Corporation. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <net/if.h>
-
-#include <glib.h>
-
-#define CONNMAN_API_SUBJECT_TO_CHANGE
-#include <connman/plugin.h>
-#include <connman/provider.h>
-#include <connman/log.h>
-#include <connman/task.h>
-#include <connman/dbus.h>
-#include <connman/inet.h>
-
-#include "vpn.h"
-
-#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
-
-enum {
-	OPT_STRING = 1,
-	OPT_BOOL = 2,
-};
-
-struct {
-	const char *cm_opt;
-	const char *pptp_opt;
-	const char *vpnc_default;
-	int type;
-} pptp_options[] = {
-	{ "PPTP.User", "user", NULL, OPT_STRING },
-	{ "PPTP.EchoFailure", "lcp-echo-failure", "0", OPT_STRING },
-	{ "PPTP.EchoInterval", "lcp-echo-interval", "0", OPT_STRING },
-	{ "PPTP.Debug", "debug", NULL, OPT_STRING },
-	{ "PPTP.RefuseEAP", "refuse-eap", NULL, OPT_BOOL },
-	{ "PPTP.RefusePAP", "refuse-pap", NULL, OPT_BOOL },
-	{ "PPTP.RefuseCHAP", "refuse-chap", NULL, OPT_BOOL },
-	{ "PPTP.RefuseMSCHAP", "refuse-mschap", NULL, OPT_BOOL },
-	{ "PPTP.RefuseMSCHAP2", "refuse-mschapv2", NULL, OPT_BOOL },
-	{ "PPTP.NoBSDComp", "nobsdcomp", NULL, OPT_BOOL },
-	{ "PPTP.NoDeflate", "nodeflate", NULL, OPT_BOOL },
-	{ "PPTP.RequirMPPE", "require-mppe", NULL, OPT_BOOL },
-	{ "PPTP.RequirMPPE40", "require-mppe-40", NULL, OPT_BOOL },
-	{ "PPTP.RequirMPPE128", "require-mppe-128", NULL, OPT_BOOL },
-	{ "PPTP.RequirMPPEStateful", "mppe-stateful", NULL, OPT_BOOL },
-	{ "PPTP.NoVJ", "no-vj-comp", NULL, OPT_BOOL },
-};
-
-static DBusConnection *connection;
-
-static DBusMessage *pptp_get_sec(struct connman_task *task,
-				DBusMessage *msg, void *user_data)
-{
-	const char *user, *passwd;
-	struct connman_provider *provider = user_data;
-	DBusMessage *reply;
-
-	if (dbus_message_get_no_reply(msg) == TRUE)
-		return NULL;
-
-	user = connman_provider_get_string(provider, "PPTP.User");
-	passwd = connman_provider_get_string(provider, "PPTP.Password");
-	if (user == NULL || strlen(user) == 0 ||
-				passwd == NULL || strlen(passwd) == 0)
-		return NULL;
-
-	reply = dbus_message_new_method_return(msg);
-	if (reply == NULL)
-		return NULL;
-
-	dbus_message_append_args(reply, DBUS_TYPE_STRING, &user,
-				DBUS_TYPE_STRING, &passwd,
-				DBUS_TYPE_INVALID);
-	return reply;
-}
-
-static int pptp_notify(DBusMessage *msg, struct connman_provider *provider)
-{
-	DBusMessageIter iter, dict;
-	const char *reason, *key, *value;
-	char *addressv4 = NULL, *netmask = NULL, *gateway = NULL;
-	char *ifname = NULL, *nameservers = NULL;
-	struct connman_ipaddress *ipaddress = NULL;
-
-	dbus_message_iter_init(msg, &iter);
-
-	dbus_message_iter_get_basic(&iter, &reason);
-	dbus_message_iter_next(&iter);
-
-	if (provider == NULL) {
-		connman_error("No provider found");
-		return VPN_STATE_FAILURE;
-	}
-
-	if (strcmp(reason, "auth failed") == 0)
-		return VPN_STATE_AUTH_FAILURE;
-
-	if (strcmp(reason, "connect"))
-		return VPN_STATE_DISCONNECT;
-
-	dbus_message_iter_recurse(&iter, &dict);
-
-	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
-		DBusMessageIter entry;
-
-		dbus_message_iter_recurse(&dict, &entry);
-		dbus_message_iter_get_basic(&entry, &key);
-		dbus_message_iter_next(&entry);
-		dbus_message_iter_get_basic(&entry, &value);
-
-		DBG("%s = %s", key, value);
-
-		if (!strcmp(key, "INTERNAL_IP4_ADDRESS")) {
-			connman_provider_set_string(provider, "Address", value);
-			addressv4 = g_strdup(value);
-		}
-
-		if (!strcmp(key, "INTERNAL_IP4_NETMASK")) {
-			connman_provider_set_string(provider, "Netmask", value);
-			netmask = g_strdup(value);
-		}
-
-		if (!strcmp(key, "INTERNAL_IP4_DNS")) {
-			connman_provider_set_string(provider, "DNS", value);
-			nameservers = g_strdup(value);
-		}
-
-		if (!strcmp(key, "INTERNAL_IFNAME"))
-			ifname = g_strdup(value);
-
-		dbus_message_iter_next(&dict);
-	}
-
-	if (vpn_set_ifname(provider, ifname) < 0) {
-		g_free(ifname);
-		g_free(addressv4);
-		g_free(netmask);
-		g_free(nameservers);
-		return VPN_STATE_FAILURE;
-	}
-
-	if (addressv4 != NULL)
-		ipaddress = connman_ipaddress_alloc(AF_INET);
-
-	g_free(ifname);
-
-	if (ipaddress == NULL) {
-		connman_error("No IP address for provider");
-		g_free(addressv4);
-		g_free(netmask);
-		g_free(nameservers);
-		return VPN_STATE_FAILURE;
-	}
-
-	value = connman_provider_get_string(provider, "HostIP");
-	if (value != NULL) {
-		connman_provider_set_string(provider, "Gateway", value);
-		gateway = g_strdup(value);
-	}
-
-	if (addressv4 != NULL)
-		connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask,
-					gateway);
-
-	connman_provider_set_ipaddress(provider, ipaddress);
-	connman_provider_set_nameservers(provider, nameservers);
-
-	g_free(addressv4);
-	g_free(netmask);
-	g_free(gateway);
-	g_free(nameservers);
-	connman_ipaddress_free(ipaddress);
-
-	return VPN_STATE_CONNECT;
-}
-
-static int pptp_save(struct connman_provider *provider, GKeyFile *keyfile)
-{
-	const char *option;
-	int i;
-
-	for (i = 0; i < (int)ARRAY_SIZE(pptp_options); i++) {
-		if (strncmp(pptp_options[i].cm_opt, "PPTP.", 5) == 0) {
-			option = connman_provider_get_string(provider,
-							pptp_options[i].cm_opt);
-			if (option == NULL)
-				continue;
-
-			g_key_file_set_string(keyfile,
-					connman_provider_get_save_group(provider),
-					pptp_options[i].cm_opt, option);
-		}
-	}
-	return 0;
-}
-
-static void pptp_write_bool_option(struct connman_task *task,
-				const char *key, const char *value)
-{
-	if (key != NULL && value != NULL) {
-		if (strcasecmp(value, "yes") == 0 ||
-				strcasecmp(value, "true") == 0 ||
-				strcmp(value, "1") == 0)
-			connman_task_add_argument(task, key, NULL);
-	}
-}
-
-static int pptp_connect(struct connman_provider *provider,
-		struct connman_task *task, const char *if_name)
-{
-	const char *opt_s, *host;
-	char *str;
-	int err, i;
-
-	if (connman_task_set_notify(task, "getsec",
-					pptp_get_sec, provider))
-		return -ENOMEM;
-
-	host = connman_provider_get_string(provider, "Host");
-	if (host == NULL) {
-		connman_error("Host not set; cannot enable VPN");
-		return -EINVAL;
-	}
-
-	str = g_strdup_printf("%s %s --nolaunchpppd --loglevel 2",
-				PPTP, host);
-	if (str == NULL) {
-		connman_error("can not allocate memory");
-		return -ENOMEM;
-	}
-
-	connman_task_add_argument(task, "pty", str);
-	g_free(str);
-
-	connman_task_add_argument(task, "nodetach", NULL);
-	connman_task_add_argument(task, "lock", NULL);
-	connman_task_add_argument(task, "usepeerdns", NULL);
-	connman_task_add_argument(task, "noipdefault", NULL);
-	connman_task_add_argument(task, "noauth", NULL);
-	connman_task_add_argument(task, "nodefaultroute", NULL);
-	connman_task_add_argument(task, "ipparam", "pptp_plugin");
-
-	for (i = 0; i < (int)ARRAY_SIZE(pptp_options); i++) {
-		opt_s = connman_provider_get_string(provider,
-					pptp_options[i].cm_opt);
-		if (opt_s == NULL)
-			opt_s = pptp_options[i].vpnc_default;
-
-		if (opt_s == NULL)
-			continue;
-
-		if (pptp_options[i].type == OPT_STRING)
-			connman_task_add_argument(task,
-					pptp_options[i].pptp_opt, opt_s);
-		else if (pptp_options[i].type == OPT_BOOL)
-			pptp_write_bool_option(task,
-					pptp_options[i].pptp_opt, opt_s);
-	}
-
-	connman_task_add_argument(task, "plugin",
-				SCRIPTDIR "/libppp-plugin.so");
-
-	err = connman_task_run(task, vpn_died, provider,
-				NULL, NULL, NULL);
-	if (err < 0) {
-		connman_error("pptp failed to start");
-		return -EIO;
-	}
-
-	return 0;
-}
-
-static int pptp_error_code(int exit_code)
-{
-
-	switch (exit_code) {
-	case 1:
-		return CONNMAN_PROVIDER_ERROR_CONNECT_FAILED;
-	case 2:
-		return CONNMAN_PROVIDER_ERROR_LOGIN_FAILED;
-	case 16:
-		return CONNMAN_PROVIDER_ERROR_AUTH_FAILED;
-	default:
-		return CONNMAN_PROVIDER_ERROR_UNKNOWN;
-	}
-}
-
-static struct vpn_driver vpn_driver = {
-	.flags		= VPN_FLAG_NO_TUN,
-	.notify		= pptp_notify,
-	.connect	= pptp_connect,
-	.error_code     = pptp_error_code,
-	.save		= pptp_save,
-};
-
-static int pptp_init(void)
-{
-	connection = connman_dbus_get_connection();
-
-	return vpn_register("pptp", &vpn_driver, PPPD);
-}
-
-static void pptp_exit(void)
-{
-	vpn_unregister("pptp");
-
-	dbus_connection_unref(connection);
-}
-
-CONNMAN_PLUGIN_DEFINE(pptp, "pptp plugin", VERSION,
-	CONNMAN_PLUGIN_PRIORITY_DEFAULT, pptp_init, pptp_exit)
diff --git a/plugins/vpn.c b/plugins/vpn.c
deleted file mode 100644
index 165c325..0000000
--- a/plugins/vpn.c
+++ /dev/null
@@ -1,534 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#define _GNU_SOURCE
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <stdio.h>
-#include <errno.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <linux/if_tun.h>
-#include <net/if.h>
-
-#include <dbus/dbus.h>
-
-#include <glib/gprintf.h>
-
-#include <connman/provider.h>
-#include <connman/log.h>
-#include <connman/rtnl.h>
-#include <connman/task.h>
-#include <connman/inet.h>
-
-#include "vpn.h"
-
-struct vpn_data {
-	struct connman_provider *provider;
-	char *if_name;
-	unsigned flags;
-	unsigned int watch;
-	unsigned int state;
-	struct connman_task *task;
-};
-
-struct vpn_driver_data {
-	const char *name;
-	const char *program;
-	struct vpn_driver *vpn_driver;
-	struct connman_provider_driver provider_driver;
-};
-
-GHashTable *driver_hash = NULL;
-
-static int stop_vpn(struct connman_provider *provider)
-{
-	struct vpn_data *data = connman_provider_get_data(provider);
-	struct vpn_driver_data *vpn_driver_data;
-	const char *name;
-	struct ifreq ifr;
-	int fd, err;
-
-	if (data == NULL)
-		return -EINVAL;
-
-	name = connman_provider_get_driver_name(provider);
-	if (name == NULL)
-		return -EINVAL;
-
-	vpn_driver_data = g_hash_table_lookup(driver_hash, name);
-
-	if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL &&
-			vpn_driver_data->vpn_driver->flags == VPN_FLAG_NO_TUN)
-		return 0;
-
-	memset(&ifr, 0, sizeof(ifr));
-	ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
-	sprintf(ifr.ifr_name, "%s", data->if_name);
-
-	fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
-	if (fd < 0) {
-		err = -errno;
-		connman_error("Failed to open /dev/net/tun to device %s: %s",
-			      data->if_name, strerror(errno));
-		return err;
-	}
-
-	if (ioctl(fd, TUNSETIFF, (void *)&ifr)) {
-		err = -errno;
-		connman_error("Failed to TUNSETIFF for device %s to it: %s",
-			      data->if_name, strerror(errno));
-		close(fd);
-		return err;
-	}
-
-	if (ioctl(fd, TUNSETPERSIST, 0)) {
-		err = -errno;
-		connman_error("Failed to set tun device %s nonpersistent: %s",
-			      data->if_name, strerror(errno));
-		close(fd);
-		return err;
-	}
-	close(fd);
-	DBG("Killed tun device %s", data->if_name);
-	return 0;
-}
-
-void vpn_died(struct connman_task *task, int exit_code, void *user_data)
-{
-	struct connman_provider *provider = user_data;
-	struct vpn_data *data = connman_provider_get_data(provider);
-	int state = VPN_STATE_FAILURE;
-	enum connman_provider_error ret;
-
-	DBG("provider %p data %p", provider, data);
-
-	if (data == NULL)
-		goto vpn_exit;
-
-	state = data->state;
-
-	stop_vpn(provider);
-	connman_provider_set_data(provider, NULL);
-
-	if (data->watch != 0) {
-		connman_provider_unref(provider);
-		connman_rtnl_remove_watch(data->watch);
-		data->watch = 0;
-	}
-
-vpn_exit:
-	if (state != VPN_STATE_READY && state != VPN_STATE_DISCONNECT) {
-		const char *name;
-		struct vpn_driver_data *vpn_data = NULL;
-
-		name = connman_provider_get_driver_name(provider);
-		if (name != NULL)
-			vpn_data = g_hash_table_lookup(driver_hash, name);
-
-		if (vpn_data != NULL &&
-				vpn_data->vpn_driver->error_code != NULL)
-			ret = vpn_data->vpn_driver->error_code(exit_code);
-		else
-			ret = CONNMAN_PROVIDER_ERROR_UNKNOWN;
-
-		connman_provider_indicate_error(provider, ret);
-	} else
-		connman_provider_set_state(provider,
-						CONNMAN_PROVIDER_STATE_IDLE);
-
-	connman_provider_set_index(provider, -1);
-
-	if (data != NULL) {
-		connman_provider_unref(data->provider);
-		g_free(data->if_name);
-		g_free(data);
-	}
-
-	connman_task_destroy(task);
-}
-
-int vpn_set_ifname(struct connman_provider *provider, const char *ifname)
-{
-	struct vpn_data *data = connman_provider_get_data(provider);
-	int index;
-
-	if (ifname == NULL || data == NULL)
-		return  -EIO;
-
-	index = connman_inet_ifindex(ifname);
-	if (index < 0)
-		return  -EIO;
-
-	if (data->if_name != NULL)
-		g_free(data->if_name);
-
-	data->if_name = (char *)g_strdup(ifname);
-	connman_provider_set_index(provider, index);
-
-	return 0;
-}
-
-static void vpn_newlink(unsigned flags, unsigned change, void *user_data)
-{
-	struct connman_provider *provider = user_data;
-	struct vpn_data *data = connman_provider_get_data(provider);
-
-	if ((data->flags & IFF_UP) != (flags & IFF_UP)) {
-		if (flags & IFF_UP) {
-			data->state = VPN_STATE_READY;
-			connman_provider_set_state(provider,
-					CONNMAN_PROVIDER_STATE_READY);
-		}
-	}
-	data->flags = flags;
-}
-
-static DBusMessage *vpn_notify(struct connman_task *task,
-			DBusMessage *msg, void *user_data)
-{
-	struct connman_provider *provider = user_data;
-	struct vpn_data *data;
-	struct vpn_driver_data *vpn_driver_data;
-	const char *name;
-	int state, index;
-
-	data = connman_provider_get_data(provider);
-
-	name = connman_provider_get_driver_name(provider);
-	if (name == NULL)
-		return NULL;
-
-	vpn_driver_data = g_hash_table_lookup(driver_hash, name);
-	if (vpn_driver_data == NULL)
-		return NULL;
-
-	state = vpn_driver_data->vpn_driver->notify(msg, provider);
-	switch (state) {
-	case VPN_STATE_CONNECT:
-	case VPN_STATE_READY:
-		index = connman_provider_get_index(provider);
-		connman_provider_ref(provider);
-		data->watch = connman_rtnl_add_newlink_watch(index,
-						     vpn_newlink, provider);
-		connman_inet_ifup(index);
-		break;
-
-	case VPN_STATE_UNKNOWN:
-	case VPN_STATE_IDLE:
-	case VPN_STATE_DISCONNECT:
-	case VPN_STATE_FAILURE:
-		connman_provider_set_state(provider,
-					CONNMAN_PROVIDER_STATE_DISCONNECT);
-		break;
-
-	case VPN_STATE_AUTH_FAILURE:
-		connman_provider_indicate_error(provider,
-					CONNMAN_PROVIDER_ERROR_AUTH_FAILED);
-		break;
-	}
-
-	return NULL;
-}
-
-static int vpn_create_tun(struct connman_provider *provider)
-{
-	struct vpn_data *data = connman_provider_get_data(provider);
-	struct ifreq ifr;
-	int i, fd, index;
-	int ret = 0;
-
-	if (data == NULL)
-		return -EISCONN;
-
-	fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
-	if (fd < 0) {
-		i = -errno;
-		connman_error("Failed to open /dev/net/tun: %s",
-			      strerror(errno));
-		ret = i;
-		goto exist_err;
-	}
-
-	memset(&ifr, 0, sizeof(ifr));
-	ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
-
-	for (i = 0; i < 256; i++) {
-		sprintf(ifr.ifr_name, "vpn%d", i);
-
-		if (!ioctl(fd, TUNSETIFF, (void *)&ifr))
-			break;
-	}
-
-	if (i == 256) {
-		connman_error("Failed to find available tun device");
-		close(fd);
-		ret = -ENODEV;
-		goto exist_err;
-	}
-
-	data->if_name = (char *)g_strdup(ifr.ifr_name);
-	if (data->if_name == NULL) {
-		connman_error("Failed to allocate memory");
-		close(fd);
-		ret = -ENOMEM;
-		goto exist_err;
-	}
-
-	if (ioctl(fd, TUNSETPERSIST, 1)) {
-		i = -errno;
-		connman_error("Failed to set tun persistent: %s",
-			      strerror(errno));
-		close(fd);
-		ret = i;
-		goto exist_err;
-	}
-
-	close(fd);
-
-	index = connman_inet_ifindex(data->if_name);
-	if (index < 0) {
-		connman_error("Failed to get tun ifindex");
-		stop_vpn(provider);
-		ret = -EIO;
-		goto exist_err;
-	}
-	connman_provider_set_index(provider, index);
-
-	return 0;
-
-exist_err:
-	return ret;
-}
-
-static int vpn_connect(struct connman_provider *provider)
-{
-	struct vpn_data *data = connman_provider_get_data(provider);
-	struct vpn_driver_data *vpn_driver_data;
-	const char *name;
-	int ret = 0;
-
-	if (data != NULL)
-		return -EISCONN;
-
-	data = g_try_new0(struct vpn_data, 1);
-	if (data == NULL)
-		return -ENOMEM;
-
-	data->provider = connman_provider_ref(provider);
-	data->watch = 0;
-	data->flags = 0;
-	data->task = NULL;
-	data->state = VPN_STATE_IDLE;
-
-	connman_provider_set_data(provider, data);
-
-	name = connman_provider_get_driver_name(provider);
-	if (name == NULL)
-		return -EINVAL;
-
-	vpn_driver_data = g_hash_table_lookup(driver_hash, name);
-
-	if (vpn_driver_data == NULL || vpn_driver_data->vpn_driver == NULL) {
-		ret = -EINVAL;
-		goto exist_err;
-	}
-
-	if (vpn_driver_data->vpn_driver->flags != VPN_FLAG_NO_TUN) {
-		ret = vpn_create_tun(provider);
-		if (ret < 0)
-			goto exist_err;
-	}
-
-	data->task = connman_task_create(vpn_driver_data->program);
-
-	if (data->task == NULL) {
-		ret = -ENOMEM;
-		stop_vpn(provider);
-		goto exist_err;
-	}
-
-	if (connman_task_set_notify(data->task, "notify",
-					vpn_notify, provider)) {
-		ret = -ENOMEM;
-		stop_vpn(provider);
-		connman_task_destroy(data->task);
-		data->task = NULL;
-		goto exist_err;
-	}
-
-	ret = vpn_driver_data->vpn_driver->connect(provider, data->task,
-							data->if_name);
-	if (ret < 0) {
-		stop_vpn(provider);
-		connman_task_destroy(data->task);
-		data->task = NULL;
-		goto exist_err;
-	}
-
-	DBG("%s started with dev %s",
-		vpn_driver_data->provider_driver.name, data->if_name);
-
-	data->state = VPN_STATE_CONNECT;
-
-	return -EINPROGRESS;
-
-exist_err:
-	connman_provider_set_index(provider, -1);
-	connman_provider_set_data(provider, NULL);
-	connman_provider_unref(data->provider);
-	g_free(data->if_name);
-	g_free(data);
-
-	return ret;
-}
-
-static int vpn_probe(struct connman_provider *provider)
-{
-	return 0;
-}
-
-static int vpn_disconnect(struct connman_provider *provider)
-{
-	struct vpn_data *data = connman_provider_get_data(provider);
-	struct vpn_driver_data *vpn_driver_data;
-	const char *name;
-
-	DBG("disconnect provider %p:", provider);
-
-	if (data == NULL)
-		return 0;
-
-	name = connman_provider_get_driver_name(provider);
-	if (name == NULL)
-		return 0;
-
-	vpn_driver_data = g_hash_table_lookup(driver_hash, name);
-	if (vpn_driver_data->vpn_driver->disconnect)
-		vpn_driver_data->vpn_driver->disconnect();
-
-	if (data->watch != 0) {
-		connman_provider_unref(provider);
-		connman_rtnl_remove_watch(data->watch);
-		data->watch = 0;
-	}
-
-	data->state = VPN_STATE_DISCONNECT;
-	connman_task_stop(data->task);
-
-	return 0;
-}
-
-static int vpn_remove(struct connman_provider *provider)
-{
-	struct vpn_data *data;
-
-	data = connman_provider_get_data(provider);
-	if (data == NULL)
-		return 0;
-
-	if (data->watch != 0) {
-		connman_provider_unref(provider);
-		connman_rtnl_remove_watch(data->watch);
-		data->watch = 0;
-	}
-
-	connman_task_stop(data->task);
-
-	g_usleep(G_USEC_PER_SEC);
-	stop_vpn(provider);
-	return 0;
-}
-
-static int vpn_save (struct connman_provider *provider, GKeyFile *keyfile)
-{
-	struct vpn_driver_data *vpn_driver_data;
-	const char *name;
-
-	name = connman_provider_get_driver_name(provider);
-	vpn_driver_data = g_hash_table_lookup(driver_hash, name);
-	if (vpn_driver_data != NULL &&
-			vpn_driver_data->vpn_driver->save != NULL)
-		return vpn_driver_data->vpn_driver->save(provider, keyfile);
-
-	return 0;
-}
-
-int vpn_register(const char *name, struct vpn_driver *vpn_driver,
-			const char *program)
-{
-	struct vpn_driver_data *data;
-
-	data = g_try_new0(struct vpn_driver_data, 1);
-	if (data == NULL)
-		return -ENOMEM;
-
-	data->name = name;
-	data->program = program;
-
-	data->vpn_driver = vpn_driver;
-
-	data->provider_driver.name = name;
-	data->provider_driver.disconnect = vpn_disconnect;
-	data->provider_driver.connect = vpn_connect;
-	data->provider_driver.probe = vpn_probe;
-	data->provider_driver.remove = vpn_remove;
-	data->provider_driver.save = vpn_save;
-
-	if (driver_hash == NULL)
-		driver_hash = g_hash_table_new_full(g_str_hash,
-							g_str_equal,
-							NULL, g_free);
-
-	if (driver_hash == NULL) {
-		connman_error("driver_hash not initialized for %s", name);
-		g_free(data);
-		return -ENOMEM;
-	}
-
-	g_hash_table_replace(driver_hash, (char *)name, data);
-
-	connman_provider_driver_register(&data->provider_driver);
-
-	return 0;
-}
-
-void vpn_unregister(const char *name)
-{
-	struct vpn_driver_data *data;
-
-	data = g_hash_table_lookup(driver_hash, name);
-	if (data == NULL)
-		return;
-
-	connman_provider_driver_unregister(&data->provider_driver);
-
-	g_hash_table_remove(driver_hash, name);
-
-	if (g_hash_table_size(driver_hash) == 0)
-		g_hash_table_destroy(driver_hash);
-}
diff --git a/plugins/vpn.h b/plugins/vpn.h
deleted file mode 100644
index 1ecfba2..0000000
--- a/plugins/vpn.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#define VPN_FLAG_NO_TUN	1
-
-enum vpn_state {
-	VPN_STATE_UNKNOWN       = 0,
-	VPN_STATE_IDLE          = 1,
-	VPN_STATE_CONNECT       = 2,
-	VPN_STATE_READY         = 3,
-	VPN_STATE_DISCONNECT    = 4,
-	VPN_STATE_FAILURE       = 5,
-	VPN_STATE_AUTH_FAILURE  = 6,
-};
-
-struct vpn_driver {
-	int flags;
-	int (*notify) (DBusMessage *msg, struct connman_provider *provider);
-	int (*connect) (struct connman_provider *provider,
-			struct connman_task *task, const char *if_name);
-	void (*disconnect) (void);
-	int (*error_code) (int exit_code);
-	int (*save) (struct connman_provider *provider, GKeyFile *keyfile);
-};
-
-int vpn_register(const char *name, struct vpn_driver *driver,
-			const char *program);
-void vpn_unregister(const char *provider_name);
-void vpn_died(struct connman_task *task, int exit_code, void *user_data);
-int vpn_set_ifname(struct connman_provider *provider, const char *ifname);
diff --git a/plugins/vpnc.c b/plugins/vpnc.c
deleted file mode 100644
index 6b7a02b..0000000
--- a/plugins/vpnc.c
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- *
- *  Connection Manager
- *
- *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <net/if.h>
-
-#include <glib.h>
-
-#define CONNMAN_API_SUBJECT_TO_CHANGE
-#include <connman/plugin.h>
-#include <connman/provider.h>
-#include <connman/log.h>
-#include <connman/task.h>
-#include <connman/ipconfig.h>
-#include <connman/dbus.h>
-
-#include "vpn.h"
-
-#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
-
-static DBusConnection *connection;
-
-enum {
-	OPT_STRING = 1,
-	OPT_BOOLEAN = 2,
-};
-
-struct {
-	const char *cm_opt;
-	const char *vpnc_opt;
-	const char *vpnc_default;
-	int type;
-	connman_bool_t cm_save;
-} vpnc_options[] = {
-	{ "Host", "IPSec gateway", NULL, OPT_STRING, TRUE },
-	{ "VPNC.IPSec.ID", "IPSec ID", NULL, OPT_STRING, TRUE },
-	{ "VPNC.IPSec.Secret", "IPSec secret", NULL, OPT_STRING, FALSE },
-	{ "VPNC.Xauth.Username", "Xauth username", NULL, OPT_STRING, FALSE },
-	{ "VPNC.Xauth.Password", "Xauth password", NULL, OPT_STRING, FALSE },
-	{ "VPNC.IKE.Authmode", "IKE Authmode", NULL, OPT_STRING, TRUE },
-	{ "VPNC.IKE.DHGroup", "IKE DH Group", NULL, OPT_STRING, TRUE },
-	{ "VPNC.PFS", "Perfect Forward Secrecy", NULL, OPT_STRING, TRUE },
-	{ "VPNC.Domain", "Domain", NULL, OPT_STRING, TRUE },
-	{ "VPNC.Vendor", "Vendor", NULL, OPT_STRING, TRUE },
-	{ "VPNC.LocalPort", "Local Port", "0", OPT_STRING, TRUE, },
-	{ "VPNC.CiscoPort","Cisco UDP Encapsulation Port", "0", OPT_STRING,
-	  TRUE },
-	{ "VPNC.AppVersion", "Application Version", NULL, OPT_STRING, TRUE },
-	{ "VPNC.NATTMode", "NAT Traversal Mode", "cisco-udp", OPT_STRING,
-	  TRUE },
-	{ "VPNC.DPDTimeout", "DPD idle timeout (our side)", NULL, OPT_STRING,
-	  TRUE },
-	{ "VPNC.SingleDES", "Enable Single DES", NULL, OPT_BOOLEAN, TRUE },
-	{ "VPNC.NoEncryption", "Enable no encryption", NULL, OPT_BOOLEAN,
-	  TRUE },
-};
-
-static int vc_notify(DBusMessage *msg, struct connman_provider *provider)
-{
-	DBusMessageIter iter, dict;
-	char *address = NULL, *netmask = NULL, *gateway = NULL;
-	struct connman_ipaddress *ipaddress;
-	const char *reason, *key, *value;
-
-	dbus_message_iter_init(msg, &iter);
-
-	dbus_message_iter_get_basic(&iter, &reason);
-	dbus_message_iter_next(&iter);
-
-	if (!provider) {
-		connman_error("No provider found");
-		return VPN_STATE_FAILURE;
-	}
-
-	if (strcmp(reason, "connect"))
-		return VPN_STATE_DISCONNECT;
-
-	dbus_message_iter_recurse(&iter, &dict);
-
-	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
-		DBusMessageIter entry;
-
-		dbus_message_iter_recurse(&dict, &entry);
-		dbus_message_iter_get_basic(&entry, &key);
-		dbus_message_iter_next(&entry);
-		dbus_message_iter_get_basic(&entry, &value);
-
-		DBG("%s = %s", key, value);
-
-		if (!strcmp(key, "VPNGATEWAY"))
-			gateway = g_strdup(value);
-
-		if (!strcmp(key, "INTERNAL_IP4_ADDRESS"))
-			address = g_strdup(value);
-
-		if (!strcmp(key, "INTERNAL_IP4_NETMASK"))
-			netmask = g_strdup(value);
-
-		if (!strcmp(key, "INTERNAL_IP4_DNS"))
-			connman_provider_set_nameservers(provider, value);
-
-		if (!strcmp(key, "CISCO_DEF_DOMAIN"))
-			connman_provider_set_domain(provider, value);
-
-		if (g_str_has_prefix(key, "CISCO_SPLIT_INC") == TRUE ||
-			g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC") == TRUE)
-			connman_provider_append_route(provider, key, value);
-
-		dbus_message_iter_next(&dict);
-	}
-
-
-	ipaddress = connman_ipaddress_alloc(AF_INET);
-	if (ipaddress == NULL) {
-		g_free(address);
-		g_free(netmask);
-		g_free(gateway);
-
-		return VPN_STATE_FAILURE;
-	}
-
-	connman_ipaddress_set_ipv4(ipaddress, address, netmask, gateway);
-	connman_provider_set_ipaddress(provider, ipaddress);
-
-	g_free(address);
-	g_free(netmask);
-	g_free(gateway);
-	connman_ipaddress_free(ipaddress);
-
-	return VPN_STATE_CONNECT;
-}
-
-static ssize_t full_write(int fd, const void *buf, size_t len)
-{
-	ssize_t byte_write;
-
-	while (len) {
-		byte_write = write(fd, buf, len);
-		if (byte_write < 0) {
-			connman_error("failed to write config to vpnc: %s\n",
-					strerror(errno));
-			return byte_write;
-		}
-		len -= byte_write;
-		buf += byte_write;
-	}
-
-	return 0;
-}
-
-static ssize_t write_option(int fd, const char *key, const char *value)
-{
-	gchar *buf;
-	ssize_t ret = 0;
-
-	if (key != NULL && value != NULL) {
-		buf = g_strdup_printf("%s %s\n", key, value);
-		ret = full_write(fd, buf, strlen(buf));
-
-		g_free(buf);
-	}
-
-	return ret;
-}
-
-static ssize_t write_bool_option(int fd, const char *key, const char *value)
-{
-	gchar *buf;
-	ssize_t ret = 0;
-
-	if (key != NULL && value != NULL) {
-		if (strcasecmp(value, "yes") == 0 ||
-				strcasecmp(value, "true") == 0 ||
-				strcmp(value, "1") == 0) {
-			buf = g_strdup_printf("%s\n", key);
-			ret = full_write(fd, buf, strlen(buf));
-
-			g_free(buf);
-		}
-	}
-
-	return ret;
-}
-
-static int vc_write_config_data(struct connman_provider *provider, int fd)
-{
-	const char *opt_s;
-	int i;
-
-	for (i = 0; i < (int)ARRAY_SIZE(vpnc_options); i++) {
-		opt_s = connman_provider_get_string(provider,
-					vpnc_options[i].cm_opt);
-		if (!opt_s)
-			opt_s= vpnc_options[i].vpnc_default;
-
-		if(!opt_s)
-			continue;
-
-		if (vpnc_options[i].type == OPT_STRING) {
-			if (write_option(fd,
-					vpnc_options[i].vpnc_opt, opt_s) < 0)
-				return -EIO;
-		} else if (vpnc_options[i].type == OPT_BOOLEAN) {
-			if (write_bool_option(fd,
-					vpnc_options[i].vpnc_opt, opt_s) < 0)
-				return -EIO;
-		}
-
-	}
-
-	return 0;
-}
-
-static int vc_save(struct connman_provider *provider, GKeyFile *keyfile)
-{
-	const char *option;
-	int i;
-
-	for (i = 0; i < (int)ARRAY_SIZE(vpnc_options); i++) {
-		if (strncmp(vpnc_options[i].cm_opt, "VPNC.", 5) == 0) {
-
-			if (vpnc_options[i].cm_save == FALSE)
-				continue;
-
-			option = connman_provider_get_string(provider,
-							vpnc_options[i].cm_opt);
-			if (option == NULL)
-				continue;
-
-			g_key_file_set_string(keyfile,
-					connman_provider_get_save_group(provider),
-					vpnc_options[i].cm_opt, option);
-		}
-	}
-	return 0;
-}
-
-static int vc_connect(struct connman_provider *provider,
-		struct connman_task *task, const char *if_name)
-{
-	const char *option;
-	int err, fd;
-
-	option = connman_provider_get_string(provider, "Host");
-	if (option == NULL) {
-		connman_error("Host not set; cannot enable VPN");
-		return -EINVAL;
-	}
-	option = connman_provider_get_string(provider, "VPNC.IPSec.ID");
-	if (option == NULL) {
-		connman_error("Group not set; cannot enable VPN");
-		return -EINVAL;
-	}
-
-	connman_task_add_argument(task, "--non-inter", NULL);
-	connman_task_add_argument(task, "--no-detach", NULL);
-
-	connman_task_add_argument(task, "--ifname", if_name);
-	connman_task_add_argument(task, "--ifmode", "tun");
-
-	connman_task_add_argument(task, "--script",
-				SCRIPTDIR "/openconnect-script");
-
-	option = connman_provider_get_string(provider, "VPNC.Debug");
-	if (option != NULL)
-		connman_task_add_argument(task, "--debug", option);
-
-	connman_task_add_argument(task, "-", NULL);
-
-	err = connman_task_run(task, vpn_died, provider,
-				&fd, NULL, NULL);
-	if (err < 0) {
-		connman_error("vpnc failed to start");
-		return -EIO;
-	}
-
-	err = vc_write_config_data(provider, fd);
-
-	close(fd);
-
-	return err;
-}
-
-static int vc_error_code(int exit_code)
-{
-	switch (exit_code) {
-	case 1:
-		return CONNMAN_PROVIDER_ERROR_CONNECT_FAILED;
-	case 2:
-		return CONNMAN_PROVIDER_ERROR_LOGIN_FAILED;
-	default:
-		return CONNMAN_PROVIDER_ERROR_UNKNOWN;
-	}
-}
-
-static struct vpn_driver vpn_driver = {
-	.notify		= vc_notify,
-	.connect	= vc_connect,
-	.error_code	= vc_error_code,
-	.save		= vc_save,
-};
-
-static int vpnc_init(void)
-{
-	connection = connman_dbus_get_connection();
-
-	return vpn_register("vpnc", &vpn_driver, VPNC);
-}
-
-static void vpnc_exit(void)
-{
-	vpn_unregister("vpnc");
-
-	dbus_connection_unref(connection);
-}
-
-CONNMAN_PLUGIN_DEFINE(vpnc, "vpnc plugin", VERSION,
-	CONNMAN_PLUGIN_PRIORITY_DEFAULT, vpnc_init, vpnc_exit)
diff --git a/src/plugin.c b/src/plugin.c
index 65e0311..adf8525 100644
--- a/src/plugin.c
+++ b/src/plugin.c
@@ -114,7 +114,7 @@ static gboolean check_plugin(struct connman_plugin_desc *desc,
 	return TRUE;
 }
 
-#include "builtin.h"
+#include <builtin.h>
 
 int __connman_plugin_init(const char *pattern, const char *exclude)
 {
diff --git a/vpn/connman-vpn.service.in b/vpn/connman-vpn.service.in
new file mode 100644
index 0000000..ec02a86
--- /dev/null
+++ b/vpn/connman-vpn.service.in
@@ -0,0 +1,12 @@
+[Unit]
+Description=ConnMan VPN service
+After=syslog.target
+
+[Service]
+Type=dbus
+BusName=net.connman.vpn
+ExecStart=@prefix@/sbin/connman-vpnd -n
+StandardOutput=null
+
+[Install]
+WantedBy=multi-user.target
diff --git a/vpn/main.c b/vpn/main.c
new file mode 100644
index 0000000..e89d29b
--- /dev/null
+++ b/vpn/main.c
@@ -0,0 +1,257 @@
+/*
+ *
+ *  ConnMan VPN daemon
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+#include <getopt.h>
+#include <sys/stat.h>
+#include <net/if.h>
+#include <netdb.h>
+
+#include <gdbus.h>
+
+#include "../src/connman.h"
+#include "vpn.h"
+
+#include "vpn-dbus.h"
+
+static GMainLoop *main_loop = NULL;
+
+static unsigned int __terminated = 0;
+
+static gboolean signal_handler(GIOChannel *channel, GIOCondition cond,
+							gpointer user_data)
+{
+	struct signalfd_siginfo si;
+	ssize_t result;
+	int fd;
+
+	if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP))
+		return FALSE;
+
+	fd = g_io_channel_unix_get_fd(channel);
+
+	result = read(fd, &si, sizeof(si));
+	if (result != sizeof(si))
+		return FALSE;
+
+	switch (si.ssi_signo) {
+	case SIGINT:
+	case SIGTERM:
+		if (__terminated == 0) {
+			connman_info("Terminating");
+			g_main_loop_quit(main_loop);
+		}
+
+		__terminated = 1;
+		break;
+	}
+
+	return TRUE;
+}
+
+static guint setup_signalfd(void)
+{
+	GIOChannel *channel;
+	guint source;
+	sigset_t mask;
+	int fd;
+
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGINT);
+	sigaddset(&mask, SIGTERM);
+
+	if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) {
+		perror("Failed to set signal mask");
+		return 0;
+	}
+
+	fd = signalfd(-1, &mask, 0);
+	if (fd < 0) {
+		perror("Failed to create signal descriptor");
+		return 0;
+	}
+
+	channel = g_io_channel_unix_new(fd);
+
+	g_io_channel_set_close_on_unref(channel, TRUE);
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	source = g_io_add_watch(channel,
+				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+				signal_handler, NULL);
+
+	g_io_channel_unref(channel);
+
+	return source;
+}
+
+static void disconnect_callback(DBusConnection *conn, void *user_data)
+{
+	connman_error("D-Bus disconnect");
+
+	g_main_loop_quit(main_loop);
+}
+
+static gchar *option_debug = NULL;
+static gchar *option_plugin = NULL;
+static gchar *option_noplugin = NULL;
+static gboolean option_detach = TRUE;
+static gboolean option_version = FALSE;
+
+static gboolean parse_debug(const char *key, const char *value,
+					gpointer user_data, GError **error)
+{
+	if (value)
+		option_debug = g_strdup(value);
+	else
+		option_debug = g_strdup("*");
+
+	return TRUE;
+}
+
+static GOptionEntry options[] = {
+	{ "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG,
+				G_OPTION_ARG_CALLBACK, parse_debug,
+				"Specify debug options to enable", "DEBUG" },
+	{ "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin,
+				"Specify plugins to load", "NAME,..." },
+	{ "noplugin", 'P', 0, G_OPTION_ARG_STRING, &option_noplugin,
+				"Specify plugins not to load", "NAME,..." },
+	{ "nodaemon", 'n', G_OPTION_FLAG_REVERSE,
+				G_OPTION_ARG_NONE, &option_detach,
+				"Don't fork daemon to background" },
+	{ "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
+				"Show version information and exit" },
+	{ NULL },
+};
+
+int main(int argc, char *argv[])
+{
+	GOptionContext *context;
+	GError *error = NULL;
+	DBusConnection *conn;
+	DBusError err;
+	guint signal;
+
+	context = g_option_context_new(NULL);
+	g_option_context_add_main_entries(context, options, NULL);
+
+	if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
+		if (error != NULL) {
+			g_printerr("%s\n", error->message);
+			g_error_free(error);
+		} else
+			g_printerr("An unknown error occurred\n");
+		exit(1);
+	}
+
+	g_option_context_free(context);
+
+	if (option_version == TRUE) {
+		printf("%s\n", VERSION);
+		exit(0);
+	}
+
+	if (option_detach == TRUE) {
+		if (daemon(0, 0)) {
+			perror("Can't start daemon");
+			exit(1);
+		}
+	}
+
+	if (mkdir(STATEDIR, S_IRUSR | S_IWUSR | S_IXUSR |
+				S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) {
+		if (errno != EEXIST)
+			perror("Failed to create state directory");
+	}
+
+	if (mkdir(STORAGEDIR, S_IRUSR | S_IWUSR | S_IXUSR |
+				S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) {
+		if (errno != EEXIST)
+			perror("Failed to create storage directory");
+	}
+
+	umask(0077);
+
+	main_loop = g_main_loop_new(NULL, FALSE);
+
+	signal = setup_signalfd();
+
+	dbus_error_init(&err);
+
+	conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, VPN_SERVICE, &err);
+	if (conn == NULL) {
+		if (dbus_error_is_set(&err) == TRUE) {
+			fprintf(stderr, "%s\n", err.message);
+			dbus_error_free(&err);
+		} else
+			fprintf(stderr, "Can't register with system bus\n");
+		exit(1);
+	}
+
+	g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL);
+
+	__connman_log_init(argv[0], option_debug, option_detach, FALSE,
+			"Connection Manager VPN daemon", VERSION);
+	__connman_dbus_init(conn);
+	__vpn_provider_init();
+	__vpn_manager_init();
+	__vpn_ipconfig_init();
+	__vpn_rtnl_init();
+	__connman_task_init();
+	__connman_plugin_init(option_plugin, option_noplugin);
+
+	__vpn_rtnl_start();
+
+	g_free(option_plugin);
+	g_free(option_noplugin);
+
+	g_main_loop_run(main_loop);
+
+	g_source_remove(signal);
+
+	__connman_task_cleanup();
+	__vpn_rtnl_cleanup();
+	__vpn_ipconfig_cleanup();
+	__vpn_manager_cleanup();
+	__vpn_provider_cleanup();
+	__connman_dbus_cleanup();
+	__connman_log_cleanup(FALSE);
+
+	dbus_connection_unref(conn);
+
+	g_main_loop_unref(main_loop);
+
+	g_free(option_debug);
+
+	return 0;
+}
diff --git a/vpn/net.connman.vpn.service.in b/vpn/net.connman.vpn.service.in
new file mode 100644
index 0000000..fc9e9bf
--- /dev/null
+++ b/vpn/net.connman.vpn.service.in
@@ -0,0 +1,5 @@
+[D-BUS Service]
+Name=net.connman.vpn
+Exec=@prefix@/sbin/connman-vpnd -n
+User=root
+SystemdService=connman-vpn.service
diff --git a/vpn/plugins/l2tp.c b/vpn/plugins/l2tp.c
new file mode 100644
index 0000000..5a11e52
--- /dev/null
+++ b/vpn/plugins/l2tp.c
@@ -0,0 +1,533 @@
+/*
+ *
+ *  ConnMan VPN daemon
+ *
+ *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <stdio.h>
+#include <net/if.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/provider.h>
+#include <connman/log.h>
+#include <connman/task.h>
+#include <connman/dbus.h>
+#include <connman/inet.h>
+
+#include "vpn.h"
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+enum {
+	OPT_STRING = 1,
+	OPT_BOOL = 2,
+};
+
+enum {
+	OPT_ALL = 1,
+	OPT_L2G = 2,
+	OPT_L2	= 3,
+	OPT_PPPD = 4,
+};
+
+struct {
+	const char *cm_opt;
+	const char *pppd_opt;
+	int sub;
+	const char *vpn_default;
+	int type;
+} pppd_options[] = {
+	{ "L2TP.User", "name", OPT_ALL, NULL, OPT_STRING },
+	{ "L2TP.BPS", "bps", OPT_L2, NULL, OPT_STRING },
+	{ "L2TP.LengthBit", "length bit", OPT_L2, NULL, OPT_STRING },
+	{ "L2TP.Challenge", "challenge", OPT_L2, NULL, OPT_STRING },
+	{ "L2TP.DefaultRoute", "defaultroute", OPT_L2, NULL, OPT_STRING },
+	{ "L2TP.FlowBit", "flow bit", OPT_L2, NULL, OPT_STRING },
+	{ "L2TP.TunnelRWS", "tunnel rws", OPT_L2, NULL, OPT_STRING },
+	{ "L2TP.Exclusive", "exclusive", OPT_L2, NULL, OPT_STRING },
+	{ "L2TP.Autodial", "autodial", OPT_L2, "yes", OPT_STRING },
+	{ "L2TP.Redial", "redial", OPT_L2, "yes", OPT_STRING },
+	{ "L2TP.RedialTimeout", "redial timeout", OPT_L2, "10", OPT_STRING },
+	{ "L2TP.MaxRedials", "max redials", OPT_L2, NULL, OPT_STRING },
+	{ "L2TP.RequirePAP", "require pap", OPT_L2, "no", OPT_STRING },
+	{ "L2TP.RequireCHAP", "require chap", OPT_L2, "yes", OPT_STRING },
+	{ "L2TP.ReqAuth", "require authentication", OPT_L2, "no", OPT_STRING },
+	{ "L2TP.AccessControl", "access control", OPT_L2G, "yes", OPT_STRING },
+	{ "L2TP.AuthFile", "auth file", OPT_L2G, NULL, OPT_STRING },
+	{ "L2TP.ForceUserSpace", "force userspace", OPT_L2G, NULL, OPT_STRING },
+	{ "L2TP.ListenAddr", "listen-addr", OPT_L2G, NULL, OPT_STRING },
+	{ "L2TP.Rand Source", "rand source", OPT_L2G, NULL, OPT_STRING },
+	{ "L2TP.IPsecSaref", "ipsec saref", OPT_L2G, NULL, OPT_STRING },
+	{ "L2TP.Port", "port", OPT_L2G, NULL, OPT_STRING },
+	{ "L2TP.EchoFailure", "lcp-echo-failure", OPT_PPPD, "0", OPT_STRING },
+	{ "L2TP.EchoInterval", "lcp-echo-interval", OPT_PPPD, "0", OPT_STRING },
+	{ "L2TP.Debug", "debug", OPT_PPPD, NULL, OPT_STRING },
+	{ "L2TP.RefuseEAP", "refuse-eap", OPT_PPPD, NULL, OPT_BOOL },
+	{ "L2TP.RefusePAP", "refuse-pap", OPT_PPPD, NULL, OPT_BOOL },
+	{ "L2TP.RefuseCHAP", "refuse-chap", OPT_PPPD, NULL, OPT_BOOL },
+	{ "L2TP.RefuseMSCHAP", "refuse-mschap", OPT_PPPD, NULL, OPT_BOOL },
+	{ "L2TP.RefuseMSCHAP2", "refuse-mschapv2", OPT_PPPD, NULL, OPT_BOOL },
+	{ "L2TP.NoBSDComp", "nobsdcomp", OPT_PPPD, NULL, OPT_BOOL },
+	{ "L2TP.NoPcomp", "nopcomp", OPT_PPPD, NULL, OPT_BOOL },
+	{ "L2TP.UseAccomp", "accomp", OPT_PPPD, NULL, OPT_BOOL },
+	{ "L2TP.NoDeflate", "nodeflatey", OPT_PPPD, NULL, OPT_BOOL },
+	{ "L2TP.ReqMPPE", "require-mppe", OPT_PPPD, NULL, OPT_BOOL },
+	{ "L2TP.ReqMPPE40", "require-mppe-40", OPT_PPPD, NULL, OPT_BOOL },
+	{ "L2TP.ReqMPPE128", "require-mppe-128", OPT_PPPD, NULL, OPT_BOOL },
+	{ "L2TP.ReqMPPEStateful", "mppe-stateful", OPT_PPPD, NULL, OPT_BOOL },
+	{ "L2TP.NoVJ", "no-vj-comp", OPT_PPPD, NULL, OPT_BOOL },
+};
+
+static DBusConnection *connection;
+
+static DBusMessage *l2tp_get_sec(struct connman_task *task,
+			DBusMessage *msg, void *user_data)
+{
+	const char *user, *passwd;
+	struct vpn_provider *provider = user_data;
+
+	if (dbus_message_get_no_reply(msg) == FALSE) {
+		DBusMessage *reply;
+
+		user = vpn_provider_get_string(provider, "L2TP.User");
+		passwd = vpn_provider_get_string(provider, "L2TP.Password");
+
+		if (user == NULL || strlen(user) == 0 ||
+				passwd == NULL || strlen(passwd) == 0)
+			return NULL;
+
+		reply = dbus_message_new_method_return(msg);
+		if (reply == NULL)
+			return NULL;
+
+		dbus_message_append_args(reply, DBUS_TYPE_STRING, &user,
+						DBUS_TYPE_STRING, &passwd,
+						DBUS_TYPE_INVALID);
+
+		return reply;
+	}
+
+	return NULL;
+}
+
+static int l2tp_notify(DBusMessage *msg, struct vpn_provider *provider)
+{
+	DBusMessageIter iter, dict;
+	const char *reason, *key, *value;
+	char *addressv4 = NULL, *netmask = NULL, *gateway = NULL;
+	char *ifname = NULL, *nameservers = NULL;
+	struct connman_ipaddress *ipaddress = NULL;
+
+	dbus_message_iter_init(msg, &iter);
+
+	dbus_message_iter_get_basic(&iter, &reason);
+	dbus_message_iter_next(&iter);
+
+	if (!provider) {
+		connman_error("No provider found");
+		return VPN_STATE_FAILURE;
+	}
+
+	if (strcmp(reason, "auth failed") == 0)
+		return VPN_STATE_AUTH_FAILURE;
+
+	if (strcmp(reason, "connect"))
+		return VPN_STATE_DISCONNECT;
+
+	dbus_message_iter_recurse(&iter, &dict);
+
+	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter entry;
+
+		dbus_message_iter_recurse(&dict, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_get_basic(&entry, &value);
+
+		DBG("%s = %s", key, value);
+
+		if (!strcmp(key, "INTERNAL_IP4_ADDRESS")) {
+			vpn_provider_set_string(provider, "Address", value);
+			addressv4 = g_strdup(value);
+		}
+
+		if (!strcmp(key, "INTERNAL_IP4_NETMASK")) {
+			vpn_provider_set_string(provider, "Netmask", value);
+			netmask = g_strdup(value);
+		}
+
+		if (!strcmp(key, "INTERNAL_IP4_DNS")) {
+			vpn_provider_set_string(provider, "DNS", value);
+			nameservers = g_strdup(value);
+		}
+
+		if (!strcmp(key, "INTERNAL_IFNAME"))
+			ifname = g_strdup(value);
+
+		dbus_message_iter_next(&dict);
+	}
+
+	if (vpn_set_ifname(provider, ifname) < 0) {
+		g_free(ifname);
+		g_free(addressv4);
+		g_free(netmask);
+		g_free(nameservers);
+		return VPN_STATE_FAILURE;
+	}
+
+	if (addressv4 != NULL)
+		ipaddress = connman_ipaddress_alloc(AF_INET);
+
+	g_free(ifname);
+
+	if (ipaddress == NULL) {
+		connman_error("No IP address for provider");
+		g_free(addressv4);
+		g_free(netmask);
+		g_free(nameservers);
+		return VPN_STATE_FAILURE;
+	}
+
+	value = vpn_provider_get_string(provider, "HostIP");
+	if (value != NULL) {
+		vpn_provider_set_string(provider, "Gateway", value);
+		gateway = g_strdup(value);
+	}
+
+	if (addressv4 != NULL)
+		connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask,
+					gateway);
+
+	vpn_provider_set_ipaddress(provider, ipaddress);
+	vpn_provider_set_nameservers(provider, nameservers);
+
+	g_free(addressv4);
+	g_free(netmask);
+	g_free(gateway);
+	g_free(nameservers);
+	connman_ipaddress_free(ipaddress);
+
+	return VPN_STATE_CONNECT;
+}
+
+static int l2tp_save(struct vpn_provider *provider, GKeyFile *keyfile)
+{
+	const char *option;
+	int i;
+
+	for (i = 0; i < (int)ARRAY_SIZE(pppd_options); i++) {
+		if (strncmp(pppd_options[i].cm_opt, "L2TP.", 5) == 0) {
+			option = vpn_provider_get_string(provider,
+							pppd_options[i].cm_opt);
+			if (option == NULL)
+				continue;
+
+			g_key_file_set_string(keyfile,
+					vpn_provider_get_save_group(provider),
+					pppd_options[i].cm_opt, option);
+		}
+	}
+	return 0;
+}
+
+static ssize_t full_write(int fd, const void *buf, size_t len)
+{
+	ssize_t byte_write;
+
+	while (len) {
+		byte_write = write(fd, buf, len);
+		if (byte_write < 0) {
+			connman_error("failed to write config to l2tp: %s\n",
+					strerror(errno));
+			return byte_write;
+		}
+		len -= byte_write;
+		buf += byte_write;
+	}
+
+	return 0;
+}
+
+static ssize_t l2tp_write_bool_option(int fd,
+					const char *key, const char *value)
+{
+	gchar *buf;
+	ssize_t ret = 0;
+
+	if (key != NULL && value != NULL) {
+		if (strcasecmp(value, "yes") == 0 ||
+				strcasecmp(value, "true") == 0 ||
+				strcmp(value, "1") == 0) {
+			buf = g_strdup_printf("%s\n", key);
+			ret = full_write(fd, buf, strlen(buf));
+
+			g_free(buf);
+		}
+	}
+
+	return ret;
+}
+
+static int l2tp_write_option(int fd, const char *key, const char *value)
+{
+	gchar *buf;
+	ssize_t ret = 0;
+
+	if (key != NULL) {
+		if (value != NULL)
+			buf = g_strdup_printf("%s %s\n", key, value);
+		else
+			buf = g_strdup_printf("%s\n", key);
+
+		ret = full_write(fd, buf, strlen(buf));
+
+		g_free(buf);
+	}
+
+	return ret;
+}
+
+static int l2tp_write_section(int fd, const char *key, const char *value)
+{
+	gchar *buf;
+	ssize_t ret = 0;
+
+	if (key != NULL && value != NULL) {
+		buf = g_strdup_printf("%s = %s\n", key, value);
+		ret = full_write(fd, buf, strlen(buf));
+
+		g_free(buf);
+	}
+
+	return ret;
+}
+
+static int write_pppd_option(struct vpn_provider *provider, int fd)
+{
+	int i;
+	const char *opt_s;
+
+	l2tp_write_option(fd, "nodetach", NULL);
+	l2tp_write_option(fd, "lock", NULL);
+	l2tp_write_option(fd, "usepeerdns", NULL);
+	l2tp_write_option(fd, "noipdefault", NULL);
+	l2tp_write_option(fd, "noauth", NULL);
+	l2tp_write_option(fd, "nodefaultroute", NULL);
+	l2tp_write_option(fd, "ipparam", "l2tp_plugin");
+
+	for (i = 0; i < (int)ARRAY_SIZE(pppd_options); i++) {
+		if (pppd_options[i].sub != OPT_ALL &&
+			pppd_options[i].sub != OPT_PPPD)
+			continue;
+
+		opt_s = vpn_provider_get_string(provider,
+					pppd_options[i].cm_opt);
+		if (!opt_s)
+			opt_s = pppd_options[i].vpn_default;
+
+		if (!opt_s)
+			continue;
+
+		if (pppd_options[i].type == OPT_STRING)
+			l2tp_write_option(fd,
+				pppd_options[i].pppd_opt, opt_s);
+		else if (pppd_options[i].type == OPT_BOOL)
+			l2tp_write_bool_option(fd,
+				pppd_options[i].pppd_opt, opt_s);
+	}
+
+	l2tp_write_option(fd, "plugin",
+				SCRIPTDIR "/libppp-plugin.so");
+
+	return 0;
+}
+
+
+static int l2tp_write_fields(struct vpn_provider *provider,
+						int fd, int sub)
+{
+	int i;
+	const char *opt_s;
+
+	for (i = 0; i < (int)ARRAY_SIZE(pppd_options); i++) {
+		if (pppd_options[i].sub != sub)
+			continue;
+
+		opt_s = vpn_provider_get_string(provider,
+					pppd_options[i].cm_opt);
+		if (!opt_s)
+			opt_s = pppd_options[i].vpn_default;
+
+		if (!opt_s)
+			continue;
+
+		if (pppd_options[i].type == OPT_STRING)
+			l2tp_write_section(fd,
+				pppd_options[i].pppd_opt, opt_s);
+		else if (pppd_options[i].type == OPT_BOOL)
+			l2tp_write_bool_option(fd,
+				pppd_options[i].pppd_opt, opt_s);
+	}
+
+	return 0;
+}
+
+static int l2tp_write_config(struct vpn_provider *provider,
+					const char *pppd_name, int fd)
+{
+	const char *option;
+
+	l2tp_write_option(fd, "[global]", NULL);
+	l2tp_write_fields(provider, fd, OPT_L2G);
+
+	l2tp_write_option(fd, "[lac l2tp]", NULL);
+
+	option = vpn_provider_get_string(provider, "Host");
+	l2tp_write_option(fd, "lns =", option);
+
+	l2tp_write_fields(provider, fd, OPT_ALL);
+	l2tp_write_fields(provider, fd, OPT_L2);
+
+	l2tp_write_option(fd, "pppoptfile =", pppd_name);
+
+	return 0;
+}
+
+static void l2tp_died(struct connman_task *task, int exit_code, void *user_data)
+{
+	char *conf_file;
+
+	vpn_died(task, exit_code, user_data);
+
+	conf_file = g_strdup_printf("/var/run/connman/connman-xl2tpd.conf");
+	unlink(conf_file);
+	g_free(conf_file);
+
+	conf_file = g_strdup_printf("/var/run/connman/connman-ppp-option.conf");
+	unlink(conf_file);
+	g_free(conf_file);
+}
+
+static int l2tp_connect(struct vpn_provider *provider,
+		struct connman_task *task, const char *if_name)
+{
+	const char *host;
+	char *l2tp_name, *pppd_name;
+	int l2tp_fd, pppd_fd;
+	int err;
+
+	if (connman_task_set_notify(task, "getsec",
+					l2tp_get_sec, provider))
+		return -ENOMEM;
+
+	host = vpn_provider_get_string(provider, "Host");
+	if (host == NULL) {
+		connman_error("Host not set; cannot enable VPN");
+		return -EINVAL;
+	}
+
+	l2tp_name = g_strdup_printf("/var/run/connman/connman-xl2tpd.conf");
+
+	l2tp_fd = open(l2tp_name, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
+	if (l2tp_fd < 0) {
+		g_free(l2tp_name);
+		connman_error("Error writing l2tp config");
+		return -EIO;
+	}
+
+	pppd_name = g_strdup_printf("/var/run/connman/connman-ppp-option.conf");
+
+	pppd_fd = open(pppd_name, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
+	if (pppd_fd < 0) {
+		connman_error("Error writing pppd config");
+		g_free(l2tp_name);
+		g_free(pppd_name);
+		close(l2tp_fd);
+		return -EIO;
+	}
+
+	l2tp_write_config(provider, pppd_name, l2tp_fd);
+
+	write_pppd_option(provider, pppd_fd);
+
+	connman_task_add_argument(task, "-D", NULL);
+	connman_task_add_argument(task, "-c", l2tp_name);
+
+	g_free(l2tp_name);
+	g_free(pppd_name);
+
+	err = connman_task_run(task, l2tp_died, provider,
+				NULL, NULL, NULL);
+	if (err < 0) {
+		connman_error("l2tp failed to start");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int l2tp_error_code(int exit_code)
+{
+	switch (exit_code) {
+	case 1:
+		return CONNMAN_PROVIDER_ERROR_CONNECT_FAILED;
+	default:
+		return CONNMAN_PROVIDER_ERROR_UNKNOWN;
+	}
+}
+
+static struct vpn_driver vpn_driver = {
+	.flags		= VPN_FLAG_NO_TUN,
+	.notify		= l2tp_notify,
+	.connect	= l2tp_connect,
+	.error_code	= l2tp_error_code,
+	.save		= l2tp_save,
+};
+
+static int l2tp_init(void)
+{
+	connection = connman_dbus_get_connection();
+
+	return vpn_register("l2tp", &vpn_driver, L2TP);
+}
+
+static void l2tp_exit(void)
+{
+	vpn_unregister("l2tp");
+
+	dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(l2tp, "l2tp plugin", VERSION,
+	CONNMAN_PLUGIN_PRIORITY_DEFAULT, l2tp_init, l2tp_exit)
diff --git a/vpn/plugins/openconnect.c b/vpn/plugins/openconnect.c
new file mode 100644
index 0000000..0f54108
--- /dev/null
+++ b/vpn/plugins/openconnect.c
@@ -0,0 +1,282 @@
+/*
+ *
+ *  ConnMan VPN daemon
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <net/if.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/log.h>
+#include <connman/task.h>
+#include <connman/ipconfig.h>
+
+#include "../vpn-provider.h"
+
+#include "vpn.h"
+
+static int oc_notify(DBusMessage *msg, struct vpn_provider *provider)
+{
+	DBusMessageIter iter, dict;
+	const char *reason, *key, *value;
+	const char *domain = NULL;
+	char *addressv4 = NULL, *addressv6 = NULL;
+	char *netmask = NULL, *gateway = NULL;
+	unsigned char prefix_len = 0;
+	struct connman_ipaddress *ipaddress;
+
+	dbus_message_iter_init(msg, &iter);
+
+	dbus_message_iter_get_basic(&iter, &reason);
+	dbus_message_iter_next(&iter);
+
+	if (!provider) {
+		connman_error("No provider found");
+		return VPN_STATE_FAILURE;
+	}
+
+	if (strcmp(reason, "connect"))
+		return VPN_STATE_DISCONNECT;
+
+	domain = vpn_provider_get_string(provider, "VPN.Domain");
+
+	dbus_message_iter_recurse(&iter, &dict);
+
+	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter entry;
+
+		dbus_message_iter_recurse(&dict, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_get_basic(&entry, &value);
+
+		if (strcmp(key, "CISCO_CSTP_OPTIONS"))
+			DBG("%s = %s", key, value);
+
+		if (!strcmp(key, "VPNGATEWAY"))
+			gateway = g_strdup(value);
+
+		if (!strcmp(key, "INTERNAL_IP4_ADDRESS"))
+			addressv4 = g_strdup(value);
+
+		if (!strcmp(key, "INTERNAL_IP6_ADDRESS")) {
+			addressv6 = g_strdup(value);
+			prefix_len = 128;
+		}
+
+		if (!strcmp(key, "INTERNAL_IP4_NETMASK"))
+			netmask = g_strdup(value);
+
+		if (!strcmp(key, "INTERNAL_IP6_NETMASK")) {
+			char *sep;
+
+			/* The netmask contains the address and the prefix */
+			sep = strchr(value, '/');
+			if (sep != NULL) {
+				unsigned char ip_len = sep - value;
+
+				addressv6 = g_strndup(value, ip_len);
+				prefix_len = (unsigned char)
+						strtol(sep + 1, NULL, 10);
+			}
+		}
+
+		if (!strcmp(key, "INTERNAL_IP4_DNS") ||
+				!strcmp(key, "INTERNAL_IP6_DNS"))
+			vpn_provider_set_nameservers(provider, value);
+
+		if (!strcmp(key, "CISCO_PROXY_PAC"))
+			vpn_provider_set_pac(provider, value);
+
+		if (domain == NULL && !strcmp(key, "CISCO_DEF_DOMAIN"))
+			domain = value;
+
+		if (g_str_has_prefix(key, "CISCO_SPLIT_INC") == TRUE ||
+			g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC") == TRUE)
+			vpn_provider_append_route(provider, key, value);
+
+		dbus_message_iter_next(&dict);
+	}
+
+	DBG("%p %p", addressv4, addressv6);
+
+	if (addressv4 != NULL)
+		ipaddress = connman_ipaddress_alloc(AF_INET);
+	else if (addressv6 != NULL)
+		ipaddress = connman_ipaddress_alloc(AF_INET6);
+	else
+		ipaddress = NULL;
+
+	if (ipaddress == NULL) {
+		g_free(addressv4);
+		g_free(addressv6);
+		g_free(netmask);
+		g_free(gateway);
+
+		return VPN_STATE_FAILURE;
+	}
+
+	if (addressv4 != NULL)
+		connman_ipaddress_set_ipv4(ipaddress, addressv4,
+						netmask, gateway);
+	else
+		connman_ipaddress_set_ipv6(ipaddress, addressv6,
+						prefix_len, gateway);
+	vpn_provider_set_ipaddress(provider, ipaddress);
+	vpn_provider_set_domain(provider, domain);
+
+	g_free(addressv4);
+	g_free(addressv6);
+	g_free(netmask);
+	g_free(gateway);
+	connman_ipaddress_free(ipaddress);
+
+	return VPN_STATE_CONNECT;
+}
+
+static int oc_connect(struct vpn_provider *provider,
+			struct connman_task *task, const char *if_name)
+{
+	const char *vpnhost, *vpncookie, *cafile, *certsha1, *mtu;
+	int fd, err;
+
+	vpnhost = vpn_provider_get_string(provider, "Host");
+	if (!vpnhost) {
+		connman_error("Host not set; cannot enable VPN");
+		return -EINVAL;
+	}
+
+	vpncookie = vpn_provider_get_string(provider, "OpenConnect.Cookie");
+	if (!vpncookie) {
+		connman_error("OpenConnect.Cookie not set; cannot enable VPN");
+		return -EINVAL;
+	}
+
+	certsha1 = vpn_provider_get_string(provider,
+						"OpenConnect.ServerCert");
+	if (certsha1)
+		connman_task_add_argument(task, "--servercert",
+							(char *)certsha1);
+
+	cafile = vpn_provider_get_string(provider, "OpenConnect.CACert");
+	mtu = vpn_provider_get_string(provider, "VPN.MTU");
+
+	if (cafile)
+		connman_task_add_argument(task, "--cafile",
+							(char *)cafile);
+	if (mtu)
+		connman_task_add_argument(task, "--mtu", (char *)mtu);
+
+	connman_task_add_argument(task, "--syslog", NULL);
+	connman_task_add_argument(task, "--cookie-on-stdin", NULL);
+
+	connman_task_add_argument(task, "--script",
+				  SCRIPTDIR "/openconnect-script");
+
+	connman_task_add_argument(task, "--interface", if_name);
+
+	connman_task_add_argument(task, (char *)vpnhost, NULL);
+
+	err = connman_task_run(task, vpn_died, provider,
+			       &fd, NULL, NULL);
+	if (err < 0) {
+		connman_error("openconnect failed to start");
+		return -EIO;
+	}
+
+	if (write(fd, vpncookie, strlen(vpncookie)) !=
+			(ssize_t)strlen(vpncookie) ||
+			write(fd, "\n", 1) != 1) {
+		connman_error("openconnect failed to take cookie on stdin");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int oc_save(struct vpn_provider *provider, GKeyFile *keyfile)
+{
+	const char *setting;
+
+	setting = vpn_provider_get_string(provider,
+					"OpenConnect.ServerCert");
+	if (setting != NULL)
+		g_key_file_set_string(keyfile,
+				vpn_provider_get_save_group(provider),
+				"OpenConnect.ServerCert", setting);
+
+	setting = vpn_provider_get_string(provider,
+					"OpenConnect.CACert");
+	if (setting != NULL)
+		g_key_file_set_string(keyfile,
+				vpn_provider_get_save_group(provider),
+				"OpenConnect.CACert", setting);
+
+	setting = vpn_provider_get_string(provider,
+					"VPN.MTU");
+	if (setting != NULL)
+		g_key_file_set_string(keyfile,
+				vpn_provider_get_save_group(provider),
+				"VPN.MTU", setting);
+
+	return 0;
+}
+
+static int oc_error_code(int exit_code)
+{
+
+	switch (exit_code) {
+	case 1:
+		return VPN_PROVIDER_ERROR_CONNECT_FAILED;
+	case 2:
+		return VPN_PROVIDER_ERROR_LOGIN_FAILED;
+	default:
+		return VPN_PROVIDER_ERROR_UNKNOWN;
+	}
+}
+
+static struct vpn_driver vpn_driver = {
+	.notify         = oc_notify,
+	.connect	= oc_connect,
+	.error_code	= oc_error_code,
+	.save		= oc_save,
+};
+
+static int openconnect_init(void)
+{
+	return vpn_register("openconnect", &vpn_driver, OPENCONNECT);
+}
+
+static void openconnect_exit(void)
+{
+	vpn_unregister("openconnect");
+}
+
+CONNMAN_PLUGIN_DEFINE(openconnect, "OpenConnect VPN plugin", VERSION,
+	CONNMAN_PLUGIN_PRIORITY_DEFAULT, openconnect_init, openconnect_exit)
diff --git a/vpn/plugins/openvpn.c b/vpn/plugins/openvpn.c
new file mode 100644
index 0000000..dd65415
--- /dev/null
+++ b/vpn/plugins/openvpn.c
@@ -0,0 +1,328 @@
+/*
+ *
+ *  ConnMan VPN daemon
+ *
+ *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <net/if.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/log.h>
+#include <connman/task.h>
+#include <connman/dbus.h>
+#include <connman/ipconfig.h>
+
+#include "../vpn-provider.h"
+
+#include "vpn.h"
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+static DBusConnection *connection;
+
+struct {
+	const char *cm_opt;
+	const char *ov_opt;
+	char       has_value;
+} ov_options[] = {
+	{ "Host", "--remote", 1 },
+	{ "OpenVPN.CACert", "--ca", 1 },
+	{ "OpenVPN.Cert", "--cert", 1 },
+	{ "OpenVPN.Key", "--key", 1 },
+	{ "OpenVPN.MTU", "--mtu", 1 },
+	{ "OpenVPN.NSCertType", "--ns-cert-type", 1 },
+	{ "OpenVPN.Proto", "--proto", 1 },
+	{ "OpenVPN.Port", "--port", 1 },
+	{ "OpenVPN.AuthUserPass", "--auth-user-pass", 1 },
+	{ "OpenVPN.AskPass", "--askpass", 1 },
+	{ "OpenVPN.AuthNoCache", "--auth-nocache", 0 },
+	{ "OpenVPN.TLSRemote", "--tls-remote", 1 },
+	{ "OpenVPN.TLSAuth", NULL, 1 },
+	{ "OpenVPN.TLSAuthDir", NULL, 1 },
+	{ "OpenVPN.Cipher", "--cipher", 1 },
+	{ "OpenVPN.Auth", "--auth", 1 },
+	{ "OpenVPN.CompLZO", "--comp-lzo", 0 },
+	{ "OpenVPN.RemoteCertTls", "--remote-cert-tls", 1 },
+};
+
+static void ov_append_dns_entries(const char *key, const char *value,
+					char **dns_entries)
+{
+	gchar **options;
+
+	if (g_str_has_prefix(key, "foreign_option_") == FALSE)
+		return;
+
+	options = g_strsplit(value, " ", 3);
+	if (options[0] != NULL &&
+		!strcmp(options[0], "dhcp-option") &&
+			options[1] != NULL &&
+			!strcmp(options[1], "DNS") &&
+				options[2] != NULL) {
+
+		if (*dns_entries != NULL) {
+			char *tmp;
+
+			tmp = g_strjoin(" ", *dns_entries,
+						options[2], NULL);
+			g_free(*dns_entries);
+			*dns_entries = tmp;
+		} else {
+			*dns_entries = g_strdup(options[2]);
+		}
+	}
+
+	g_strfreev(options);
+}
+
+static int ov_notify(DBusMessage *msg, struct vpn_provider *provider)
+{
+	DBusMessageIter iter, dict;
+	const char *reason, *key, *value;
+	char *nameservers = NULL;
+	char *address = NULL, *gateway = NULL, *peer = NULL;
+	struct connman_ipaddress *ipaddress;
+
+	dbus_message_iter_init(msg, &iter);
+
+	dbus_message_iter_get_basic(&iter, &reason);
+	dbus_message_iter_next(&iter);
+
+	if (!provider) {
+		connman_error("No provider found");
+		return VPN_STATE_FAILURE;
+	}
+
+	if (strcmp(reason, "up"))
+		return VPN_STATE_DISCONNECT;
+
+	dbus_message_iter_recurse(&iter, &dict);
+
+	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter entry;
+
+		dbus_message_iter_recurse(&dict, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_get_basic(&entry, &value);
+
+		DBG("%s = %s", key, value);
+
+		if (!strcmp(key, "trusted_ip")) {
+			vpn_provider_set_string(provider, "Gateway", value);
+			gateway = g_strdup(value);
+		}
+
+		if (!strcmp(key, "ifconfig_local")) {
+			vpn_provider_set_string(provider, "Address", value);
+			address = g_strdup(value);
+		}
+
+		if (!strcmp(key, "ifconfig_remote")) {
+			vpn_provider_set_string(provider, "Peer", value);
+			peer = g_strdup(value);
+		}
+
+		if (g_str_has_prefix(key, "route_") == TRUE)
+			vpn_provider_append_route(provider, key, value);
+
+		ov_append_dns_entries(key, value, &nameservers);
+
+		dbus_message_iter_next(&dict);
+	}
+
+	ipaddress = connman_ipaddress_alloc(AF_INET);
+	if (ipaddress == NULL) {
+		g_free(nameservers);
+		g_free(address);
+		g_free(gateway);
+		g_free(peer);
+
+		return VPN_STATE_FAILURE;
+	}
+
+	connman_ipaddress_set_ipv4(ipaddress, address, NULL, gateway);
+	connman_ipaddress_set_peer(ipaddress, peer);
+	vpn_provider_set_ipaddress(provider, ipaddress);
+
+	vpn_provider_set_nameservers(provider, nameservers);
+
+	g_free(nameservers);
+	g_free(address);
+	g_free(gateway);
+	g_free(peer);
+	connman_ipaddress_free(ipaddress);
+
+	return VPN_STATE_CONNECT;
+}
+
+static int ov_save(struct vpn_provider *provider, GKeyFile *keyfile)
+{
+	const char *option;
+	int i;
+
+	for (i = 0; i < (int)ARRAY_SIZE(ov_options); i++) {
+		if (strncmp(ov_options[i].cm_opt, "OpenVPN.", 8) == 0) {
+			option = vpn_provider_get_string(provider,
+							ov_options[i].cm_opt);
+			if (option == NULL)
+				continue;
+
+			g_key_file_set_string(keyfile,
+					vpn_provider_get_save_group(provider),
+					ov_options[i].cm_opt, option);
+		}
+	}
+	return 0;
+}
+
+static int task_append_config_data(struct vpn_provider *provider,
+					struct connman_task *task)
+{
+	const char *option;
+	int i;
+
+	for (i = 0; i < (int)ARRAY_SIZE(ov_options); i++) {
+		if (ov_options[i].ov_opt == NULL)
+			continue;
+
+		option = vpn_provider_get_string(provider,
+					ov_options[i].cm_opt);
+		if (option == NULL)
+			continue;
+
+		if (connman_task_add_argument(task,
+				ov_options[i].ov_opt,
+				ov_options[i].has_value ? option : NULL) < 0) {
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+static int ov_connect(struct vpn_provider *provider,
+		struct connman_task *task, const char *if_name)
+{
+	const char *option;
+	int err, fd;
+
+	option = vpn_provider_get_string(provider, "Host");
+	if (option == NULL) {
+		connman_error("Host not set; cannot enable VPN");
+		return -EINVAL;
+	}
+
+	task_append_config_data(provider, task);
+
+	option = vpn_provider_get_string(provider, "OpenVPN.TLSAuth");
+	if (option != NULL) {
+		connman_task_add_argument(task, "--tls-auth", option);
+		option = vpn_provider_get_string(provider,
+				"OpenVPN.TLSAuthDir");
+		if (option != NULL)
+			connman_task_add_argument(task, option, NULL);
+	}
+
+	connman_task_add_argument(task, "--syslog", NULL);
+
+	connman_task_add_argument(task, "--script-security", "2");
+
+	connman_task_add_argument(task, "--up",
+					SCRIPTDIR "/openvpn-script");
+	connman_task_add_argument(task, "--up-restart", NULL);
+
+	connman_task_add_argument(task, "--setenv", NULL);
+	connman_task_add_argument(task, "CONNMAN_BUSNAME",
+					dbus_bus_get_unique_name(connection));
+
+	connman_task_add_argument(task, "--setenv", NULL);
+	connman_task_add_argument(task, "CONNMAN_INTERFACE",
+					CONNMAN_TASK_INTERFACE);
+
+	connman_task_add_argument(task, "--setenv", NULL);
+	connman_task_add_argument(task, "CONNMAN_PATH",
+					connman_task_get_path(task));
+
+	connman_task_add_argument(task, "--dev", if_name);
+	connman_task_add_argument(task, "--dev-type", "tun");
+
+	connman_task_add_argument(task, "--tls-client", NULL);
+	connman_task_add_argument(task, "--nobind", NULL);
+	connman_task_add_argument(task, "--persist-key", NULL);
+	connman_task_add_argument(task, "--persist-tun", NULL);
+
+	connman_task_add_argument(task, "--route-noexec", NULL);
+	connman_task_add_argument(task, "--ifconfig-noexec", NULL);
+
+	/*
+	 * Disable client restarts because we can't handle this at the
+	 * moment. The problem is that when OpenVPN decides to switch
+	 * from CONNECTED state to RECONNECTING and then to RESOLVE,
+	 * it is not possible to do a DNS lookup. The DNS server is
+	 * not accessable through the tunnel anymore and so we end up
+	 * trying to resolve the OpenVPN servers address.
+	 */
+	connman_task_add_argument(task, "--ping-restart", "0");
+
+	connman_task_add_argument(task, "--client", NULL);
+
+	fd = fileno(stderr);
+	err = connman_task_run(task, vpn_died, provider,
+			NULL, &fd, &fd);
+	if (err < 0) {
+		connman_error("openvpn failed to start");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static struct vpn_driver vpn_driver = {
+	.notify	= ov_notify,
+	.connect	= ov_connect,
+	.save		= ov_save,
+};
+
+static int openvpn_init(void)
+{
+	connection = connman_dbus_get_connection();
+
+	return vpn_register("openvpn", &vpn_driver, OPENVPN);
+}
+
+static void openvpn_exit(void)
+{
+	vpn_unregister("openvpn");
+
+	dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(openvpn, "OpenVPN plugin", VERSION,
+	CONNMAN_PLUGIN_PRIORITY_DEFAULT, openvpn_init, openvpn_exit)
diff --git a/vpn/plugins/pptp.c b/vpn/plugins/pptp.c
new file mode 100644
index 0000000..f737c31
--- /dev/null
+++ b/vpn/plugins/pptp.c
@@ -0,0 +1,339 @@
+/*
+ *
+ *  ConnMan VPN daemon
+ *
+ *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <net/if.h>
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/provider.h>
+#include <connman/log.h>
+#include <connman/task.h>
+#include <connman/dbus.h>
+#include <connman/inet.h>
+
+#include "vpn.h"
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+enum {
+	OPT_STRING = 1,
+	OPT_BOOL = 2,
+};
+
+struct {
+	const char *cm_opt;
+	const char *pptp_opt;
+	const char *vpnc_default;
+	int type;
+} pptp_options[] = {
+	{ "PPTP.User", "user", NULL, OPT_STRING },
+	{ "PPTP.EchoFailure", "lcp-echo-failure", "0", OPT_STRING },
+	{ "PPTP.EchoInterval", "lcp-echo-interval", "0", OPT_STRING },
+	{ "PPTP.Debug", "debug", NULL, OPT_STRING },
+	{ "PPTP.RefuseEAP", "refuse-eap", NULL, OPT_BOOL },
+	{ "PPTP.RefusePAP", "refuse-pap", NULL, OPT_BOOL },
+	{ "PPTP.RefuseCHAP", "refuse-chap", NULL, OPT_BOOL },
+	{ "PPTP.RefuseMSCHAP", "refuse-mschap", NULL, OPT_BOOL },
+	{ "PPTP.RefuseMSCHAP2", "refuse-mschapv2", NULL, OPT_BOOL },
+	{ "PPTP.NoBSDComp", "nobsdcomp", NULL, OPT_BOOL },
+	{ "PPTP.NoDeflate", "nodeflate", NULL, OPT_BOOL },
+	{ "PPTP.RequirMPPE", "require-mppe", NULL, OPT_BOOL },
+	{ "PPTP.RequirMPPE40", "require-mppe-40", NULL, OPT_BOOL },
+	{ "PPTP.RequirMPPE128", "require-mppe-128", NULL, OPT_BOOL },
+	{ "PPTP.RequirMPPEStateful", "mppe-stateful", NULL, OPT_BOOL },
+	{ "PPTP.NoVJ", "no-vj-comp", NULL, OPT_BOOL },
+};
+
+static DBusConnection *connection;
+
+static DBusMessage *pptp_get_sec(struct connman_task *task,
+				DBusMessage *msg, void *user_data)
+{
+	const char *user, *passwd;
+	struct vpn_provider *provider = user_data;
+	DBusMessage *reply;
+
+	if (dbus_message_get_no_reply(msg) == TRUE)
+		return NULL;
+
+	user = vpn_provider_get_string(provider, "PPTP.User");
+	passwd = vpn_provider_get_string(provider, "PPTP.Password");
+	if (user == NULL || strlen(user) == 0 ||
+				passwd == NULL || strlen(passwd) == 0)
+		return NULL;
+
+	reply = dbus_message_new_method_return(msg);
+	if (reply == NULL)
+		return NULL;
+
+	dbus_message_append_args(reply, DBUS_TYPE_STRING, &user,
+				DBUS_TYPE_STRING, &passwd,
+				DBUS_TYPE_INVALID);
+	return reply;
+}
+
+static int pptp_notify(DBusMessage *msg, struct vpn_provider *provider)
+{
+	DBusMessageIter iter, dict;
+	const char *reason, *key, *value;
+	char *addressv4 = NULL, *netmask = NULL, *gateway = NULL;
+	char *ifname = NULL, *nameservers = NULL;
+	struct connman_ipaddress *ipaddress = NULL;
+
+	dbus_message_iter_init(msg, &iter);
+
+	dbus_message_iter_get_basic(&iter, &reason);
+	dbus_message_iter_next(&iter);
+
+	if (provider == NULL) {
+		connman_error("No provider found");
+		return VPN_STATE_FAILURE;
+	}
+
+	if (strcmp(reason, "auth failed") == 0)
+		return VPN_STATE_AUTH_FAILURE;
+
+	if (strcmp(reason, "connect"))
+		return VPN_STATE_DISCONNECT;
+
+	dbus_message_iter_recurse(&iter, &dict);
+
+	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter entry;
+
+		dbus_message_iter_recurse(&dict, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_get_basic(&entry, &value);
+
+		DBG("%s = %s", key, value);
+
+		if (!strcmp(key, "INTERNAL_IP4_ADDRESS")) {
+			vpn_provider_set_string(provider, "Address", value);
+			addressv4 = g_strdup(value);
+		}
+
+		if (!strcmp(key, "INTERNAL_IP4_NETMASK")) {
+			vpn_provider_set_string(provider, "Netmask", value);
+			netmask = g_strdup(value);
+		}
+
+		if (!strcmp(key, "INTERNAL_IP4_DNS")) {
+			vpn_provider_set_string(provider, "DNS", value);
+			nameservers = g_strdup(value);
+		}
+
+		if (!strcmp(key, "INTERNAL_IFNAME"))
+			ifname = g_strdup(value);
+
+		dbus_message_iter_next(&dict);
+	}
+
+	if (vpn_set_ifname(provider, ifname) < 0) {
+		g_free(ifname);
+		g_free(addressv4);
+		g_free(netmask);
+		g_free(nameservers);
+		return VPN_STATE_FAILURE;
+	}
+
+	if (addressv4 != NULL)
+		ipaddress = connman_ipaddress_alloc(AF_INET);
+
+	g_free(ifname);
+
+	if (ipaddress == NULL) {
+		connman_error("No IP address for provider");
+		g_free(addressv4);
+		g_free(netmask);
+		g_free(nameservers);
+		return VPN_STATE_FAILURE;
+	}
+
+	value = vpn_provider_get_string(provider, "HostIP");
+	if (value != NULL) {
+		vpn_provider_set_string(provider, "Gateway", value);
+		gateway = g_strdup(value);
+	}
+
+	if (addressv4 != NULL)
+		connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask,
+					gateway);
+
+	vpn_provider_set_ipaddress(provider, ipaddress);
+	vpn_provider_set_nameservers(provider, nameservers);
+
+	g_free(addressv4);
+	g_free(netmask);
+	g_free(gateway);
+	g_free(nameservers);
+	connman_ipaddress_free(ipaddress);
+
+	return VPN_STATE_CONNECT;
+}
+
+static int pptp_save(struct vpn_provider *provider, GKeyFile *keyfile)
+{
+	const char *option;
+	int i;
+
+	for (i = 0; i < (int)ARRAY_SIZE(pptp_options); i++) {
+		if (strncmp(pptp_options[i].cm_opt, "PPTP.", 5) == 0) {
+			option = vpn_provider_get_string(provider,
+							pptp_options[i].cm_opt);
+			if (option == NULL)
+				continue;
+
+			g_key_file_set_string(keyfile,
+					vpn_provider_get_save_group(provider),
+					pptp_options[i].cm_opt, option);
+		}
+	}
+	return 0;
+}
+
+static void pptp_write_bool_option(struct connman_task *task,
+				const char *key, const char *value)
+{
+	if (key != NULL && value != NULL) {
+		if (strcasecmp(value, "yes") == 0 ||
+				strcasecmp(value, "true") == 0 ||
+				strcmp(value, "1") == 0)
+			connman_task_add_argument(task, key, NULL);
+	}
+}
+
+static int pptp_connect(struct vpn_provider *provider,
+		struct connman_task *task, const char *if_name)
+{
+	const char *opt_s, *host;
+	char *str;
+	int err, i;
+
+	if (connman_task_set_notify(task, "getsec",
+					pptp_get_sec, provider))
+		return -ENOMEM;
+
+	host = vpn_provider_get_string(provider, "Host");
+	if (host == NULL) {
+		connman_error("Host not set; cannot enable VPN");
+		return -EINVAL;
+	}
+
+	str = g_strdup_printf("%s %s --nolaunchpppd --loglevel 2",
+				PPTP, host);
+	if (str == NULL) {
+		connman_error("can not allocate memory");
+		return -ENOMEM;
+	}
+
+	connman_task_add_argument(task, "pty", str);
+	g_free(str);
+
+	connman_task_add_argument(task, "nodetach", NULL);
+	connman_task_add_argument(task, "lock", NULL);
+	connman_task_add_argument(task, "usepeerdns", NULL);
+	connman_task_add_argument(task, "noipdefault", NULL);
+	connman_task_add_argument(task, "noauth", NULL);
+	connman_task_add_argument(task, "nodefaultroute", NULL);
+	connman_task_add_argument(task, "ipparam", "pptp_plugin");
+
+	for (i = 0; i < (int)ARRAY_SIZE(pptp_options); i++) {
+		opt_s = vpn_provider_get_string(provider,
+					pptp_options[i].cm_opt);
+		if (opt_s == NULL)
+			opt_s = pptp_options[i].vpnc_default;
+
+		if (opt_s == NULL)
+			continue;
+
+		if (pptp_options[i].type == OPT_STRING)
+			connman_task_add_argument(task,
+					pptp_options[i].pptp_opt, opt_s);
+		else if (pptp_options[i].type == OPT_BOOL)
+			pptp_write_bool_option(task,
+					pptp_options[i].pptp_opt, opt_s);
+	}
+
+	connman_task_add_argument(task, "plugin",
+				SCRIPTDIR "/libppp-plugin.so");
+
+	err = connman_task_run(task, vpn_died, provider,
+				NULL, NULL, NULL);
+	if (err < 0) {
+		connman_error("pptp failed to start");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int pptp_error_code(int exit_code)
+{
+
+	switch (exit_code) {
+	case 1:
+		return CONNMAN_PROVIDER_ERROR_CONNECT_FAILED;
+	case 2:
+		return CONNMAN_PROVIDER_ERROR_LOGIN_FAILED;
+	case 16:
+		return CONNMAN_PROVIDER_ERROR_AUTH_FAILED;
+	default:
+		return CONNMAN_PROVIDER_ERROR_UNKNOWN;
+	}
+}
+
+static struct vpn_driver vpn_driver = {
+	.flags		= VPN_FLAG_NO_TUN,
+	.notify		= pptp_notify,
+	.connect	= pptp_connect,
+	.error_code     = pptp_error_code,
+	.save		= pptp_save,
+};
+
+static int pptp_init(void)
+{
+	connection = connman_dbus_get_connection();
+
+	return vpn_register("pptp", &vpn_driver, PPPD);
+}
+
+static void pptp_exit(void)
+{
+	vpn_unregister("pptp");
+
+	dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(pptp, "pptp plugin", VERSION,
+	CONNMAN_PLUGIN_PRIORITY_DEFAULT, pptp_init, pptp_exit)
diff --git a/vpn/plugins/vpn.c b/vpn/plugins/vpn.c
new file mode 100644
index 0000000..2a0fdaf
--- /dev/null
+++ b/vpn/plugins/vpn.c
@@ -0,0 +1,535 @@
+/*
+ *
+ *  ConnMan VPN daemon
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <linux/if_tun.h>
+#include <net/if.h>
+
+#include <dbus/dbus.h>
+
+#include <glib/gprintf.h>
+
+#include <connman/log.h>
+#include <connman/rtnl.h>
+#include <connman/task.h>
+#include <connman/inet.h>
+
+#include "../vpn-rtnl.h"
+#include "../vpn-provider.h"
+
+#include "vpn.h"
+
+struct vpn_data {
+	struct vpn_provider *provider;
+	char *if_name;
+	unsigned flags;
+	unsigned int watch;
+	unsigned int state;
+	struct connman_task *task;
+};
+
+struct vpn_driver_data {
+	const char *name;
+	const char *program;
+	struct vpn_driver *vpn_driver;
+	struct vpn_provider_driver provider_driver;
+};
+
+GHashTable *driver_hash = NULL;
+
+static int stop_vpn(struct vpn_provider *provider)
+{
+	struct vpn_data *data = vpn_provider_get_data(provider);
+	struct vpn_driver_data *vpn_driver_data;
+	const char *name;
+	struct ifreq ifr;
+	int fd, err;
+
+	if (data == NULL)
+		return -EINVAL;
+
+	name = vpn_provider_get_driver_name(provider);
+	if (name == NULL)
+		return -EINVAL;
+
+	vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+
+	if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL &&
+			vpn_driver_data->vpn_driver->flags == VPN_FLAG_NO_TUN)
+		return 0;
+
+	memset(&ifr, 0, sizeof(ifr));
+	ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+	sprintf(ifr.ifr_name, "%s", data->if_name);
+
+	fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
+	if (fd < 0) {
+		err = -errno;
+		connman_error("Failed to open /dev/net/tun to device %s: %s",
+			      data->if_name, strerror(errno));
+		return err;
+	}
+
+	if (ioctl(fd, TUNSETIFF, (void *)&ifr)) {
+		err = -errno;
+		connman_error("Failed to TUNSETIFF for device %s to it: %s",
+			      data->if_name, strerror(errno));
+		close(fd);
+		return err;
+	}
+
+	if (ioctl(fd, TUNSETPERSIST, 0)) {
+		err = -errno;
+		connman_error("Failed to set tun device %s nonpersistent: %s",
+			      data->if_name, strerror(errno));
+		close(fd);
+		return err;
+	}
+	close(fd);
+	DBG("Killed tun device %s", data->if_name);
+	return 0;
+}
+
+void vpn_died(struct connman_task *task, int exit_code, void *user_data)
+{
+	struct vpn_provider *provider = user_data;
+	struct vpn_data *data = vpn_provider_get_data(provider);
+	int state = VPN_STATE_FAILURE;
+	enum vpn_provider_error ret;
+
+	DBG("provider %p data %p", provider, data);
+
+	if (data == NULL)
+		goto vpn_exit;
+
+	state = data->state;
+
+	stop_vpn(provider);
+	vpn_provider_set_data(provider, NULL);
+
+	if (data->watch != 0) {
+		vpn_provider_unref(provider);
+		vpn_rtnl_remove_watch(data->watch);
+		data->watch = 0;
+	}
+
+vpn_exit:
+	if (state != VPN_STATE_READY && state != VPN_STATE_DISCONNECT) {
+		const char *name;
+		struct vpn_driver_data *vpn_data = NULL;
+
+		name = vpn_provider_get_driver_name(provider);
+		if (name != NULL)
+			vpn_data = g_hash_table_lookup(driver_hash, name);
+
+		if (vpn_data != NULL &&
+				vpn_data->vpn_driver->error_code != NULL)
+			ret = vpn_data->vpn_driver->error_code(exit_code);
+		else
+			ret = VPN_PROVIDER_ERROR_UNKNOWN;
+
+		vpn_provider_indicate_error(provider, ret);
+	} else
+		vpn_provider_set_state(provider, VPN_PROVIDER_STATE_IDLE);
+
+	vpn_provider_set_index(provider, -1);
+
+	if (data != NULL) {
+		vpn_provider_unref(data->provider);
+		g_free(data->if_name);
+		g_free(data);
+	}
+
+	connman_task_destroy(task);
+}
+
+int vpn_set_ifname(struct vpn_provider *provider, const char *ifname)
+{
+	struct vpn_data *data = vpn_provider_get_data(provider);
+	int index;
+
+	if (ifname == NULL || data == NULL)
+		return  -EIO;
+
+	index = connman_inet_ifindex(ifname);
+	if (index < 0)
+		return  -EIO;
+
+	if (data->if_name != NULL)
+		g_free(data->if_name);
+
+	data->if_name = (char *)g_strdup(ifname);
+	vpn_provider_set_index(provider, index);
+
+	return 0;
+}
+
+static void vpn_newlink(unsigned flags, unsigned change, void *user_data)
+{
+	struct vpn_provider *provider = user_data;
+	struct vpn_data *data = vpn_provider_get_data(provider);
+
+	if ((data->flags & IFF_UP) != (flags & IFF_UP)) {
+		if (flags & IFF_UP) {
+			data->state = VPN_STATE_READY;
+			vpn_provider_set_state(provider,
+					VPN_PROVIDER_STATE_READY);
+		}
+	}
+	data->flags = flags;
+}
+
+static DBusMessage *vpn_notify(struct connman_task *task,
+			DBusMessage *msg, void *user_data)
+{
+	struct vpn_provider *provider = user_data;
+	struct vpn_data *data;
+	struct vpn_driver_data *vpn_driver_data;
+	const char *name;
+	int state, index;
+
+	data = vpn_provider_get_data(provider);
+
+	name = vpn_provider_get_driver_name(provider);
+	if (name == NULL)
+		return NULL;
+
+	vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+	if (vpn_driver_data == NULL)
+		return NULL;
+
+	state = vpn_driver_data->vpn_driver->notify(msg, provider);
+	switch (state) {
+	case VPN_STATE_CONNECT:
+	case VPN_STATE_READY:
+		index = vpn_provider_get_index(provider);
+		vpn_provider_ref(provider);
+		data->watch = vpn_rtnl_add_newlink_watch(index,
+						     vpn_newlink, provider);
+		connman_inet_ifup(index);
+		break;
+
+	case VPN_STATE_UNKNOWN:
+	case VPN_STATE_IDLE:
+	case VPN_STATE_DISCONNECT:
+	case VPN_STATE_FAILURE:
+		vpn_provider_set_state(provider,
+					VPN_PROVIDER_STATE_DISCONNECT);
+		break;
+
+	case VPN_STATE_AUTH_FAILURE:
+		vpn_provider_indicate_error(provider,
+					VPN_PROVIDER_ERROR_AUTH_FAILED);
+		break;
+	}
+
+	return NULL;
+}
+
+static int vpn_create_tun(struct vpn_provider *provider)
+{
+	struct vpn_data *data = vpn_provider_get_data(provider);
+	struct ifreq ifr;
+	int i, fd, index;
+	int ret = 0;
+
+	if (data == NULL)
+		return -EISCONN;
+
+	fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
+	if (fd < 0) {
+		i = -errno;
+		connman_error("Failed to open /dev/net/tun: %s",
+			      strerror(errno));
+		ret = i;
+		goto exist_err;
+	}
+
+	memset(&ifr, 0, sizeof(ifr));
+	ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+
+	for (i = 0; i < 256; i++) {
+		sprintf(ifr.ifr_name, "vpn%d", i);
+
+		if (!ioctl(fd, TUNSETIFF, (void *)&ifr))
+			break;
+	}
+
+	if (i == 256) {
+		connman_error("Failed to find available tun device");
+		close(fd);
+		ret = -ENODEV;
+		goto exist_err;
+	}
+
+	data->if_name = (char *)g_strdup(ifr.ifr_name);
+	if (data->if_name == NULL) {
+		connman_error("Failed to allocate memory");
+		close(fd);
+		ret = -ENOMEM;
+		goto exist_err;
+	}
+
+	if (ioctl(fd, TUNSETPERSIST, 1)) {
+		i = -errno;
+		connman_error("Failed to set tun persistent: %s",
+			      strerror(errno));
+		close(fd);
+		ret = i;
+		goto exist_err;
+	}
+
+	close(fd);
+
+	index = connman_inet_ifindex(data->if_name);
+	if (index < 0) {
+		connman_error("Failed to get tun ifindex");
+		stop_vpn(provider);
+		ret = -EIO;
+		goto exist_err;
+	}
+	vpn_provider_set_index(provider, index);
+
+	return 0;
+
+exist_err:
+	return ret;
+}
+
+static int vpn_connect(struct vpn_provider *provider)
+{
+	struct vpn_data *data = vpn_provider_get_data(provider);
+	struct vpn_driver_data *vpn_driver_data;
+	const char *name;
+	int ret = 0;
+
+	if (data != NULL)
+		return -EISCONN;
+
+	data = g_try_new0(struct vpn_data, 1);
+	if (data == NULL)
+		return -ENOMEM;
+
+	data->provider = vpn_provider_ref(provider);
+	data->watch = 0;
+	data->flags = 0;
+	data->task = NULL;
+	data->state = VPN_STATE_IDLE;
+
+	vpn_provider_set_data(provider, data);
+
+	name = vpn_provider_get_driver_name(provider);
+	if (name == NULL)
+		return -EINVAL;
+
+	vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+
+	if (vpn_driver_data == NULL || vpn_driver_data->vpn_driver == NULL) {
+		ret = -EINVAL;
+		goto exist_err;
+	}
+
+	if (vpn_driver_data->vpn_driver->flags != VPN_FLAG_NO_TUN) {
+		ret = vpn_create_tun(provider);
+		if (ret < 0)
+			goto exist_err;
+	}
+
+	data->task = connman_task_create(vpn_driver_data->program);
+
+	if (data->task == NULL) {
+		ret = -ENOMEM;
+		stop_vpn(provider);
+		goto exist_err;
+	}
+
+	if (connman_task_set_notify(data->task, "notify",
+					vpn_notify, provider)) {
+		ret = -ENOMEM;
+		stop_vpn(provider);
+		connman_task_destroy(data->task);
+		data->task = NULL;
+		goto exist_err;
+	}
+
+	ret = vpn_driver_data->vpn_driver->connect(provider, data->task,
+							data->if_name);
+	if (ret < 0) {
+		stop_vpn(provider);
+		connman_task_destroy(data->task);
+		data->task = NULL;
+		goto exist_err;
+	}
+
+	DBG("%s started with dev %s",
+		vpn_driver_data->provider_driver.name, data->if_name);
+
+	data->state = VPN_STATE_CONNECT;
+
+	return -EINPROGRESS;
+
+exist_err:
+	vpn_provider_set_index(provider, -1);
+	vpn_provider_set_data(provider, NULL);
+	vpn_provider_unref(data->provider);
+	g_free(data->if_name);
+	g_free(data);
+
+	return ret;
+}
+
+static int vpn_probe(struct vpn_provider *provider)
+{
+	return 0;
+}
+
+static int vpn_disconnect(struct vpn_provider *provider)
+{
+	struct vpn_data *data = vpn_provider_get_data(provider);
+	struct vpn_driver_data *vpn_driver_data;
+	const char *name;
+
+	DBG("disconnect provider %p:", provider);
+
+	if (data == NULL)
+		return 0;
+
+	name = vpn_provider_get_driver_name(provider);
+	if (name == NULL)
+		return 0;
+
+	vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+	if (vpn_driver_data->vpn_driver->disconnect)
+		vpn_driver_data->vpn_driver->disconnect();
+
+	if (data->watch != 0) {
+		vpn_provider_unref(provider);
+		vpn_rtnl_remove_watch(data->watch);
+		data->watch = 0;
+	}
+
+	data->state = VPN_STATE_DISCONNECT;
+	connman_task_stop(data->task);
+
+	return 0;
+}
+
+static int vpn_remove(struct vpn_provider *provider)
+{
+	struct vpn_data *data;
+
+	data = vpn_provider_get_data(provider);
+	if (data == NULL)
+		return 0;
+
+	if (data->watch != 0) {
+		vpn_provider_unref(provider);
+		vpn_rtnl_remove_watch(data->watch);
+		data->watch = 0;
+	}
+
+	connman_task_stop(data->task);
+
+	g_usleep(G_USEC_PER_SEC);
+	stop_vpn(provider);
+	return 0;
+}
+
+static int vpn_save(struct vpn_provider *provider, GKeyFile *keyfile)
+{
+	struct vpn_driver_data *vpn_driver_data;
+	const char *name;
+
+	name = vpn_provider_get_driver_name(provider);
+	vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+	if (vpn_driver_data != NULL &&
+			vpn_driver_data->vpn_driver->save != NULL)
+		return vpn_driver_data->vpn_driver->save(provider, keyfile);
+
+	return 0;
+}
+
+int vpn_register(const char *name, struct vpn_driver *vpn_driver,
+			const char *program)
+{
+	struct vpn_driver_data *data;
+
+	data = g_try_new0(struct vpn_driver_data, 1);
+	if (data == NULL)
+		return -ENOMEM;
+
+	data->name = name;
+	data->program = program;
+
+	data->vpn_driver = vpn_driver;
+
+	data->provider_driver.name = name;
+	data->provider_driver.disconnect = vpn_disconnect;
+	data->provider_driver.connect = vpn_connect;
+	data->provider_driver.probe = vpn_probe;
+	data->provider_driver.remove = vpn_remove;
+	data->provider_driver.save = vpn_save;
+
+	if (driver_hash == NULL)
+		driver_hash = g_hash_table_new_full(g_str_hash,
+							g_str_equal,
+							NULL, g_free);
+
+	if (driver_hash == NULL) {
+		connman_error("driver_hash not initialized for %s", name);
+		g_free(data);
+		return -ENOMEM;
+	}
+
+	g_hash_table_replace(driver_hash, (char *)name, data);
+
+	vpn_provider_driver_register(&data->provider_driver);
+
+	return 0;
+}
+
+void vpn_unregister(const char *name)
+{
+	struct vpn_driver_data *data;
+
+	data = g_hash_table_lookup(driver_hash, name);
+	if (data == NULL)
+		return;
+
+	vpn_provider_driver_unregister(&data->provider_driver);
+
+	g_hash_table_remove(driver_hash, name);
+
+	if (g_hash_table_size(driver_hash) == 0)
+		g_hash_table_destroy(driver_hash);
+}
diff --git a/vpn/plugins/vpn.h b/vpn/plugins/vpn.h
new file mode 100644
index 0000000..6693cdb
--- /dev/null
+++ b/vpn/plugins/vpn.h
@@ -0,0 +1,63 @@
+/*
+ *
+ *  ConnMan VPN daemon
+ *
+ *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __CONNMAN_VPND_VPN_H
+#define __CONNMAN_VPND_VPN_H
+
+#include "../vpn-provider.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define VPN_FLAG_NO_TUN	1
+
+enum vpn_state {
+	VPN_STATE_UNKNOWN       = 0,
+	VPN_STATE_IDLE          = 1,
+	VPN_STATE_CONNECT       = 2,
+	VPN_STATE_READY         = 3,
+	VPN_STATE_DISCONNECT    = 4,
+	VPN_STATE_FAILURE       = 5,
+	VPN_STATE_AUTH_FAILURE  = 6,
+};
+
+struct vpn_driver {
+	int flags;
+	int (*notify) (DBusMessage *msg, struct vpn_provider *provider);
+	int (*connect) (struct vpn_provider *provider,
+			struct connman_task *task, const char *if_name);
+	void (*disconnect) (void);
+	int (*error_code) (int exit_code);
+	int (*save) (struct vpn_provider *provider, GKeyFile *keyfile);
+};
+
+int vpn_register(const char *name, struct vpn_driver *driver,
+			const char *program);
+void vpn_unregister(const char *provider_name);
+void vpn_died(struct connman_task *task, int exit_code, void *user_data);
+int vpn_set_ifname(struct vpn_provider *provider, const char *ifname);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_VPND_VPN_H */
diff --git a/vpn/plugins/vpnc.c b/vpn/plugins/vpnc.c
new file mode 100644
index 0000000..9fd1dec
--- /dev/null
+++ b/vpn/plugins/vpnc.c
@@ -0,0 +1,345 @@
+/*
+ *
+ *  ConnMan VPN daemon
+ *
+ *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
+ *  Copyright (C) 2010  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <net/if.h>
+
+#include <glib.h>
+
+#define CONNMAN_API_SUBJECT_TO_CHANGE
+#include <connman/plugin.h>
+#include <connman/log.h>
+#include <connman/task.h>
+#include <connman/ipconfig.h>
+#include <connman/dbus.h>
+
+#include "../vpn-provider.h"
+
+#include "vpn.h"
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+static DBusConnection *connection;
+
+enum {
+	OPT_STRING = 1,
+	OPT_BOOLEAN = 2,
+};
+
+struct {
+	const char *cm_opt;
+	const char *vpnc_opt;
+	const char *vpnc_default;
+	int type;
+	connman_bool_t cm_save;
+} vpnc_options[] = {
+	{ "Host", "IPSec gateway", NULL, OPT_STRING, TRUE },
+	{ "VPNC.IPSec.ID", "IPSec ID", NULL, OPT_STRING, TRUE },
+	{ "VPNC.IPSec.Secret", "IPSec secret", NULL, OPT_STRING, FALSE },
+	{ "VPNC.Xauth.Username", "Xauth username", NULL, OPT_STRING, FALSE },
+	{ "VPNC.Xauth.Password", "Xauth password", NULL, OPT_STRING, FALSE },
+	{ "VPNC.IKE.Authmode", "IKE Authmode", NULL, OPT_STRING, TRUE },
+	{ "VPNC.IKE.DHGroup", "IKE DH Group", NULL, OPT_STRING, TRUE },
+	{ "VPNC.PFS", "Perfect Forward Secrecy", NULL, OPT_STRING, TRUE },
+	{ "VPNC.Domain", "Domain", NULL, OPT_STRING, TRUE },
+	{ "VPNC.Vendor", "Vendor", NULL, OPT_STRING, TRUE },
+	{ "VPNC.LocalPort", "Local Port", "0", OPT_STRING, TRUE, },
+	{ "VPNC.CiscoPort", "Cisco UDP Encapsulation Port", "0", OPT_STRING,
+									TRUE },
+	{ "VPNC.AppVersion", "Application Version", NULL, OPT_STRING, TRUE },
+	{ "VPNC.NATTMode", "NAT Traversal Mode", "cisco-udp", OPT_STRING,
+									TRUE },
+	{ "VPNC.DPDTimeout", "DPD idle timeout (our side)", NULL, OPT_STRING,
+									TRUE },
+	{ "VPNC.SingleDES", "Enable Single DES", NULL, OPT_BOOLEAN, TRUE },
+	{ "VPNC.NoEncryption", "Enable no encryption", NULL, OPT_BOOLEAN,
+									TRUE },
+};
+
+static int vc_notify(DBusMessage *msg, struct vpn_provider *provider)
+{
+	DBusMessageIter iter, dict;
+	char *address = NULL, *netmask = NULL, *gateway = NULL;
+	struct connman_ipaddress *ipaddress;
+	const char *reason, *key, *value;
+
+	dbus_message_iter_init(msg, &iter);
+
+	dbus_message_iter_get_basic(&iter, &reason);
+	dbus_message_iter_next(&iter);
+
+	if (!provider) {
+		connman_error("No provider found");
+		return VPN_STATE_FAILURE;
+	}
+
+	if (strcmp(reason, "connect"))
+		return VPN_STATE_DISCONNECT;
+
+	dbus_message_iter_recurse(&iter, &dict);
+
+	while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter entry;
+
+		dbus_message_iter_recurse(&dict, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_get_basic(&entry, &value);
+
+		DBG("%s = %s", key, value);
+
+		if (!strcmp(key, "VPNGATEWAY"))
+			gateway = g_strdup(value);
+
+		if (!strcmp(key, "INTERNAL_IP4_ADDRESS"))
+			address = g_strdup(value);
+
+		if (!strcmp(key, "INTERNAL_IP4_NETMASK"))
+			netmask = g_strdup(value);
+
+		if (!strcmp(key, "INTERNAL_IP4_DNS"))
+			vpn_provider_set_nameservers(provider, value);
+
+		if (!strcmp(key, "CISCO_DEF_DOMAIN"))
+			vpn_provider_set_domain(provider, value);
+
+		if (g_str_has_prefix(key, "CISCO_SPLIT_INC") == TRUE ||
+			g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC") == TRUE)
+			vpn_provider_append_route(provider, key, value);
+
+		dbus_message_iter_next(&dict);
+	}
+
+
+	ipaddress = connman_ipaddress_alloc(AF_INET);
+	if (ipaddress == NULL) {
+		g_free(address);
+		g_free(netmask);
+		g_free(gateway);
+
+		return VPN_STATE_FAILURE;
+	}
+
+	connman_ipaddress_set_ipv4(ipaddress, address, netmask, gateway);
+	vpn_provider_set_ipaddress(provider, ipaddress);
+
+	g_free(address);
+	g_free(netmask);
+	g_free(gateway);
+	connman_ipaddress_free(ipaddress);
+
+	return VPN_STATE_CONNECT;
+}
+
+static ssize_t full_write(int fd, const void *buf, size_t len)
+{
+	ssize_t byte_write;
+
+	while (len) {
+		byte_write = write(fd, buf, len);
+		if (byte_write < 0) {
+			connman_error("failed to write config to vpnc: %s\n",
+					strerror(errno));
+			return byte_write;
+		}
+		len -= byte_write;
+		buf += byte_write;
+	}
+
+	return 0;
+}
+
+static ssize_t write_option(int fd, const char *key, const char *value)
+{
+	gchar *buf;
+	ssize_t ret = 0;
+
+	if (key != NULL && value != NULL) {
+		buf = g_strdup_printf("%s %s\n", key, value);
+		ret = full_write(fd, buf, strlen(buf));
+
+		g_free(buf);
+	}
+
+	return ret;
+}
+
+static ssize_t write_bool_option(int fd, const char *key, const char *value)
+{
+	gchar *buf;
+	ssize_t ret = 0;
+
+	if (key != NULL && value != NULL) {
+		if (strcasecmp(value, "yes") == 0 ||
+				strcasecmp(value, "true") == 0 ||
+				strcmp(value, "1") == 0) {
+			buf = g_strdup_printf("%s\n", key);
+			ret = full_write(fd, buf, strlen(buf));
+
+			g_free(buf);
+		}
+	}
+
+	return ret;
+}
+
+static int vc_write_config_data(struct vpn_provider *provider, int fd)
+{
+	const char *opt_s;
+	int i;
+
+	for (i = 0; i < (int)ARRAY_SIZE(vpnc_options); i++) {
+		opt_s = vpn_provider_get_string(provider,
+					vpnc_options[i].cm_opt);
+		if (opt_s == FALSE)
+			opt_s = vpnc_options[i].vpnc_default;
+
+		if (opt_s == FALSE)
+			continue;
+
+		if (vpnc_options[i].type == OPT_STRING) {
+			if (write_option(fd,
+					vpnc_options[i].vpnc_opt, opt_s) < 0)
+				return -EIO;
+		} else if (vpnc_options[i].type == OPT_BOOLEAN) {
+			if (write_bool_option(fd,
+					vpnc_options[i].vpnc_opt, opt_s) < 0)
+				return -EIO;
+		}
+
+	}
+
+	return 0;
+}
+
+static int vc_save(struct vpn_provider *provider, GKeyFile *keyfile)
+{
+	const char *option;
+	int i;
+
+	for (i = 0; i < (int)ARRAY_SIZE(vpnc_options); i++) {
+		if (strncmp(vpnc_options[i].cm_opt, "VPNC.", 5) == 0) {
+
+			if (vpnc_options[i].cm_save == FALSE)
+				continue;
+
+			option = vpn_provider_get_string(provider,
+							vpnc_options[i].cm_opt);
+			if (option == NULL)
+				continue;
+
+			g_key_file_set_string(keyfile,
+					vpn_provider_get_save_group(provider),
+					vpnc_options[i].cm_opt, option);
+		}
+	}
+	return 0;
+}
+
+static int vc_connect(struct vpn_provider *provider,
+		struct connman_task *task, const char *if_name)
+{
+	const char *option;
+	int err, fd;
+
+	option = vpn_provider_get_string(provider, "Host");
+	if (option == NULL) {
+		connman_error("Host not set; cannot enable VPN");
+		return -EINVAL;
+	}
+	option = vpn_provider_get_string(provider, "VPNC.IPSec.ID");
+	if (option == NULL) {
+		connman_error("Group not set; cannot enable VPN");
+		return -EINVAL;
+	}
+
+	connman_task_add_argument(task, "--non-inter", NULL);
+	connman_task_add_argument(task, "--no-detach", NULL);
+
+	connman_task_add_argument(task, "--ifname", if_name);
+	connman_task_add_argument(task, "--ifmode", "tun");
+
+	connman_task_add_argument(task, "--script",
+				SCRIPTDIR "/openconnect-script");
+
+	option = vpn_provider_get_string(provider, "VPNC.Debug");
+	if (option != NULL)
+		connman_task_add_argument(task, "--debug", option);
+
+	connman_task_add_argument(task, "-", NULL);
+
+	err = connman_task_run(task, vpn_died, provider,
+				&fd, NULL, NULL);
+	if (err < 0) {
+		connman_error("vpnc failed to start");
+		return -EIO;
+	}
+
+	err = vc_write_config_data(provider, fd);
+
+	close(fd);
+
+	return err;
+}
+
+static int vc_error_code(int exit_code)
+{
+	switch (exit_code) {
+	case 1:
+		return VPN_PROVIDER_ERROR_CONNECT_FAILED;
+	case 2:
+		return VPN_PROVIDER_ERROR_LOGIN_FAILED;
+	default:
+		return VPN_PROVIDER_ERROR_UNKNOWN;
+	}
+}
+
+static struct vpn_driver vpn_driver = {
+	.notify		= vc_notify,
+	.connect	= vc_connect,
+	.error_code	= vc_error_code,
+	.save		= vc_save,
+};
+
+static int vpnc_init(void)
+{
+	connection = connman_dbus_get_connection();
+
+	return vpn_register("vpnc", &vpn_driver, VPNC);
+}
+
+static void vpnc_exit(void)
+{
+	vpn_unregister("vpnc");
+
+	dbus_connection_unref(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE(vpnc, "vpnc plugin", VERSION,
+	CONNMAN_PLUGIN_PRIORITY_DEFAULT, vpnc_init, vpnc_exit)
diff --git a/vpn/vpn-dbus.conf b/vpn/vpn-dbus.conf
new file mode 100644
index 0000000..0f0c8da
--- /dev/null
+++ b/vpn/vpn-dbus.conf
@@ -0,0 +1,15 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy user="root">
+        <allow own="net.connman.vpn"/>
+        <allow send_destination="net.connman.vpn"/>
+        <allow send_interface="net.connman.vpn.Agent"/>
+    </policy>
+    <policy at_console="true">
+        <allow send_destination="net.connman.vpn"/>
+    </policy>
+    <policy context="default">
+        <deny send_destination="net.connman.vpn"/>
+    </policy>
+</busconfig>
diff --git a/vpn/vpn-ipconfig.c b/vpn/vpn-ipconfig.c
new file mode 100644
index 0000000..cb5167f
--- /dev/null
+++ b/vpn/vpn-ipconfig.c
@@ -0,0 +1,450 @@
+/*
+ *
+ *  ConnMan VPN daemon
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <linux/if_link.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP	0x10000
+#endif
+
+#include <gdbus.h>
+
+#include "../src/connman.h"
+
+#include "vpn.h"
+
+struct vpn_ipconfig {
+	int refcount;
+	int index;
+	int family;
+	connman_bool_t enabled;
+	struct connman_ipaddress *address;
+	struct connman_ipaddress *system;
+};
+
+struct vpn_ipdevice {
+	int index;
+	char *ifname;
+	unsigned short type;
+	unsigned int flags;
+	char *address;
+	uint16_t mtu;
+
+	GSList *address_list;
+	char *ipv4_gateway;
+	char *ipv6_gateway;
+
+	char *pac;
+};
+
+static GHashTable *ipdevice_hash = NULL;
+
+unsigned char __vpn_ipconfig_netmask_prefix_len(const char *netmask)
+{
+	unsigned char bits;
+	in_addr_t mask;
+	in_addr_t host;
+
+	if (netmask == NULL)
+		return 32;
+
+	mask = inet_network(netmask);
+	host = ~mask;
+
+	/* a valid netmask must be 2^n - 1 */
+	if ((host & (host + 1)) != 0)
+		return -1;
+
+	bits = 0;
+	for (; mask; mask <<= 1)
+		++bits;
+
+	return bits;
+}
+
+const char *__vpn_ipconfig_get_peer(struct vpn_ipconfig *ipconfig)
+{
+	if (ipconfig->address == NULL)
+		return NULL;
+
+	return ipconfig->address->peer;
+}
+
+unsigned short __vpn_ipconfig_get_type_from_index(int index)
+{
+	struct vpn_ipdevice *ipdevice;
+
+	ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
+	if (ipdevice == NULL)
+		return ARPHRD_VOID;
+
+	return ipdevice->type;
+}
+
+unsigned int __vpn_ipconfig_get_flags_from_index(int index)
+{
+	struct vpn_ipdevice *ipdevice;
+
+	ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
+	if (ipdevice == NULL)
+		return 0;
+
+	return ipdevice->flags;
+}
+
+void __vpn_ipconfig_foreach(void (*function) (int index,
+					void *user_data), void *user_data)
+{
+	GList *list, *keys;
+
+	keys = g_hash_table_get_keys(ipdevice_hash);
+	if (keys == NULL)
+		return;
+
+	for (list = g_list_first(keys); list; list = g_list_next(list)) {
+		int index = GPOINTER_TO_INT(list->data);
+
+		function(index, user_data);
+	}
+
+	g_list_free(keys);
+}
+
+void __vpn_ipconfig_set_local(struct vpn_ipconfig *ipconfig,
+							const char *address)
+{
+	if (ipconfig->address == NULL)
+		return;
+
+	g_free(ipconfig->address->local);
+	ipconfig->address->local = g_strdup(address);
+}
+
+const char *__vpn_ipconfig_get_local(struct vpn_ipconfig *ipconfig)
+{
+	if (ipconfig->address == NULL)
+		return NULL;
+
+	return ipconfig->address->local;
+}
+
+void __vpn_ipconfig_set_peer(struct vpn_ipconfig *ipconfig,
+							const char *address)
+{
+	if (ipconfig->address == NULL)
+		return;
+
+	g_free(ipconfig->address->peer);
+	ipconfig->address->peer = g_strdup(address);
+}
+
+void __vpn_ipconfig_set_broadcast(struct vpn_ipconfig *ipconfig,
+					const char *broadcast)
+{
+	if (ipconfig->address == NULL)
+		return;
+
+	g_free(ipconfig->address->broadcast);
+	ipconfig->address->broadcast = g_strdup(broadcast);
+}
+
+void __vpn_ipconfig_set_gateway(struct vpn_ipconfig *ipconfig,
+							const char *gateway)
+{
+	DBG("");
+
+	if (ipconfig->address == NULL)
+		return;
+	g_free(ipconfig->address->gateway);
+	ipconfig->address->gateway = g_strdup(gateway);
+}
+
+const char *
+__vpn_ipconfig_get_gateway(struct vpn_ipconfig *ipconfig)
+{
+	if (ipconfig->address == NULL)
+		return NULL;
+
+	return ipconfig->address->gateway;
+}
+
+void __vpn_ipconfig_set_prefixlen(struct vpn_ipconfig *ipconfig,
+					unsigned char prefixlen)
+{
+	if (ipconfig->address == NULL)
+		return;
+
+	ipconfig->address->prefixlen = prefixlen;
+}
+
+unsigned char
+__vpn_ipconfig_get_prefixlen(struct vpn_ipconfig *ipconfig)
+{
+	if (ipconfig->address == NULL)
+		return 0;
+
+	return ipconfig->address->prefixlen;
+}
+
+int __vpn_ipconfig_address_add(struct vpn_ipconfig *ipconfig, int family)
+{
+	DBG("ipconfig %p family %d", ipconfig, family);
+
+	if (ipconfig == NULL)
+		return -EINVAL;
+
+	if (family == AF_INET)
+		return connman_inet_set_address(ipconfig->index,
+						ipconfig->address);
+	else if (family == AF_INET6)
+		return connman_inet_set_ipv6_address(ipconfig->index,
+							ipconfig->address);
+
+	return 0;
+}
+
+int __vpn_ipconfig_gateway_add(struct vpn_ipconfig *ipconfig, int family)
+{
+	DBG("ipconfig %p family %d", ipconfig, family);
+
+	if (ipconfig == NULL || ipconfig->address == NULL)
+		return -EINVAL;
+
+	DBG("family %d gw %s peer %s", family,
+		ipconfig->address->gateway, ipconfig->address->peer);
+
+	if (family == AF_INET)
+		connman_inet_add_host_route(ipconfig->index,
+					ipconfig->address->gateway,
+					ipconfig->address->peer);
+	else if (family == AF_INET6)
+		connman_inet_add_ipv6_host_route(ipconfig->index,
+						ipconfig->address->gateway,
+						ipconfig->address->peer);
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static struct vpn_ipconfig *create_ipv6config(int index)
+{
+	struct vpn_ipconfig *ipv6config;
+
+	DBG("index %d", index);
+
+	ipv6config = g_try_new0(struct vpn_ipconfig, 1);
+	if (ipv6config == NULL)
+		return NULL;
+
+	ipv6config->refcount = 1;
+
+	ipv6config->index = index;
+	ipv6config->enabled = FALSE;
+	ipv6config->family = AF_INET6;
+
+	ipv6config->address = connman_ipaddress_alloc(AF_INET6);
+	if (ipv6config->address == NULL) {
+		g_free(ipv6config);
+		return NULL;
+	}
+
+	ipv6config->system = connman_ipaddress_alloc(AF_INET6);
+
+	DBG("ipconfig %p", ipv6config);
+
+	return ipv6config;
+}
+
+struct vpn_ipconfig *__vpn_ipconfig_create(int index, int family)
+{
+	struct vpn_ipconfig *ipconfig;
+
+	if (family == AF_INET6)
+		return create_ipv6config(index);
+
+	DBG("index %d", index);
+
+	ipconfig = g_try_new0(struct vpn_ipconfig, 1);
+	if (ipconfig == NULL)
+		return NULL;
+
+	ipconfig->refcount = 1;
+
+	ipconfig->index = index;
+	ipconfig->enabled = FALSE;
+	ipconfig->family = family;
+
+	ipconfig->address = connman_ipaddress_alloc(AF_INET);
+	if (ipconfig->address == NULL) {
+		g_free(ipconfig);
+		return NULL;
+	}
+
+	ipconfig->system = connman_ipaddress_alloc(AF_INET);
+
+	DBG("ipconfig %p", ipconfig);
+
+	return ipconfig;
+}
+
+void __vpn_ipconfig_set_index(struct vpn_ipconfig *ipconfig, int index)
+{
+	ipconfig->index = index;
+}
+
+static const char *type2str(unsigned short type)
+{
+	switch (type) {
+	case ARPHRD_ETHER:
+		return "ETHER";
+	case ARPHRD_LOOPBACK:
+		return "LOOPBACK";
+	case ARPHRD_PPP:
+		return "PPP";
+	case ARPHRD_NONE:
+		return "NONE";
+	case ARPHRD_VOID:
+		return "VOID";
+	}
+
+	return "";
+}
+
+void __vpn_ipconfig_newlink(int index, unsigned short type,
+				unsigned int flags,
+				const char *address,
+				unsigned short mtu,
+				struct rtnl_link_stats *stats)
+{
+	struct vpn_ipdevice *ipdevice;
+	GString *str;
+
+	if (type == ARPHRD_LOOPBACK)
+		return;
+
+	ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
+	if (ipdevice != NULL)
+		goto update;
+
+	ipdevice = g_try_new0(struct vpn_ipdevice, 1);
+	if (ipdevice == NULL)
+		return;
+
+	ipdevice->index = index;
+	ipdevice->ifname = connman_inet_ifname(index);
+	ipdevice->type = type;
+
+	ipdevice->address = g_strdup(address);
+
+	g_hash_table_insert(ipdevice_hash, GINT_TO_POINTER(index), ipdevice);
+
+	connman_info("%s {create} index %d type %d <%s>", ipdevice->ifname,
+						index, type, type2str(type));
+
+update:
+	ipdevice->mtu = mtu;
+
+	if (flags == ipdevice->flags)
+		return;
+
+	ipdevice->flags = flags;
+
+	str = g_string_new(NULL);
+	if (str == NULL)
+		return;
+
+	if (flags & IFF_UP)
+		g_string_append(str, "UP");
+	else
+		g_string_append(str, "DOWN");
+
+	if (flags & IFF_RUNNING)
+		g_string_append(str, ",RUNNING");
+
+	if (flags & IFF_LOWER_UP)
+		g_string_append(str, ",LOWER_UP");
+
+	connman_info("%s {update} flags %u <%s>", ipdevice->ifname,
+							flags, str->str);
+
+	g_string_free(str, TRUE);
+}
+
+void __vpn_ipconfig_dellink(int index, struct rtnl_link_stats *stats)
+{
+	struct vpn_ipdevice *ipdevice;
+
+	DBG("index %d", index);
+
+	ipdevice = g_hash_table_lookup(ipdevice_hash, GINT_TO_POINTER(index));
+	if (ipdevice == NULL)
+		return;
+
+	g_hash_table_remove(ipdevice_hash, GINT_TO_POINTER(index));
+}
+
+static void free_ipdevice(gpointer data)
+{
+	struct vpn_ipdevice *ipdevice = data;
+
+	connman_info("%s {remove} index %d", ipdevice->ifname,
+							ipdevice->index);
+
+	g_free(ipdevice->ipv4_gateway);
+	g_free(ipdevice->ipv6_gateway);
+	g_free(ipdevice->pac);
+
+	g_free(ipdevice->address);
+
+	g_free(ipdevice->ifname);
+	g_free(ipdevice);
+}
+
+int __vpn_ipconfig_init(void)
+{
+	DBG("");
+
+	ipdevice_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+							NULL, free_ipdevice);
+
+	return 0;
+}
+
+void __vpn_ipconfig_cleanup(void)
+{
+	DBG("");
+
+	g_hash_table_destroy(ipdevice_hash);
+	ipdevice_hash = NULL;
+}
diff --git a/vpn/vpn-manager.c b/vpn/vpn-manager.c
new file mode 100644
index 0000000..1569a55
--- /dev/null
+++ b/vpn/vpn-manager.c
@@ -0,0 +1,142 @@
+/*
+ *
+ *  ConnMan VPN daemon
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+
+#include <gdbus.h>
+#include <connman/log.h>
+
+#include "../src/connman.h"
+
+#include "vpn.h"
+#include "vpn-dbus.h"
+
+static int vpn_connect_count;
+static DBusConnection *connection;
+
+static DBusMessage *create(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	int err;
+
+	DBG("conn %p", conn);
+
+	err = __vpn_provider_create_and_connect(msg);
+	if (err < 0) {
+		if (err == -EINPROGRESS) {
+			connman_error("Invalid return code (%d) "
+					"from connect", err);
+			err = -EINVAL;
+		}
+
+		return __connman_error_failed(msg, -err);
+	}
+
+	return NULL;
+}
+
+static DBusMessage *remove(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+	const char *path;
+	int err;
+
+	dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+							DBUS_TYPE_INVALID);
+
+	DBG("conn %p path %s", conn, path);
+
+	err = __vpn_provider_remove(path);
+	if (err < 0)
+		return __connman_error_failed(msg, -err);
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *get_connections(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	DBusMessage *reply;
+
+	DBG("conn %p", conn);
+
+	reply = __vpn_provider_get_connections(msg);
+	if (reply == NULL)
+		return __connman_error_failed(msg, -EINVAL);
+
+	return reply;
+}
+
+static const GDBusMethodTable manager_methods[] = {
+	{ GDBUS_ASYNC_METHOD("Create",
+			GDBUS_ARGS({ "properties", "a{sv}" }),
+			GDBUS_ARGS({ "path", "o" }),
+			create) },
+	{ GDBUS_ASYNC_METHOD("Remove",
+			GDBUS_ARGS({ "identifier", "o" }), NULL,
+			remove) },
+	{ GDBUS_METHOD("GetConnections", NULL,
+			GDBUS_ARGS({ "connections", "a(oa{sv})" }),
+			get_connections) },
+	{ },
+};
+
+static const GDBusSignalTable manager_signals[] = {
+	{ GDBUS_SIGNAL("ConnectionAdded",
+			GDBUS_ARGS({ "identifier", "o" },
+				{ "properties", "a{sv}" })) },
+	{ GDBUS_SIGNAL("ConnectionRemoved",
+			GDBUS_ARGS({ "identifier", "o" })) },
+	{ },
+};
+
+int __vpn_manager_init(void)
+{
+	DBG("");
+
+	connection = connman_dbus_get_connection();
+	if (connection == NULL)
+		return -1;
+
+	g_dbus_register_interface(connection, VPN_MANAGER_PATH,
+					VPN_MANAGER_INTERFACE,
+					manager_methods,
+					manager_signals, NULL, NULL, NULL);
+
+	vpn_connect_count = 0;
+
+	return 0;
+}
+
+void __vpn_manager_cleanup(void)
+{
+	DBG("");
+
+	if (connection == NULL)
+		return;
+
+	g_dbus_unregister_interface(connection, VPN_MANAGER_PATH,
+						VPN_MANAGER_INTERFACE);
+
+	dbus_connection_unref(connection);
+}
diff --git a/vpn/vpn-polkit.conf b/vpn/vpn-polkit.conf
new file mode 100644
index 0000000..a1dc617
--- /dev/null
+++ b/vpn/vpn-polkit.conf
@@ -0,0 +1,11 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy user="root">
+        <allow own="net.connman.vpn"/>
+        <allow send_interface="net.connman.vpn.Agent"/>
+    </policy>
+    <policy context="default">
+        <allow send_destination="net.connman.vpn"/>
+    </policy>
+</busconfig>
diff --git a/vpn/vpn-polkit.policy b/vpn/vpn-polkit.policy
new file mode 100644
index 0000000..0c42722
--- /dev/null
+++ b/vpn/vpn-polkit.policy
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+
+<policyconfig>
+
+  <vendor>Connection Manager VPN daemon</vendor>
+  <icon_name>network-wireless</icon_name>
+
+  <action id="net.connman.vpn.modify">
+    <description>Settings configuration</description>
+    <message>Policy prevents modification of settings</message>
+    <defaults>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>auth_self_keep_session</allow_active>
+    </defaults>
+  </action>
+
+  <action id="net.connman.vpn.secret">
+    <description>Secrets configuration</description>
+    <message>Policy prevents modification of secrets</message>
+    <defaults>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>auth_admin_keep_session</allow_active>
+    </defaults>
+  </action>
+
+</policyconfig>
diff --git a/vpn/vpn-provider.c b/vpn/vpn-provider.c
new file mode 100644
index 0000000..a82075d
--- /dev/null
+++ b/vpn/vpn-provider.c
@@ -0,0 +1,1680 @@
+/*
+ *
+ *  ConnMan VPN daemon
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <gdbus.h>
+#include <connman/log.h>
+#include <gweb/gresolv.h>
+
+#include "../src/connman.h"
+#include "vpn-dbus.h"
+#include "vpn-provider.h"
+#include "vpn.h"
+
+static DBusConnection *connection;
+static GHashTable *provider_hash;
+static GSList *driver_list;
+static int configuration_count;
+
+struct vpn_route {
+	int family;
+	char *host;
+	char *netmask;
+	char *gateway;
+};
+
+struct vpn_provider {
+	int refcount;
+	int index;
+	int fd;
+	enum vpn_provider_state state;
+	char *path;
+	char *identifier;
+	char *name;
+	char *type;
+	char *host;
+	char *domain;
+	int family;
+	GHashTable *routes;
+	struct vpn_provider_driver *driver;
+	void *driver_data;
+	GHashTable *setting_strings;
+	GHashTable *user_routes;
+	gchar **user_networks;
+	gsize num_user_networks;
+	GResolv *resolv;
+	char **host_ip;
+	DBusMessage *pending_msg;
+	struct vpn_ipconfig *ipconfig_ipv4;
+	struct vpn_ipconfig *ipconfig_ipv6;
+	char **nameservers;
+};
+
+static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	DBG("conn %p", conn);
+
+	// XXX:
+
+	return NULL;
+}
+
+static DBusMessage *clear_property(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	DBG("conn %p", conn);
+
+	// XXX:
+
+	return NULL;
+}
+
+static DBusMessage *do_connect(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct vpn_provider *provider = data;
+	int err;
+
+	DBG("conn %p provider %p", conn, provider);
+
+	err = __vpn_provider_connect(provider);
+	if (err < 0)
+		return __connman_error_failed(msg, -err);
+
+	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *do_disconnect(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct vpn_provider *provider = data;
+	int err;
+
+	DBG("conn %p provider %p", conn, provider);
+
+	err = __vpn_provider_disconnect(provider);
+	if (err < 0)
+		return __connman_error_failed(msg, -err);
+	else
+		return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable connection_methods[] = {
+	{ GDBUS_METHOD("SetProperty",
+			GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+			NULL, set_property) },
+	{ GDBUS_METHOD("ClearProperty",
+			GDBUS_ARGS({ "name", "s" }), NULL,
+			clear_property) },
+	{ GDBUS_ASYNC_METHOD("Connect", NULL, NULL, do_connect) },
+	{ GDBUS_METHOD("Disconnect", NULL, NULL, do_disconnect) },
+	{ },
+};
+
+static const GDBusSignalTable connection_signals[] = {
+	{ GDBUS_SIGNAL("PropertyChanged",
+			GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+	{ },
+};
+
+static void resolv_result(GResolvResultStatus status,
+					char **results, gpointer user_data)
+{
+	struct vpn_provider *provider = user_data;
+
+	DBG("status %d", status);
+
+	if (status == G_RESOLV_RESULT_STATUS_SUCCESS && results != NULL &&
+						g_strv_length(results) > 0)
+		provider->host_ip = g_strdupv(results);
+
+	vpn_provider_unref(provider);
+}
+
+static void provider_resolv_host_addr(struct vpn_provider *provider)
+{
+	if (provider->host == NULL)
+		return;
+
+	if (connman_inet_check_ipaddress(provider->host) > 0)
+		return;
+
+	if (provider->host_ip != NULL)
+		return;
+
+	/*
+	 * If the hostname is not numeric, try to resolv it. We do not wait
+	 * the result as it might take some time. We will get the result
+	 * before VPN will feed routes to us because VPN client will need
+	 * the IP address also before VPN connection can be established.
+	 */
+	provider->resolv = g_resolv_new(0);
+	if (provider->resolv == NULL) {
+		DBG("Cannot resolv %s", provider->host);
+		return;
+	}
+
+	DBG("Trying to resolv %s", provider->host);
+
+	vpn_provider_ref(provider);
+
+	g_resolv_lookup_hostname(provider->resolv, provider->host,
+				resolv_result, provider);
+}
+
+void __vpn_provider_append_properties(struct vpn_provider *provider,
+							DBusMessageIter *iter)
+{
+	if (provider->host != NULL)
+		connman_dbus_dict_append_basic(iter, "Host",
+					DBUS_TYPE_STRING, &provider->host);
+
+	if (provider->domain != NULL)
+		connman_dbus_dict_append_basic(iter, "Domain",
+					DBUS_TYPE_STRING, &provider->domain);
+
+	if (provider->type != NULL)
+		connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING,
+						 &provider->type);
+}
+
+int __vpn_provider_append_user_route(struct vpn_provider *provider,
+			int family, const char *network, const char *netmask)
+{
+	struct vpn_route *route;
+	char *key = g_strdup_printf("%d/%s/%s", family, network, netmask);
+
+	DBG("family %d network %s netmask %s", family, network, netmask);
+
+	route = g_hash_table_lookup(provider->user_routes, key);
+	if (route == NULL) {
+		route = g_try_new0(struct vpn_route, 1);
+		if (route == NULL) {
+			connman_error("out of memory");
+			return -ENOMEM;
+		}
+
+		route->family = family;
+		route->host = g_strdup(network);
+		route->netmask = g_strdup(netmask);
+
+		g_hash_table_replace(provider->user_routes, key, route);
+	} else
+		g_free(key);
+
+	return 0;
+}
+
+static void set_user_networks(struct vpn_provider *provider,
+							char **networks)
+{
+	int i = 0;
+
+	while (networks[i] != NULL) {
+		char **elems = g_strsplit(networks[i], "/", 0);
+		char *network, *netmask;
+		int family = PF_UNSPEC, ret;
+
+		if (elems == NULL)
+			break;
+
+		network = elems[0];
+		if (network == NULL || *network == '\0') {
+			DBG("no network/netmask set");
+			g_strfreev(elems);
+			break;
+		}
+
+		netmask = elems[1];
+		if (netmask != NULL && *netmask == '\0') {
+			DBG("no netmask set");
+			g_strfreev(elems);
+			break;
+		}
+
+		if (g_strrstr(network, ":") != NULL)
+			family = AF_INET6;
+		else if (g_strrstr(network, ".") != NULL) {
+			family = AF_INET;
+
+			if (g_strrstr(netmask, ".") == NULL) {
+				/* We have netmask length */
+				in_addr_t addr;
+				struct in_addr netmask_in;
+				unsigned char prefix_len = 32;
+
+				if (netmask != NULL)
+					prefix_len = atoi(netmask);
+
+				addr = 0xffffffff << (32 - prefix_len);
+				netmask_in.s_addr = htonl(addr);
+				netmask = inet_ntoa(netmask_in);
+
+				DBG("network %s netmask %s", network, netmask);
+			}
+		}
+
+		ret = __vpn_provider_append_user_route(provider,
+						family, network, netmask);
+		g_strfreev(elems);
+
+		if (ret != 0)
+			break;
+
+		i++;
+	}
+}
+
+static int provider_load_from_keyfile(struct vpn_provider *provider,
+		GKeyFile *keyfile)
+{
+	gsize idx = 0;
+	gchar **settings;
+	gchar *key, *value;
+	gsize length;
+
+	settings = g_key_file_get_keys(keyfile, provider->identifier, &length,
+				NULL);
+	if (settings == NULL) {
+		g_key_file_free(keyfile);
+		return -ENOENT;
+	}
+
+	while (idx < length) {
+		key = settings[idx];
+		if (key != NULL) {
+			if (g_str_equal(key, "Networks") == TRUE) {
+				g_strfreev(provider->user_networks);
+				provider->user_networks =
+					g_key_file_get_string_list(keyfile,
+						provider->identifier,
+						key,
+						&provider->num_user_networks,
+						NULL);
+			} else {
+				value = g_key_file_get_string(keyfile,
+							provider->identifier,
+							key, NULL);
+				vpn_provider_set_string(provider, key,
+							value);
+				g_free(value);
+			}
+		}
+		idx += 1;
+	}
+	g_strfreev(settings);
+
+	if (provider->user_networks != NULL)
+		set_user_networks(provider, provider->user_networks);
+
+	return 0;
+}
+
+
+static int vpn_provider_load(struct vpn_provider *provider)
+{
+	GKeyFile *keyfile;
+
+	DBG("provider %p", provider);
+
+	keyfile = __connman_storage_load_provider(provider->identifier);
+	if (keyfile == NULL)
+		return -ENOENT;
+
+	provider_load_from_keyfile(provider, keyfile);
+
+	g_key_file_free(keyfile);
+	return 0;
+}
+
+static int vpn_provider_save(struct vpn_provider *provider)
+{
+	GKeyFile *keyfile;
+
+	DBG("provider %p", provider);
+
+	keyfile = g_key_file_new();
+	if (keyfile == NULL)
+		return -ENOMEM;
+
+	g_key_file_set_string(keyfile, provider->identifier,
+			"Name", provider->name);
+	g_key_file_set_string(keyfile, provider->identifier,
+			"Type", provider->type);
+	g_key_file_set_string(keyfile, provider->identifier,
+			"Host", provider->host);
+	g_key_file_set_string(keyfile, provider->identifier,
+			"VPN.Domain", provider->domain);
+	if (provider->user_networks != NULL)
+		g_key_file_set_string_list(keyfile, provider->identifier,
+				"Networks",
+				(const gchar **)provider->user_networks,
+				provider->num_user_networks);
+
+	if (provider->driver != NULL && provider->driver->save != NULL)
+		provider->driver->save(provider, keyfile);
+
+	__connman_storage_save_provider(keyfile, provider->identifier);
+        g_key_file_free(keyfile);
+
+	return 0;
+}
+
+static struct vpn_provider *vpn_provider_lookup(const char *identifier)
+{
+	struct vpn_provider *provider = NULL;
+
+	provider = g_hash_table_lookup(provider_hash, identifier);
+
+	return provider;
+}
+
+static gboolean match_driver(struct vpn_provider *provider,
+				struct vpn_provider_driver *driver)
+{
+	if (g_strcmp0(driver->name, provider->type) == 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+static int provider_probe(struct vpn_provider *provider)
+{
+	GSList *list;
+
+	DBG("provider %p name %s", provider, provider->name);
+
+	if (provider->driver != NULL)
+		return -EALREADY;
+
+	for (list = driver_list; list; list = list->next) {
+		struct vpn_provider_driver *driver = list->data;
+
+		if (match_driver(provider, driver) == FALSE)
+			continue;
+
+		DBG("driver %p name %s", driver, driver->name);
+
+		if (driver->probe != NULL && driver->probe(provider) == 0) {
+			provider->driver = driver;
+			break;
+		}
+	}
+
+	if (provider->driver == NULL)
+		return -ENODEV;
+
+	return 0;
+}
+
+static void provider_remove(struct vpn_provider *provider)
+{
+	if (provider->driver != NULL) {
+		provider->driver->remove(provider);
+		provider->driver = NULL;
+	}
+}
+
+static int provider_register(struct vpn_provider *provider)
+{
+	return provider_probe(provider);
+}
+
+static void provider_unregister(struct vpn_provider *provider)
+{
+	provider_remove(provider);
+}
+
+struct vpn_provider *
+vpn_provider_ref_debug(struct vpn_provider *provider,
+			const char *file, int line, const char *caller)
+{
+	DBG("%p ref %d by %s:%d:%s()", provider, provider->refcount + 1,
+		file, line, caller);
+
+	__sync_fetch_and_add(&provider->refcount, 1);
+
+	return provider;
+}
+
+static void provider_destruct(struct vpn_provider *provider)
+{
+	DBG("provider %p", provider);
+
+	g_free(provider->name);
+	g_free(provider->type);
+	g_free(provider->host);
+	g_free(provider->domain);
+	g_free(provider->identifier);
+	g_free(provider->path);
+	g_strfreev(provider->user_networks);
+	g_strfreev(provider->nameservers);
+	g_hash_table_destroy(provider->routes);
+	g_hash_table_destroy(provider->user_routes);
+	g_hash_table_destroy(provider->setting_strings);
+	if (provider->resolv != NULL) {
+		g_resolv_unref(provider->resolv);
+		provider->resolv = NULL;
+	}
+	g_strfreev(provider->host_ip);
+	g_free(provider);
+}
+
+void vpn_provider_unref_debug(struct vpn_provider *provider,
+				const char *file, int line, const char *caller)
+{
+	DBG("%p ref %d by %s:%d:%s()", provider, provider->refcount - 1,
+		file, line, caller);
+
+	if (__sync_fetch_and_sub(&provider->refcount, 1) != 1)
+		return;
+
+	provider_remove(provider);
+
+	provider_destruct(provider);
+}
+
+static void configuration_count_add(void)
+{
+	DBG("count %d", configuration_count + 1);
+
+	__sync_fetch_and_add(&configuration_count, 1);
+}
+
+static void configuration_count_del(void)
+{
+	DBG("count %d", configuration_count - 1);
+
+	if (__sync_fetch_and_sub(&configuration_count, 1) != 1)
+		return;
+
+	raise(SIGTERM);
+}
+
+int __vpn_provider_disconnect(struct vpn_provider *provider)
+{
+	int err;
+
+	DBG("provider %p", provider);
+
+	if (provider->driver != NULL && provider->driver->disconnect != NULL)
+		err = provider->driver->disconnect(provider);
+	else
+		return -EOPNOTSUPP;
+
+	if (err < 0) {
+		if (err != -EINPROGRESS)
+			return err;
+
+		return -EINPROGRESS;
+	}
+
+	return 0;
+}
+
+int __vpn_provider_connect(struct vpn_provider *provider)
+{
+	int err;
+
+	DBG("provider %p", provider);
+
+	if (provider->driver != NULL && provider->driver->connect != NULL)
+		err = provider->driver->connect(provider);
+	else
+		return -EOPNOTSUPP;
+
+	return err;
+}
+
+int __vpn_provider_remove(const char *path)
+{
+	struct vpn_provider *provider;
+
+	DBG("path %s", path);
+
+	provider = vpn_provider_lookup(path);
+	if (provider != NULL) {
+		DBG("Removing VPN %s", provider->identifier);
+
+		provider_unregister(provider);
+		g_hash_table_remove(provider_hash, provider->identifier);
+		return 0;
+	}
+
+	return -ENXIO;
+}
+
+static void append_ipv4(DBusMessageIter *iter, void *user_data)
+{
+	struct vpn_provider *provider = user_data;
+	const char *address, *gateway, *peer;
+
+	address = __vpn_ipconfig_get_local(provider->ipconfig_ipv4);
+	if (address != NULL) {
+		in_addr_t addr;
+		struct in_addr netmask;
+		char *mask;
+		int prefixlen;
+
+		prefixlen = __vpn_ipconfig_get_prefixlen(
+						provider->ipconfig_ipv4);
+
+		addr = 0xffffffff << (32 - prefixlen);
+		netmask.s_addr = htonl(addr);
+		mask = inet_ntoa(netmask);
+
+		connman_dbus_dict_append_basic(iter, "Address",
+						DBUS_TYPE_STRING, &address);
+
+		connman_dbus_dict_append_basic(iter, "Netmask",
+						DBUS_TYPE_STRING, &mask);
+	}
+
+	gateway = __vpn_ipconfig_get_gateway(provider->ipconfig_ipv4);
+	if (gateway != NULL)
+		connman_dbus_dict_append_basic(iter, "Gateway",
+						DBUS_TYPE_STRING, &gateway);
+
+	peer = __vpn_ipconfig_get_peer(provider->ipconfig_ipv4);
+	if (peer != NULL)
+		connman_dbus_dict_append_basic(iter, "Host",
+						DBUS_TYPE_STRING, &peer);
+}
+
+static void append_ipv6(DBusMessageIter *iter, void *user_data)
+{
+	struct vpn_provider *provider = user_data;
+	const char *address, *gateway, *peer;
+
+	address = __vpn_ipconfig_get_local(provider->ipconfig_ipv6);
+	if (address != NULL) {
+		unsigned char prefixlen;
+
+		connman_dbus_dict_append_basic(iter, "Address",
+						DBUS_TYPE_STRING, &address);
+
+		prefixlen = __vpn_ipconfig_get_prefixlen(
+						provider->ipconfig_ipv6);
+
+		connman_dbus_dict_append_basic(iter, "PrefixLength",
+						DBUS_TYPE_BYTE, &prefixlen);
+	}
+
+	gateway = __vpn_ipconfig_get_gateway(provider->ipconfig_ipv6);
+	if (gateway != NULL)
+		connman_dbus_dict_append_basic(iter, "Gateway",
+						DBUS_TYPE_STRING, &gateway);
+
+	peer = __vpn_ipconfig_get_peer(provider->ipconfig_ipv6);
+	if (peer != NULL)
+		connman_dbus_dict_append_basic(iter, "Host",
+						DBUS_TYPE_STRING, &peer);
+}
+
+static const char *state2string(enum vpn_provider_state state)
+{
+	switch (state) {
+	case VPN_PROVIDER_STATE_UNKNOWN:
+		break;
+	case VPN_PROVIDER_STATE_IDLE:
+		return "idle";
+	case VPN_PROVIDER_STATE_CONNECT:
+		return "configuration";
+	case VPN_PROVIDER_STATE_READY:
+		return "ready";
+	case VPN_PROVIDER_STATE_DISCONNECT:
+		return "disconnect";
+	case VPN_PROVIDER_STATE_FAILURE:
+		return "failure";
+	}
+
+	return NULL;
+}
+
+static int provider_indicate_state(struct vpn_provider *provider,
+				enum vpn_provider_state state)
+{
+	const char *str;
+
+	DBG("provider %p state %d", provider, state);
+
+	str = state2string(state);
+	if (str == NULL)
+		return -EINVAL;
+
+	provider->state = state;
+
+	if (state == VPN_PROVIDER_STATE_READY) {
+		connman_dbus_property_changed_basic(provider->path,
+					VPN_CONNECTION_INTERFACE, "Index",
+					DBUS_TYPE_INT32, &provider->index);
+
+		if (provider->family == AF_INET)
+			connman_dbus_property_changed_dict(provider->path,
+					VPN_CONNECTION_INTERFACE, "IPv4",
+					append_ipv4, provider);
+		else if (provider->family == AF_INET6)
+			connman_dbus_property_changed_dict(provider->path,
+					VPN_CONNECTION_INTERFACE, "IPv6",
+					append_ipv6, provider);
+	}
+
+	connman_dbus_property_changed_basic(provider->path,
+					VPN_CONNECTION_INTERFACE, "State",
+					DBUS_TYPE_STRING, &str);
+	return 0;
+}
+
+static void append_nameservers(DBusMessageIter *iter, char **servers)
+{
+	int i;
+
+	DBG("%p", servers);
+
+	for (i = 0; servers[i] != NULL; i++) {
+		DBG("servers[%d] %s", i, servers[i]);
+		dbus_message_iter_append_basic(iter,
+					DBUS_TYPE_STRING, &servers[i]);
+	}
+}
+
+static void append_dns(DBusMessageIter *iter, void *user_data)
+{
+	struct vpn_provider *provider = user_data;
+
+	if (provider->nameservers != NULL)
+		append_nameservers(iter, provider->nameservers);
+}
+
+static void append_state(DBusMessageIter *iter,
+					struct vpn_provider *provider)
+{
+	char *str;
+
+	switch (provider->state) {
+	case VPN_PROVIDER_STATE_UNKNOWN:
+	case VPN_PROVIDER_STATE_IDLE:
+		str = "idle";
+		break;
+	case VPN_PROVIDER_STATE_CONNECT:
+		str = "configuration";
+		break;
+	case VPN_PROVIDER_STATE_READY:
+		str = "ready";
+		break;
+	case VPN_PROVIDER_STATE_DISCONNECT:
+		str = "disconnect";
+		break;
+	case VPN_PROVIDER_STATE_FAILURE:
+		str = "failure";
+		break;
+	}
+
+	connman_dbus_dict_append_basic(iter, "State",
+				DBUS_TYPE_STRING, &str);
+}
+
+static void append_properties(DBusMessageIter *iter,
+					struct vpn_provider *provider)
+{
+	DBusMessageIter dict;
+
+	connman_dbus_dict_open(iter, &dict);
+
+	append_state(&dict, provider);
+
+	if (provider->type != NULL)
+		connman_dbus_dict_append_basic(&dict, "Type",
+					DBUS_TYPE_STRING, &provider->type);
+
+	if (provider->name != NULL)
+		connman_dbus_dict_append_basic(&dict, "Name",
+					DBUS_TYPE_STRING, &provider->name);
+
+	if (provider->host != NULL)
+		connman_dbus_dict_append_basic(&dict, "Host",
+					DBUS_TYPE_STRING, &provider->host);
+	if (provider->index >= 0)
+		connman_dbus_dict_append_basic(&dict, "Index",
+					DBUS_TYPE_INT32, &provider->index);
+	if (provider->domain != NULL)
+		connman_dbus_dict_append_basic(&dict, "Domain",
+					DBUS_TYPE_STRING, &provider->domain);
+
+	if (provider->family == AF_INET)
+		connman_dbus_dict_append_dict(&dict, "IPv4", append_ipv4,
+						provider);
+	else if (provider->family == AF_INET6)
+		connman_dbus_dict_append_dict(&dict, "IPv6", append_ipv6,
+						provider);
+
+	connman_dbus_dict_append_array(&dict, "Nameservers",
+				DBUS_TYPE_STRING, append_dns, provider);
+
+	connman_dbus_dict_close(iter, &dict);
+}
+
+static connman_bool_t check_host(char **hosts, char *host)
+{
+	int i;
+
+	if (hosts == NULL)
+		return FALSE;
+
+	for (i = 0; hosts[i] != NULL; i++) {
+		if (g_strcmp0(hosts[i], host) == 0)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void provider_append_routes(gpointer key, gpointer value,
+					gpointer user_data)
+{
+	struct vpn_route *route = value;
+	struct vpn_provider *provider = user_data;
+	int index = provider->index;
+
+	/*
+	 * If the VPN administrator/user has given a route to
+	 * VPN server, then we must discard that because the
+	 * server cannot be contacted via VPN tunnel.
+	 */
+	if (check_host(provider->host_ip, route->host) == TRUE) {
+		DBG("Discarding VPN route to %s via %s at index %d",
+			route->host, route->gateway, index);
+		return;
+	}
+
+	if (route->family == AF_INET6) {
+		unsigned char prefix_len = atoi(route->netmask);
+
+		connman_inet_add_ipv6_network_route(index, route->host,
+							route->gateway,
+							prefix_len);
+	} else {
+		connman_inet_add_network_route(index, route->host,
+						route->gateway,
+						route->netmask);
+	}
+}
+
+static int set_connected(struct vpn_provider *provider,
+					connman_bool_t connected)
+{
+	struct vpn_ipconfig *ipconfig;
+
+	DBG("provider %p id %s connected %d", provider,
+					provider->identifier, connected);
+
+	if (connected == TRUE) {
+		if (provider->family == AF_INET6)
+			ipconfig = provider->ipconfig_ipv6;
+		else
+			ipconfig = provider->ipconfig_ipv4;
+
+		__vpn_ipconfig_address_add(ipconfig, provider->family);
+		__vpn_ipconfig_gateway_add(ipconfig, provider->family);
+
+		provider_indicate_state(provider,
+					VPN_PROVIDER_STATE_READY);
+
+		g_hash_table_foreach(provider->routes, provider_append_routes,
+					provider);
+
+		g_hash_table_foreach(provider->user_routes,
+					provider_append_routes, provider);
+
+	} else {
+		provider_indicate_state(provider,
+					VPN_PROVIDER_STATE_DISCONNECT);
+
+		provider_indicate_state(provider,
+					VPN_PROVIDER_STATE_IDLE);
+	}
+
+	return 0;
+}
+
+int vpn_provider_set_state(struct vpn_provider *provider,
+					enum vpn_provider_state state)
+{
+	if (provider == NULL)
+		return -EINVAL;
+
+	switch (state) {
+	case VPN_PROVIDER_STATE_UNKNOWN:
+		return -EINVAL;
+	case VPN_PROVIDER_STATE_IDLE:
+		return set_connected(provider, FALSE);
+	case VPN_PROVIDER_STATE_CONNECT:
+		return provider_indicate_state(provider, state);
+	case VPN_PROVIDER_STATE_READY:
+		return set_connected(provider, TRUE);
+	case VPN_PROVIDER_STATE_DISCONNECT:
+		return provider_indicate_state(provider, state);
+	case VPN_PROVIDER_STATE_FAILURE:
+		return provider_indicate_state(provider, state);
+	}
+	return -EINVAL;
+}
+
+int vpn_provider_indicate_error(struct vpn_provider *provider,
+					enum vpn_provider_error error)
+{
+	DBG("provider %p id %s error %d", provider, provider->identifier,
+									error);
+
+	switch (error) {
+	case VPN_PROVIDER_ERROR_LOGIN_FAILED:
+		break;
+	case VPN_PROVIDER_ERROR_AUTH_FAILED:
+		break;
+	case VPN_PROVIDER_ERROR_CONNECT_FAILED:
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static void unregister_provider(gpointer data)
+{
+	struct vpn_provider *provider = data;
+
+	configuration_count_del();
+
+	vpn_provider_unref(provider);
+}
+
+static void destroy_route(gpointer user_data)
+{
+	struct vpn_route *route = user_data;
+
+	g_free(route->host);
+	g_free(route->netmask);
+	g_free(route->gateway);
+	g_free(route);
+}
+
+static void provider_initialize(struct vpn_provider *provider)
+{
+	DBG("provider %p", provider);
+
+	provider->index = 0;
+	provider->fd = -1;
+	provider->name = NULL;
+	provider->type = NULL;
+	provider->domain = NULL;
+	provider->identifier = NULL;
+	provider->user_networks = NULL;
+	provider->routes = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+					NULL, destroy_route);
+	provider->user_routes = g_hash_table_new_full(g_str_hash, g_str_equal,
+					g_free, destroy_route);
+	provider->setting_strings = g_hash_table_new_full(g_str_hash,
+						g_str_equal, g_free, g_free);
+}
+
+static struct vpn_provider *vpn_provider_new(void)
+{
+	struct vpn_provider *provider;
+
+	provider = g_try_new0(struct vpn_provider, 1);
+	if (provider == NULL)
+		return NULL;
+
+	provider->refcount = 1;
+
+	DBG("provider %p", provider);
+	provider_initialize(provider);
+
+	return provider;
+}
+
+static struct vpn_provider *vpn_provider_get(const char *identifier)
+{
+	struct vpn_provider *provider;
+
+	provider = g_hash_table_lookup(provider_hash, identifier);
+	if (provider != NULL)
+		return provider;
+
+	provider = vpn_provider_new();
+	if (provider == NULL)
+		return NULL;
+
+	DBG("provider %p", provider);
+
+	provider->identifier = g_strdup(identifier);
+
+	g_hash_table_insert(provider_hash, provider->identifier, provider);
+
+	configuration_count_add();
+
+	return provider;
+}
+
+static void provider_dbus_ident(char *ident)
+{
+	int i, len = strlen(ident);
+
+	for (i = 0; i < len; i++) {
+		if (ident[i] >= '0' && ident[i] <= '9')
+			continue;
+		if (ident[i] >= 'a' && ident[i] <= 'z')
+			continue;
+		if (ident[i] >= 'A' && ident[i] <= 'Z')
+			continue;
+		ident[i] = '_';
+	}
+}
+
+static int connection_unregister(struct vpn_provider *provider)
+{
+	if (provider->path == NULL)
+		return -EALREADY;
+
+	g_dbus_unregister_interface(connection, provider->path,
+				VPN_CONNECTION_INTERFACE);
+
+	g_free(provider->path);
+	provider->path = NULL;
+
+	return 0;
+}
+
+static int connection_register(struct vpn_provider *provider)
+{
+	DBG("provider %p path %s", provider, provider->path);
+
+	if (provider->path != NULL)
+		return -EALREADY;
+
+	provider->path = g_strdup_printf("%s/connection/%s", VPN_PATH,
+						provider->identifier);
+
+	g_dbus_register_interface(connection, provider->path,
+				VPN_CONNECTION_INTERFACE,
+				connection_methods, connection_signals,
+				NULL, provider, NULL);
+
+	return 0;
+}
+
+static struct vpn_provider *provider_create_from_keyfile(GKeyFile *keyfile,
+		const char *ident)
+{
+	struct vpn_provider *provider;
+
+	if (keyfile == NULL || ident == NULL)
+		return NULL;
+
+	provider = vpn_provider_lookup(ident);
+	if (provider == NULL) {
+		provider = vpn_provider_get(ident);
+		if (provider == NULL) {
+			DBG("can not create provider");
+			return NULL;
+		}
+
+		provider_load_from_keyfile(provider, keyfile);
+
+		if (provider->name == NULL || provider->host == NULL ||
+				provider->domain == NULL) {
+			DBG("cannot get name, host or domain");
+			vpn_provider_unref(provider);
+			return NULL;
+		}
+
+		if (provider_register(provider) == 0)
+			connection_register(provider);
+	}
+	return provider;
+}
+
+static void provider_create_all_from_type(const char *provider_type)
+{
+	unsigned int i;
+	char **providers;
+	char *id, *type;
+	GKeyFile *keyfile;
+
+	DBG("provider type %s", provider_type);
+
+	providers = __connman_storage_get_providers();
+
+	for (i = 0; providers[i] != NULL; i+=1) {
+
+		if (strncmp(providers[i], "provider_", 9) != 0)
+			continue;
+
+		id = providers[i] + 9;
+		keyfile = __connman_storage_load_provider(id);
+
+		if (keyfile == NULL)
+			continue;
+
+		type = g_key_file_get_string(keyfile, id, "Type", NULL);
+
+		DBG("keyfile %p id %s type %s", keyfile, id, type);
+
+		if (strcmp(provider_type, type) != 0) {
+			g_free(type);
+			g_key_file_free(keyfile);
+			continue;
+		}
+
+		if (provider_create_from_keyfile(keyfile, id) == NULL)
+			DBG("could not create provider");
+
+		g_free(type);
+		g_key_file_free(keyfile);
+	}
+	g_strfreev(providers);
+}
+
+static char **get_user_networks(DBusMessageIter *array, int *count)
+{
+	DBusMessageIter entry;
+	char **networks = NULL;
+	GSList *list = NULL, *l;
+	int len;
+
+	dbus_message_iter_recurse(array, &entry);
+
+	while (dbus_message_iter_get_arg_type(&entry) == DBUS_TYPE_STRING) {
+		const char *val;
+		dbus_message_iter_get_basic(&entry, &val);
+
+		list = g_slist_prepend(list, g_strdup(val));
+		dbus_message_iter_next(&entry);
+	}
+
+	len = g_slist_length(list);
+	if (len == 0)
+		goto out;
+
+	networks = g_try_new(char *, len + 1);
+	if (networks == NULL)
+		goto out;
+
+	*count = len;
+	networks[len] = 0;
+
+	for (l = list; l != NULL; l = g_slist_next(l))
+		networks[--len] = l->data;
+
+out:
+	g_slist_free(list);
+
+	return networks;
+}
+
+int __vpn_provider_create_and_connect(DBusMessage *msg)
+{
+	struct vpn_provider *provider;
+	DBusMessageIter iter, array;
+	const char *type = NULL, *name = NULL;
+	const char *host = NULL, *domain = NULL;
+	char **networks = NULL;
+	char *ident;
+	int err, count = 0;
+
+	dbus_message_iter_init(msg, &iter);
+	dbus_message_iter_recurse(&iter, &array);
+
+	while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter entry, value;
+		const char *key;
+
+		dbus_message_iter_recurse(&array, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_recurse(&entry, &value);
+
+		switch (dbus_message_iter_get_arg_type(&value)) {
+		case DBUS_TYPE_STRING:
+			if (g_str_equal(key, "Type") == TRUE)
+				dbus_message_iter_get_basic(&value, &type);
+			else if (g_str_equal(key, "Name") == TRUE)
+				dbus_message_iter_get_basic(&value, &name);
+			else if (g_str_equal(key, "Host") == TRUE)
+				dbus_message_iter_get_basic(&value, &host);
+			else if (g_str_equal(key, "VPN.Domain") == TRUE)
+				dbus_message_iter_get_basic(&value, &domain);
+			break;
+		case DBUS_TYPE_ARRAY:
+			if (g_str_equal(key, "Networks") == TRUE)
+				networks = get_user_networks(&value, &count);
+			break;
+		}
+
+		dbus_message_iter_next(&array);
+	}
+
+	if (host == NULL || domain == NULL)
+		return -EINVAL;
+
+	DBG("Type %s name %s networks %p", type, name, networks);
+
+	if (type == NULL || name == NULL)
+		return -EOPNOTSUPP;
+
+	ident = g_strdup_printf("%s_%s", host, domain);
+	provider_dbus_ident(ident);
+
+	DBG("ident %s", ident);
+
+	provider = vpn_provider_lookup(ident);
+	if (provider == NULL) {
+		provider = vpn_provider_get(ident);
+		if (provider == NULL) {
+			DBG("can not create provider");
+			g_free(ident);
+			return -EOPNOTSUPP;
+		}
+
+		provider->host = g_strdup(host);
+		provider->domain = g_strdup(domain);
+		provider->name = g_strdup(name);
+		provider->type = g_strdup(type);
+
+		if (provider_register(provider) == 0)
+			vpn_provider_load(provider);
+
+		provider_resolv_host_addr(provider);
+	}
+
+	if (networks != NULL) {
+		g_strfreev(provider->user_networks);
+		provider->user_networks = networks;
+		provider->num_user_networks = count;
+		set_user_networks(provider, provider->user_networks);
+	}
+
+	dbus_message_iter_init(msg, &iter);
+	dbus_message_iter_recurse(&iter, &array);
+
+	while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
+		DBusMessageIter entry, value;
+		const char *key, *str;
+
+		dbus_message_iter_recurse(&array, &entry);
+		dbus_message_iter_get_basic(&entry, &key);
+
+		dbus_message_iter_next(&entry);
+		dbus_message_iter_recurse(&entry, &value);
+
+		switch (dbus_message_iter_get_arg_type(&value)) {
+		case DBUS_TYPE_STRING:
+			dbus_message_iter_get_basic(&value, &str);
+			vpn_provider_set_string(provider, key, str);
+			break;
+		}
+
+		dbus_message_iter_next(&array);
+	}
+
+	g_free(ident);
+
+	provider->pending_msg = dbus_message_ref(msg);
+
+	DBG("provider %p pending %p", provider, provider->pending_msg);
+
+	if (provider->index > 0) {
+		DBG("provider already connected");
+	} else {
+		err = __vpn_provider_connect(provider);
+		if (err < 0 && err != -EINPROGRESS)
+			goto failed;
+	}
+
+	vpn_provider_save(provider);
+
+	return 0;
+
+failed:
+	DBG("Can not connect (%d), deleting provider %p %s", err, provider,
+		provider->identifier);
+
+	vpn_provider_indicate_error(provider,
+				VPN_PROVIDER_ERROR_CONNECT_FAILED);
+
+	g_hash_table_remove(provider_hash, provider->identifier);
+
+	return err;
+}
+
+static void append_connection_structs(DBusMessageIter *iter, void *user_data)
+{
+	DBusMessageIter entry;
+	GHashTableIter hash;
+	gpointer value, key;
+
+	g_hash_table_iter_init(&hash, provider_hash);
+
+	while (g_hash_table_iter_next(&hash, &key, &value) == TRUE) {
+		struct vpn_provider *provider = value;
+
+		DBG("path %s", provider->path);
+
+		if (provider->identifier == NULL)
+			continue;
+
+		dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
+				NULL, &entry);
+		dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
+				&provider->path);
+		append_properties(&entry, provider);
+		dbus_message_iter_close_container(iter, &entry);
+	}
+}
+
+DBusMessage *__vpn_provider_get_connections(DBusMessage *msg)
+{
+	DBusMessage *reply;
+
+	DBG("");
+
+	reply = dbus_message_new_method_return(msg);
+	if (reply == NULL)
+		return NULL;
+
+	__connman_dbus_append_objpath_dict_array(reply,
+			append_connection_structs, NULL);
+
+	return reply;
+}
+
+const char * __vpn_provider_get_ident(struct vpn_provider *provider)
+{
+	if (provider == NULL)
+		return NULL;
+
+	return provider->identifier;
+}
+
+int vpn_provider_set_string(struct vpn_provider *provider,
+					const char *key, const char *value)
+{
+	DBG("provider %p key %s value %s", provider, key, value);
+
+	if (g_str_equal(key, "Type") == TRUE) {
+		g_free(provider->type);
+		provider->type = g_strdup(value);
+	} else if (g_str_equal(key, "Name") == TRUE) {
+		g_free(provider->name);
+		provider->name = g_strdup(value);
+	} else if (g_str_equal(key, "Host") == TRUE) {
+		g_free(provider->host);
+		provider->host = g_strdup(value);
+	} else if (g_str_equal(key, "VPN.Domain") == TRUE) {
+		g_free(provider->domain);
+		provider->domain = g_strdup(value);
+	} else
+		g_hash_table_replace(provider->setting_strings,
+				g_strdup(key), g_strdup(value));
+	return 0;
+}
+
+const char *vpn_provider_get_string(struct vpn_provider *provider,
+							const char *key)
+{
+	DBG("provider %p key %s", provider, key);
+
+	if (g_str_equal(key, "Type") == TRUE)
+		return provider->type;
+	else if (g_str_equal(key, "Name") == TRUE)
+		return provider->name;
+	else if (g_str_equal(key, "Host") == TRUE)
+		return provider->host;
+	else if (g_str_equal(key, "VPN.Domain") == TRUE)
+		return provider->domain;
+
+	return g_hash_table_lookup(provider->setting_strings, key);
+}
+
+connman_bool_t __vpn_provider_check_routes(struct vpn_provider *provider)
+{
+	if (provider == NULL)
+		return FALSE;
+
+	if (provider->user_routes != NULL &&
+			g_hash_table_size(provider->user_routes) > 0)
+		return TRUE;
+
+	if (provider->routes != NULL &&
+			g_hash_table_size(provider->routes) > 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+void *vpn_provider_get_data(struct vpn_provider *provider)
+{
+	return provider->driver_data;
+}
+
+void vpn_provider_set_data(struct vpn_provider *provider, void *data)
+{
+	provider->driver_data = data;
+}
+
+void vpn_provider_set_index(struct vpn_provider *provider, int index)
+{
+	DBG("index %d provider %p pending %p", index, provider,
+		provider->pending_msg);
+
+	if (provider->pending_msg != NULL) {
+		g_dbus_send_reply(connection, provider->pending_msg,
+				DBUS_TYPE_STRING, &provider->identifier,
+				DBUS_TYPE_INT32, &index,
+				DBUS_TYPE_INVALID);
+		dbus_message_unref(provider->pending_msg);
+		provider->pending_msg = NULL;
+	}
+
+	if (provider->ipconfig_ipv4 == NULL) {
+		provider->ipconfig_ipv4 = __vpn_ipconfig_create(index,
+								AF_INET);
+		if (provider->ipconfig_ipv4 == NULL) {
+			DBG("Couldnt create ipconfig for IPv4");
+			goto done;
+		}
+	}
+
+	__vpn_ipconfig_set_index(provider->ipconfig_ipv4, index);
+
+	if (provider->ipconfig_ipv6 == NULL) {
+		provider->ipconfig_ipv6 = __vpn_ipconfig_create(index,
+								AF_INET6);
+		if (provider->ipconfig_ipv6 == NULL) {
+			DBG("Couldnt create ipconfig for IPv6");
+			goto done;
+		}
+	}
+
+	__vpn_ipconfig_set_index(provider->ipconfig_ipv6, index);
+
+done:
+	provider->index = index;
+}
+
+int vpn_provider_get_index(struct vpn_provider *provider)
+{
+	return provider->index;
+}
+
+int vpn_provider_set_ipaddress(struct vpn_provider *provider,
+					struct connman_ipaddress *ipaddress)
+{
+	struct vpn_ipconfig *ipconfig = NULL;
+
+	switch (ipaddress->family) {
+	case AF_INET:
+		ipconfig = provider->ipconfig_ipv4;
+		break;
+	case AF_INET6:
+		ipconfig = provider->ipconfig_ipv6;
+		break;
+	default:
+		break;
+	}
+
+	DBG("provider %p ipconfig %p family %d", provider, ipconfig,
+							ipaddress->family);
+
+	if (ipconfig == NULL)
+		return -EINVAL;
+
+	provider->family = ipaddress->family;
+
+	__vpn_ipconfig_set_local(ipconfig, ipaddress->local);
+	__vpn_ipconfig_set_peer(ipconfig, ipaddress->peer);
+	__vpn_ipconfig_set_broadcast(ipconfig, ipaddress->broadcast);
+	__vpn_ipconfig_set_gateway(ipconfig, ipaddress->gateway);
+	__vpn_ipconfig_set_prefixlen(ipconfig, ipaddress->prefixlen);
+
+	return 0;
+}
+
+int vpn_provider_set_pac(struct vpn_provider *provider,
+				const char *pac)
+{
+	DBG("provider %p pac %s", provider, pac);
+
+	return 0;
+}
+
+
+int vpn_provider_set_domain(struct vpn_provider *provider,
+					const char *domain)
+{
+	DBG("provider %p domain %s", provider, domain);
+
+	g_free(provider->domain);
+	provider->domain = g_strdup(domain);
+
+	return 0;
+}
+
+int vpn_provider_set_nameservers(struct vpn_provider *provider,
+					const char *nameservers)
+{
+	DBG("provider %p nameservers %s", provider, nameservers);
+
+	g_strfreev(provider->nameservers);
+	provider->nameservers = NULL;
+
+	if (nameservers == NULL)
+		return 0;
+
+	provider->nameservers = g_strsplit(nameservers, " ", 0);
+
+	return 0;
+}
+
+enum provider_route_type {
+	PROVIDER_ROUTE_TYPE_NONE = 0,
+	PROVIDER_ROUTE_TYPE_MASK = 1,
+	PROVIDER_ROUTE_TYPE_ADDR = 2,
+	PROVIDER_ROUTE_TYPE_GW   = 3,
+};
+
+static int route_env_parse(struct vpn_provider *provider, const char *key,
+				int *family, unsigned long *idx,
+				enum provider_route_type *type)
+{
+	char *end;
+	const char *start;
+
+	DBG("name %s", provider->name);
+
+	if (!strcmp(provider->type, "openvpn")) {
+		if (g_str_has_prefix(key, "route_network_") == TRUE) {
+			start = key + strlen("route_network_");
+			*type = PROVIDER_ROUTE_TYPE_ADDR;
+		} else if (g_str_has_prefix(key, "route_netmask_") == TRUE) {
+			start = key + strlen("route_netmask_");
+			*type = PROVIDER_ROUTE_TYPE_MASK;
+		} else if (g_str_has_prefix(key, "route_gateway_") == TRUE) {
+			start = key + strlen("route_gateway_");
+			*type = PROVIDER_ROUTE_TYPE_GW;
+		} else
+			return -EINVAL;
+
+		*family = AF_INET;
+		*idx = g_ascii_strtoull(start, &end, 10);
+
+	} else if (!strcmp(provider->type, "openconnect")) {
+		if (g_str_has_prefix(key, "CISCO_SPLIT_INC_") == TRUE) {
+			*family = AF_INET;
+			start = key + strlen("CISCO_SPLIT_INC_");
+		} else if (g_str_has_prefix(key,
+					"CISCO_IPV6_SPLIT_INC_") == TRUE) {
+			*family = AF_INET6;
+			start = key + strlen("CISCO_IPV6_SPLIT_INC_");
+		} else
+			return -EINVAL;
+
+		*idx = g_ascii_strtoull(start, &end, 10);
+
+		if (strncmp(end, "_ADDR", 5) == 0)
+			*type = PROVIDER_ROUTE_TYPE_ADDR;
+		else if (strncmp(end, "_MASK", 5) == 0)
+			*type = PROVIDER_ROUTE_TYPE_MASK;
+		else if (strncmp(end, "_MASKLEN", 8) == 0 &&
+				*family == AF_INET6) {
+			*type = PROVIDER_ROUTE_TYPE_MASK;
+		} else
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+int vpn_provider_append_route(struct vpn_provider *provider,
+					const char *key, const char *value)
+{
+	struct vpn_route *route;
+	int ret, family = 0;
+	unsigned long idx = 0;
+	enum provider_route_type type = PROVIDER_ROUTE_TYPE_NONE;
+
+	DBG("key %s value %s", key, value);
+
+	ret = route_env_parse(provider, key, &family, &idx, &type);
+	if (ret < 0)
+		return ret;
+
+	DBG("idx %lu family %d type %d", idx, family, type);
+
+	route = g_hash_table_lookup(provider->routes, GINT_TO_POINTER(idx));
+	if (route == NULL) {
+		route = g_try_new0(struct vpn_route, 1);
+		if (route == NULL) {
+			connman_error("out of memory");
+			return -ENOMEM;
+		}
+
+		route->family = family;
+
+		g_hash_table_replace(provider->routes, GINT_TO_POINTER(idx),
+						route);
+	}
+
+	switch (type) {
+	case PROVIDER_ROUTE_TYPE_NONE:
+		break;
+	case PROVIDER_ROUTE_TYPE_MASK:
+		route->netmask = g_strdup(value);
+		break;
+	case PROVIDER_ROUTE_TYPE_ADDR:
+		route->host = g_strdup(value);
+		break;
+	case PROVIDER_ROUTE_TYPE_GW:
+		route->gateway = g_strdup(value);
+		break;
+	}
+
+	return 0;
+}
+
+const char *vpn_provider_get_driver_name(struct vpn_provider *provider)
+{
+	if (provider->driver == NULL)
+		return NULL;
+
+	return provider->driver->name;
+}
+
+const char *vpn_provider_get_save_group(struct vpn_provider *provider)
+{
+	return provider->identifier;
+}
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+	return 0;
+}
+
+static void clean_provider(gpointer key, gpointer value, gpointer user_data)
+{
+	struct vpn_provider *provider = value;
+
+	if (provider->driver != NULL && provider->driver->remove)
+		provider->driver->remove(provider);
+
+	connection_unregister(provider);
+}
+
+int vpn_provider_driver_register(struct vpn_provider_driver *driver)
+{
+	DBG("driver %p name %s", driver, driver->name);
+
+	driver_list = g_slist_insert_sorted(driver_list, driver,
+							compare_priority);
+	provider_create_all_from_type(driver->name);
+	return 0;
+}
+
+void vpn_provider_driver_unregister(struct vpn_provider_driver *driver)
+{
+	DBG("driver %p name %s", driver, driver->name);
+
+	driver_list = g_slist_remove(driver_list, driver);
+}
+
+int __vpn_provider_init(void)
+{
+	DBG("");
+
+	connection = connman_dbus_get_connection();
+
+	provider_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+						NULL, unregister_provider);
+
+	return 0;
+}
+
+void __vpn_provider_cleanup(void)
+{
+	DBG("");
+
+	g_hash_table_foreach(provider_hash, clean_provider, NULL);
+
+	g_hash_table_destroy(provider_hash);
+	provider_hash = NULL;
+
+	dbus_connection_unref(connection);
+}
diff --git a/vpn/vpn-provider.h b/vpn/vpn-provider.h
new file mode 100644
index 0000000..0f139b0
--- /dev/null
+++ b/vpn/vpn-provider.h
@@ -0,0 +1,121 @@
+/*
+ *
+ *  ConnMan VPN daemon
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __VPN_PROVIDER_H
+#define __VPN_PROVIDER_H
+
+#include <glib.h>
+#include <connman/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:provider
+ * @title: Provider premitives
+ * @short_description: Functions for handling providers
+ */
+
+enum vpn_provider_type {
+	VPN_PROVIDER_TYPE_UNKNOWN = 0,
+	VPN_PROVIDER_TYPE_VPN     = 1,
+};
+
+enum vpn_provider_state {
+	VPN_PROVIDER_STATE_UNKNOWN       = 0,
+	VPN_PROVIDER_STATE_IDLE          = 1,
+	VPN_PROVIDER_STATE_CONNECT       = 2,
+	VPN_PROVIDER_STATE_READY         = 3,
+	VPN_PROVIDER_STATE_DISCONNECT    = 4,
+	VPN_PROVIDER_STATE_FAILURE       = 5,
+};
+
+enum vpn_provider_error {
+	VPN_PROVIDER_ERROR_UNKNOWN		= 0,
+	VPN_PROVIDER_ERROR_CONNECT_FAILED	= 1,
+	VPN_PROVIDER_ERROR_LOGIN_FAILED	= 2,
+	VPN_PROVIDER_ERROR_AUTH_FAILED	= 3,
+};
+
+struct vpn_provider;
+struct connman_ipaddress;
+
+#define vpn_provider_ref(provider) \
+	vpn_provider_ref_debug(provider, __FILE__, __LINE__, __func__)
+
+#define vpn_provider_unref(provider) \
+	vpn_provider_unref_debug(provider, __FILE__, __LINE__, __func__)
+
+struct vpn_provider *
+vpn_provider_ref_debug(struct vpn_provider *provider,
+			const char *file, int line, const char *caller);
+void vpn_provider_unref_debug(struct vpn_provider *provider,
+			const char *file, int line, const char *caller);
+
+int vpn_provider_set_string(struct vpn_provider *provider,
+					const char *key, const char *value);
+const char *vpn_provider_get_string(struct vpn_provider *provider,
+							const char *key);
+
+int vpn_provider_set_state(struct vpn_provider *provider,
+					enum vpn_provider_state state);
+
+int vpn_provider_indicate_error(struct vpn_provider *provider,
+					enum vpn_provider_error error);
+
+void vpn_provider_set_index(struct vpn_provider *provider, int index);
+int vpn_provider_get_index(struct vpn_provider *provider);
+
+void vpn_provider_set_data(struct vpn_provider *provider, void *data);
+void *vpn_provider_get_data(struct vpn_provider *provider);
+int vpn_provider_set_ipaddress(struct vpn_provider *provider,
+					struct connman_ipaddress *ipaddress);
+int vpn_provider_set_pac(struct vpn_provider *provider,
+				const char *pac);
+int vpn_provider_set_domain(struct vpn_provider *provider,
+				const char *domain);
+int vpn_provider_set_nameservers(struct vpn_provider *provider,
+					const char *nameservers);
+int vpn_provider_append_route(struct vpn_provider *provider,
+					const char *key, const char *value);
+
+const char *vpn_provider_get_driver_name(struct vpn_provider *provider);
+const char *vpn_provider_get_save_group(struct vpn_provider *provider);
+
+struct vpn_provider_driver {
+	const char *name;
+	enum vpn_provider_type type;
+	int (*probe) (struct vpn_provider *provider);
+	int (*remove) (struct vpn_provider *provider);
+	int (*connect) (struct vpn_provider *provider);
+	int (*disconnect) (struct vpn_provider *provider);
+	int (*save) (struct vpn_provider *provider, GKeyFile *keyfile);
+};
+
+int vpn_provider_driver_register(struct vpn_provider_driver *driver);
+void vpn_provider_driver_unregister(struct vpn_provider_driver *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VPN_PROVIDER_H */
diff --git a/vpn/vpn-rtnl.c b/vpn/vpn-rtnl.c
new file mode 100644
index 0000000..5ce14e4
--- /dev/null
+++ b/vpn/vpn-rtnl.c
@@ -0,0 +1,1185 @@
+/*
+ *
+ *  ConnMan VPN daemon
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <netinet/ether.h>
+#include <netinet/icmp6.h>
+#include <net/if_arp.h>
+#include <linux/if.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/wireless.h>
+
+#include <glib.h>
+
+#include <connman/log.h>
+
+#include "vpn.h"
+
+#include "vpn-rtnl.h"
+
+#ifndef ARPHDR_PHONET_PIPE
+#define ARPHDR_PHONET_PIPE (821)
+#endif
+
+#define print(arg...) do { if (0) connman_info(arg); } while (0)
+#define debug(arg...) do { if (0) DBG(arg); } while (0)
+
+struct watch_data {
+	unsigned int id;
+	int index;
+	vpn_rtnl_link_cb_t newlink;
+	void *user_data;
+};
+
+static GSList *watch_list = NULL;
+static unsigned int watch_id = 0;
+
+static GSList *update_list = NULL;
+static guint update_interval = G_MAXUINT;
+static guint update_timeout = 0;
+
+struct interface_data {
+	int index;
+	char *name;
+	char *ident;
+};
+
+static GHashTable *interface_list = NULL;
+
+static void free_interface(gpointer data)
+{
+	struct interface_data *interface = data;
+
+	g_free(interface->ident);
+	g_free(interface->name);
+	g_free(interface);
+}
+
+/**
+ * vpn_rtnl_add_newlink_watch:
+ * @index: network device index
+ * @callback: callback function
+ * @user_data: callback data;
+ *
+ * Add a new RTNL watch for newlink events
+ *
+ * Returns: %0 on failure and a unique id on success
+ */
+unsigned int vpn_rtnl_add_newlink_watch(int index,
+			vpn_rtnl_link_cb_t callback, void *user_data)
+{
+	struct watch_data *watch;
+
+	watch = g_try_new0(struct watch_data, 1);
+	if (watch == NULL)
+		return 0;
+
+	watch->id = ++watch_id;
+	watch->index = index;
+
+	watch->newlink = callback;
+	watch->user_data = user_data;
+
+	watch_list = g_slist_prepend(watch_list, watch);
+
+	DBG("id %d", watch->id);
+
+	if (callback) {
+		unsigned int flags = __vpn_ipconfig_get_flags_from_index(index);
+
+		if (flags > 0)
+			callback(flags, 0, user_data);
+	}
+
+	return watch->id;
+}
+
+/**
+ * vpn_rtnl_remove_watch:
+ * @id: watch identifier
+ *
+ * Remove the RTNL watch for the identifier
+ */
+void vpn_rtnl_remove_watch(unsigned int id)
+{
+	GSList *list;
+
+	DBG("id %d", id);
+
+	if (id == 0)
+		return;
+
+	for (list = watch_list; list; list = list->next) {
+		struct watch_data *watch = list->data;
+
+		if (watch->id  == id) {
+			watch_list = g_slist_remove(watch_list, watch);
+			g_free(watch);
+			break;
+		}
+	}
+}
+
+static void trigger_rtnl(int index, void *user_data)
+{
+	struct vpn_rtnl *rtnl = user_data;
+
+	if (rtnl->newlink) {
+		unsigned short type = __vpn_ipconfig_get_type_from_index(index);
+		unsigned int flags = __vpn_ipconfig_get_flags_from_index(index);
+
+		rtnl->newlink(type, index, flags, 0);
+	}
+}
+
+static GSList *rtnl_list = NULL;
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+	const struct vpn_rtnl *rtnl1 = a;
+	const struct vpn_rtnl *rtnl2 = b;
+
+	return rtnl2->priority - rtnl1->priority;
+}
+
+/**
+ * vpn_rtnl_register:
+ * @rtnl: RTNL module
+ *
+ * Register a new RTNL module
+ *
+ * Returns: %0 on success
+ */
+int vpn_rtnl_register(struct vpn_rtnl *rtnl)
+{
+	DBG("rtnl %p name %s", rtnl, rtnl->name);
+
+	rtnl_list = g_slist_insert_sorted(rtnl_list, rtnl,
+							compare_priority);
+
+	__vpn_ipconfig_foreach(trigger_rtnl, rtnl);
+
+	return 0;
+}
+
+/**
+ * vpn_rtnl_unregister:
+ * @rtnl: RTNL module
+ *
+ * Remove a previously registered RTNL module
+ */
+void vpn_rtnl_unregister(struct vpn_rtnl *rtnl)
+{
+	DBG("rtnl %p name %s", rtnl, rtnl->name);
+
+	rtnl_list = g_slist_remove(rtnl_list, rtnl);
+}
+
+static const char *operstate2str(unsigned char operstate)
+{
+	switch (operstate) {
+	case IF_OPER_UNKNOWN:
+		return "UNKNOWN";
+	case IF_OPER_NOTPRESENT:
+		return "NOT-PRESENT";
+	case IF_OPER_DOWN:
+		return "DOWN";
+	case IF_OPER_LOWERLAYERDOWN:
+		return "LOWER-LAYER-DOWN";
+	case IF_OPER_TESTING:
+		return "TESTING";
+	case IF_OPER_DORMANT:
+		return "DORMANT";
+	case IF_OPER_UP:
+		return "UP";
+	}
+
+	return "";
+}
+
+static void extract_link(struct ifinfomsg *msg, int bytes,
+				struct ether_addr *address, const char **ifname,
+				unsigned int *mtu, unsigned char *operstate,
+						struct rtnl_link_stats *stats)
+{
+	struct rtattr *attr;
+
+	for (attr = IFLA_RTA(msg); RTA_OK(attr, bytes);
+					attr = RTA_NEXT(attr, bytes)) {
+		switch (attr->rta_type) {
+		case IFLA_ADDRESS:
+			if (address != NULL)
+				memcpy(address, RTA_DATA(attr), ETH_ALEN);
+			break;
+		case IFLA_IFNAME:
+			if (ifname != NULL)
+				*ifname = RTA_DATA(attr);
+			break;
+		case IFLA_MTU:
+			if (mtu != NULL)
+				*mtu = *((unsigned int *) RTA_DATA(attr));
+			break;
+		case IFLA_STATS:
+			if (stats != NULL)
+				memcpy(stats, RTA_DATA(attr),
+					sizeof(struct rtnl_link_stats));
+			break;
+		case IFLA_OPERSTATE:
+			if (operstate != NULL)
+				*operstate = *((unsigned char *) RTA_DATA(attr));
+			break;
+		case IFLA_LINKMODE:
+			break;
+		}
+	}
+}
+
+static void process_newlink(unsigned short type, int index, unsigned flags,
+			unsigned change, struct ifinfomsg *msg, int bytes)
+{
+	struct ether_addr address = {{ 0, 0, 0, 0, 0, 0 }};
+	struct ether_addr compare = {{ 0, 0, 0, 0, 0, 0 }};
+	struct rtnl_link_stats stats;
+	unsigned char operstate = 0xff;
+	struct interface_data *interface;
+	const char *ifname = NULL;
+	unsigned int mtu = 0;
+	char ident[13], str[18];
+	GSList *list;
+
+	memset(&stats, 0, sizeof(stats));
+	extract_link(msg, bytes, &address, &ifname, &mtu, &operstate, &stats);
+
+	snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
+						address.ether_addr_octet[0],
+						address.ether_addr_octet[1],
+						address.ether_addr_octet[2],
+						address.ether_addr_octet[3],
+						address.ether_addr_octet[4],
+						address.ether_addr_octet[5]);
+
+	snprintf(str, 18, "%02X:%02X:%02X:%02X:%02X:%02X",
+						address.ether_addr_octet[0],
+						address.ether_addr_octet[1],
+						address.ether_addr_octet[2],
+						address.ether_addr_octet[3],
+						address.ether_addr_octet[4],
+						address.ether_addr_octet[5]);
+
+	switch (type) {
+	case ARPHRD_ETHER:
+	case ARPHRD_LOOPBACK:
+	case ARPHDR_PHONET_PIPE:
+	case ARPHRD_NONE:
+		__vpn_ipconfig_newlink(index, type, flags,
+							str, mtu, &stats);
+		break;
+	}
+
+	if (memcmp(&address, &compare, ETH_ALEN) != 0)
+		connman_info("%s {newlink} index %d address %s mtu %u",
+						ifname, index, str, mtu);
+
+	if (operstate != 0xff)
+		connman_info("%s {newlink} index %d operstate %u <%s>",
+						ifname, index, operstate,
+						operstate2str(operstate));
+
+	interface = g_hash_table_lookup(interface_list, GINT_TO_POINTER(index));
+	if (interface == NULL) {
+		interface = g_new0(struct interface_data, 1);
+		interface->index = index;
+		interface->name = g_strdup(ifname);
+		interface->ident = g_strdup(ident);
+
+		g_hash_table_insert(interface_list,
+					GINT_TO_POINTER(index), interface);
+	}
+
+	for (list = rtnl_list; list; list = list->next) {
+		struct vpn_rtnl *rtnl = list->data;
+
+		if (rtnl->newlink)
+			rtnl->newlink(type, index, flags, change);
+	}
+
+	for (list = watch_list; list; list = list->next) {
+		struct watch_data *watch = list->data;
+
+		if (watch->index != index)
+			continue;
+
+		if (watch->newlink)
+			watch->newlink(flags, change, watch->user_data);
+	}
+}
+
+static void process_dellink(unsigned short type, int index, unsigned flags,
+			unsigned change, struct ifinfomsg *msg, int bytes)
+{
+	struct rtnl_link_stats stats;
+	unsigned char operstate = 0xff;
+	const char *ifname = NULL;
+	GSList *list;
+
+	memset(&stats, 0, sizeof(stats));
+	extract_link(msg, bytes, NULL, &ifname, NULL, &operstate, &stats);
+
+	if (operstate != 0xff)
+		connman_info("%s {dellink} index %d operstate %u <%s>",
+						ifname, index, operstate,
+						operstate2str(operstate));
+
+	for (list = rtnl_list; list; list = list->next) {
+		struct vpn_rtnl *rtnl = list->data;
+
+		if (rtnl->dellink)
+			rtnl->dellink(type, index, flags, change);
+	}
+
+	switch (type) {
+	case ARPHRD_ETHER:
+	case ARPHRD_LOOPBACK:
+	case ARPHRD_NONE:
+		__vpn_ipconfig_dellink(index, &stats);
+		break;
+	}
+
+	g_hash_table_remove(interface_list, GINT_TO_POINTER(index));
+}
+
+static void extract_ipv4_route(struct rtmsg *msg, int bytes, int *index,
+						struct in_addr *dst,
+						struct in_addr *gateway)
+{
+	struct rtattr *attr;
+
+	for (attr = RTM_RTA(msg); RTA_OK(attr, bytes);
+					attr = RTA_NEXT(attr, bytes)) {
+		switch (attr->rta_type) {
+		case RTA_DST:
+			if (dst != NULL)
+				*dst = *((struct in_addr *) RTA_DATA(attr));
+			break;
+		case RTA_GATEWAY:
+			if (gateway != NULL)
+				*gateway = *((struct in_addr *) RTA_DATA(attr));
+			break;
+		case RTA_OIF:
+			if (index != NULL)
+				*index = *((int *) RTA_DATA(attr));
+			break;
+		}
+	}
+}
+
+static void extract_ipv6_route(struct rtmsg *msg, int bytes, int *index,
+						struct in6_addr *dst,
+						struct in6_addr *gateway)
+{
+	struct rtattr *attr;
+
+	for (attr = RTM_RTA(msg); RTA_OK(attr, bytes);
+					attr = RTA_NEXT(attr, bytes)) {
+		switch (attr->rta_type) {
+		case RTA_DST:
+			if (dst != NULL)
+				*dst = *((struct in6_addr *) RTA_DATA(attr));
+			break;
+		case RTA_GATEWAY:
+			if (gateway != NULL)
+				*gateway =
+					*((struct in6_addr *) RTA_DATA(attr));
+			break;
+		case RTA_OIF:
+			if (index != NULL)
+				*index = *((int *) RTA_DATA(attr));
+			break;
+		}
+	}
+}
+
+static void process_newroute(unsigned char family, unsigned char scope,
+						struct rtmsg *msg, int bytes)
+{
+	GSList *list;
+	char dststr[INET6_ADDRSTRLEN], gatewaystr[INET6_ADDRSTRLEN];
+	int index = -1;
+
+	if (family == AF_INET) {
+		struct in_addr dst = { INADDR_ANY }, gateway = { INADDR_ANY };
+
+		extract_ipv4_route(msg, bytes, &index, &dst, &gateway);
+
+		inet_ntop(family, &dst, dststr, sizeof(dststr));
+		inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
+
+		/* skip host specific routes */
+		if (scope != RT_SCOPE_UNIVERSE &&
+			!(scope == RT_SCOPE_LINK && dst.s_addr == INADDR_ANY))
+			return;
+
+		if (dst.s_addr != INADDR_ANY)
+			return;
+
+	} else if (family == AF_INET6) {
+		struct in6_addr dst = IN6ADDR_ANY_INIT,
+				gateway = IN6ADDR_ANY_INIT;
+
+		extract_ipv6_route(msg, bytes, &index, &dst, &gateway);
+
+		inet_ntop(family, &dst, dststr, sizeof(dststr));
+		inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
+
+		/* skip host specific routes */
+		if (scope != RT_SCOPE_UNIVERSE &&
+			!(scope == RT_SCOPE_LINK &&
+				IN6_IS_ADDR_UNSPECIFIED(&dst)))
+			return;
+
+		if (!IN6_IS_ADDR_UNSPECIFIED(&dst))
+			return;
+	} else
+		return;
+
+	for (list = rtnl_list; list; list = list->next) {
+		struct vpn_rtnl *rtnl = list->data;
+
+		if (rtnl->newgateway)
+			rtnl->newgateway(index, gatewaystr);
+	}
+}
+
+static void process_delroute(unsigned char family, unsigned char scope,
+						struct rtmsg *msg, int bytes)
+{
+	GSList *list;
+	char dststr[INET6_ADDRSTRLEN], gatewaystr[INET6_ADDRSTRLEN];
+	int index = -1;
+
+	if (family == AF_INET) {
+		struct in_addr dst = { INADDR_ANY }, gateway = { INADDR_ANY };
+
+		extract_ipv4_route(msg, bytes, &index, &dst, &gateway);
+
+		inet_ntop(family, &dst, dststr, sizeof(dststr));
+		inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
+
+		/* skip host specific routes */
+		if (scope != RT_SCOPE_UNIVERSE &&
+			!(scope == RT_SCOPE_LINK && dst.s_addr == INADDR_ANY))
+			return;
+
+		if (dst.s_addr != INADDR_ANY)
+			return;
+
+	}  else if (family == AF_INET6) {
+		struct in6_addr dst = IN6ADDR_ANY_INIT,
+				gateway = IN6ADDR_ANY_INIT;
+
+		extract_ipv6_route(msg, bytes, &index, &dst, &gateway);
+
+		inet_ntop(family, &dst, dststr, sizeof(dststr));
+		inet_ntop(family, &gateway, gatewaystr, sizeof(gatewaystr));
+
+		/* skip host specific routes */
+		if (scope != RT_SCOPE_UNIVERSE &&
+			!(scope == RT_SCOPE_LINK &&
+				IN6_IS_ADDR_UNSPECIFIED(&dst)))
+			return;
+
+		if (!IN6_IS_ADDR_UNSPECIFIED(&dst))
+			return;
+	} else
+		return;
+
+	for (list = rtnl_list; list; list = list->next) {
+		struct vpn_rtnl *rtnl = list->data;
+
+		if (rtnl->delgateway)
+			rtnl->delgateway(index, gatewaystr);
+	}
+}
+
+static inline void print_ether(struct rtattr *attr, const char *name)
+{
+	int len = (int) RTA_PAYLOAD(attr);
+
+	if (len == ETH_ALEN) {
+		struct ether_addr eth;
+		memcpy(&eth, RTA_DATA(attr), ETH_ALEN);
+		print("  attr %s (len %d) %s\n", name, len, ether_ntoa(&eth));
+	} else
+		print("  attr %s (len %d)\n", name, len);
+}
+
+static inline void print_inet(struct rtattr *attr, const char *name,
+							unsigned char family)
+{
+	int len = (int) RTA_PAYLOAD(attr);
+
+	if (family == AF_INET && len == sizeof(struct in_addr)) {
+		struct in_addr addr;
+		addr = *((struct in_addr *) RTA_DATA(attr));
+		print("  attr %s (len %d) %s\n", name, len, inet_ntoa(addr));
+	} else
+		print("  attr %s (len %d)\n", name, len);
+}
+
+static inline void print_string(struct rtattr *attr, const char *name)
+{
+	print("  attr %s (len %d) %s\n", name, (int) RTA_PAYLOAD(attr),
+						(char *) RTA_DATA(attr));
+}
+
+static inline void print_byte(struct rtattr *attr, const char *name)
+{
+	print("  attr %s (len %d) 0x%02x\n", name, (int) RTA_PAYLOAD(attr),
+					*((unsigned char *) RTA_DATA(attr)));
+}
+
+static inline void print_integer(struct rtattr *attr, const char *name)
+{
+	print("  attr %s (len %d) %d\n", name, (int) RTA_PAYLOAD(attr),
+						*((int *) RTA_DATA(attr)));
+}
+
+static inline void print_attr(struct rtattr *attr, const char *name)
+{
+	int len = (int) RTA_PAYLOAD(attr);
+
+	if (name && len > 0)
+		print("  attr %s (len %d)\n", name, len);
+	else
+		print("  attr %d (len %d)\n", attr->rta_type, len);
+}
+
+static void rtnl_link(struct nlmsghdr *hdr)
+{
+	struct ifinfomsg *msg;
+	struct rtattr *attr;
+	int bytes;
+
+	msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
+	bytes = IFLA_PAYLOAD(hdr);
+
+	print("ifi_index %d ifi_flags 0x%04x", msg->ifi_index, msg->ifi_flags);
+
+	for (attr = IFLA_RTA(msg); RTA_OK(attr, bytes);
+					attr = RTA_NEXT(attr, bytes)) {
+		switch (attr->rta_type) {
+		case IFLA_ADDRESS:
+			print_ether(attr, "address");
+			break;
+		case IFLA_BROADCAST:
+			print_ether(attr, "broadcast");
+			break;
+		case IFLA_IFNAME:
+			print_string(attr, "ifname");
+			break;
+		case IFLA_MTU:
+			print_integer(attr, "mtu");
+			break;
+		case IFLA_LINK:
+			print_attr(attr, "link");
+			break;
+		case IFLA_QDISC:
+			print_attr(attr, "qdisc");
+			break;
+		case IFLA_STATS:
+			print_attr(attr, "stats");
+			break;
+		case IFLA_COST:
+			print_attr(attr, "cost");
+			break;
+		case IFLA_PRIORITY:
+			print_attr(attr, "priority");
+			break;
+		case IFLA_MASTER:
+			print_attr(attr, "master");
+			break;
+		case IFLA_WIRELESS:
+			print_attr(attr, "wireless");
+			break;
+		case IFLA_PROTINFO:
+			print_attr(attr, "protinfo");
+			break;
+		case IFLA_TXQLEN:
+			print_integer(attr, "txqlen");
+			break;
+		case IFLA_MAP:
+			print_attr(attr, "map");
+			break;
+		case IFLA_WEIGHT:
+			print_attr(attr, "weight");
+			break;
+		case IFLA_OPERSTATE:
+			print_byte(attr, "operstate");
+			break;
+		case IFLA_LINKMODE:
+			print_byte(attr, "linkmode");
+			break;
+		default:
+			print_attr(attr, NULL);
+			break;
+		}
+	}
+}
+
+static void rtnl_newlink(struct nlmsghdr *hdr)
+{
+	struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
+
+	rtnl_link(hdr);
+
+	process_newlink(msg->ifi_type, msg->ifi_index, msg->ifi_flags,
+				msg->ifi_change, msg, IFA_PAYLOAD(hdr));
+}
+
+static void rtnl_dellink(struct nlmsghdr *hdr)
+{
+	struct ifinfomsg *msg = (struct ifinfomsg *) NLMSG_DATA(hdr);
+
+	rtnl_link(hdr);
+
+	process_dellink(msg->ifi_type, msg->ifi_index, msg->ifi_flags,
+				msg->ifi_change, msg, IFA_PAYLOAD(hdr));
+}
+
+static void rtnl_route(struct nlmsghdr *hdr)
+{
+	struct rtmsg *msg;
+	struct rtattr *attr;
+	int bytes;
+
+	msg = (struct rtmsg *) NLMSG_DATA(hdr);
+	bytes = RTM_PAYLOAD(hdr);
+
+	print("rtm_family %d rtm_table %d rtm_protocol %d",
+			msg->rtm_family, msg->rtm_table, msg->rtm_protocol);
+	print("rtm_scope %d rtm_type %d rtm_flags 0x%04x",
+				msg->rtm_scope, msg->rtm_type, msg->rtm_flags);
+
+	for (attr = RTM_RTA(msg); RTA_OK(attr, bytes);
+					attr = RTA_NEXT(attr, bytes)) {
+		switch (attr->rta_type) {
+		case RTA_DST:
+			print_inet(attr, "dst", msg->rtm_family);
+			break;
+		case RTA_SRC:
+			print_inet(attr, "src", msg->rtm_family);
+			break;
+		case RTA_IIF:
+			print_string(attr, "iif");
+			break;
+		case RTA_OIF:
+			print_integer(attr, "oif");
+			break;
+		case RTA_GATEWAY:
+			print_inet(attr, "gateway", msg->rtm_family);
+			break;
+		case RTA_PRIORITY:
+			print_attr(attr, "priority");
+			break;
+		case RTA_PREFSRC:
+			print_inet(attr, "prefsrc", msg->rtm_family);
+			break;
+		case RTA_METRICS:
+			print_attr(attr, "metrics");
+			break;
+		case RTA_TABLE:
+			print_integer(attr, "table");
+			break;
+		default:
+			print_attr(attr, NULL);
+			break;
+		}
+	}
+}
+
+static connman_bool_t is_route_rtmsg(struct rtmsg *msg)
+{
+
+	if (msg->rtm_table != RT_TABLE_MAIN)
+		return FALSE;
+
+	if (msg->rtm_protocol != RTPROT_BOOT &&
+			msg->rtm_protocol != RTPROT_KERNEL)
+		return FALSE;
+
+	if (msg->rtm_type != RTN_UNICAST)
+		return FALSE;
+
+	return TRUE;
+}
+
+static void rtnl_newroute(struct nlmsghdr *hdr)
+{
+	struct rtmsg *msg = (struct rtmsg *) NLMSG_DATA(hdr);
+
+	rtnl_route(hdr);
+
+	if (is_route_rtmsg(msg))
+		process_newroute(msg->rtm_family, msg->rtm_scope,
+						msg, RTM_PAYLOAD(hdr));
+}
+
+static void rtnl_delroute(struct nlmsghdr *hdr)
+{
+	struct rtmsg *msg = (struct rtmsg *) NLMSG_DATA(hdr);
+
+	rtnl_route(hdr);
+
+	if (is_route_rtmsg(msg))
+		process_delroute(msg->rtm_family, msg->rtm_scope,
+						msg, RTM_PAYLOAD(hdr));
+}
+
+static const char *type2string(uint16_t type)
+{
+	switch (type) {
+	case NLMSG_NOOP:
+		return "NOOP";
+	case NLMSG_ERROR:
+		return "ERROR";
+	case NLMSG_DONE:
+		return "DONE";
+	case NLMSG_OVERRUN:
+		return "OVERRUN";
+	case RTM_GETLINK:
+		return "GETLINK";
+	case RTM_NEWLINK:
+		return "NEWLINK";
+	case RTM_DELLINK:
+		return "DELLINK";
+	case RTM_NEWADDR:
+		return "NEWADDR";
+	case RTM_DELADDR:
+		return "DELADDR";
+	case RTM_GETROUTE:
+		return "GETROUTE";
+	case RTM_NEWROUTE:
+		return "NEWROUTE";
+	case RTM_DELROUTE:
+		return "DELROUTE";
+	case RTM_NEWNDUSEROPT:
+		return "NEWNDUSEROPT";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static GIOChannel *channel = NULL;
+
+struct rtnl_request {
+	struct nlmsghdr hdr;
+	struct rtgenmsg msg;
+};
+#define RTNL_REQUEST_SIZE  (sizeof(struct nlmsghdr) + sizeof(struct rtgenmsg))
+
+static GSList *request_list = NULL;
+static guint32 request_seq = 0;
+
+static struct rtnl_request *find_request(guint32 seq)
+{
+	GSList *list;
+
+	for (list = request_list; list; list = list->next) {
+		struct rtnl_request *req = list->data;
+
+		if (req->hdr.nlmsg_seq == seq)
+			return req;
+	}
+
+	return NULL;
+}
+
+static int send_request(struct rtnl_request *req)
+{
+	struct sockaddr_nl addr;
+	int sk;
+
+	debug("%s len %d type %d flags 0x%04x seq %d",
+				type2string(req->hdr.nlmsg_type),
+				req->hdr.nlmsg_len, req->hdr.nlmsg_type,
+				req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+
+	sk = g_io_channel_unix_get_fd(channel);
+
+	memset(&addr, 0, sizeof(addr));
+	addr.nl_family = AF_NETLINK;
+
+	return sendto(sk, req, req->hdr.nlmsg_len, 0,
+				(struct sockaddr *) &addr, sizeof(addr));
+}
+
+static int queue_request(struct rtnl_request *req)
+{
+	request_list = g_slist_append(request_list, req);
+
+	if (g_slist_length(request_list) > 1)
+		return 0;
+
+	return send_request(req);
+}
+
+static int process_response(guint32 seq)
+{
+	struct rtnl_request *req;
+
+	debug("seq %d", seq);
+
+	req = find_request(seq);
+	if (req != NULL) {
+		request_list = g_slist_remove(request_list, req);
+		g_free(req);
+	}
+
+	req = g_slist_nth_data(request_list, 0);
+	if (req == NULL)
+		return 0;
+
+	return send_request(req);
+}
+
+static void rtnl_message(void *buf, size_t len)
+{
+	debug("buf %p len %zd", buf, len);
+
+	while (len > 0) {
+		struct nlmsghdr *hdr = buf;
+		struct nlmsgerr *err;
+
+		if (!NLMSG_OK(hdr, len))
+			break;
+
+		debug("%s len %d type %d flags 0x%04x seq %d pid %d",
+					type2string(hdr->nlmsg_type),
+					hdr->nlmsg_len, hdr->nlmsg_type,
+					hdr->nlmsg_flags, hdr->nlmsg_seq,
+					hdr->nlmsg_pid);
+
+		switch (hdr->nlmsg_type) {
+		case NLMSG_NOOP:
+		case NLMSG_OVERRUN:
+			return;
+		case NLMSG_DONE:
+			process_response(hdr->nlmsg_seq);
+			return;
+		case NLMSG_ERROR:
+			err = NLMSG_DATA(hdr);
+			DBG("error %d (%s)", -err->error,
+						strerror(-err->error));
+			return;
+		case RTM_NEWLINK:
+			rtnl_newlink(hdr);
+			break;
+		case RTM_DELLINK:
+			rtnl_dellink(hdr);
+			break;
+		case RTM_NEWADDR:
+			break;
+		case RTM_DELADDR:
+			break;
+		case RTM_NEWROUTE:
+			rtnl_newroute(hdr);
+			break;
+		case RTM_DELROUTE:
+			rtnl_delroute(hdr);
+			break;
+		}
+
+		len -= hdr->nlmsg_len;
+		buf += hdr->nlmsg_len;
+	}
+}
+
+static gboolean netlink_event(GIOChannel *chan,
+				GIOCondition cond, gpointer data)
+{
+	unsigned char buf[4096];
+	struct sockaddr_nl nladdr;
+	socklen_t addr_len = sizeof(nladdr);
+	ssize_t status;
+	int fd;
+
+	if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
+		return FALSE;
+
+	memset(buf, 0, sizeof(buf));
+	memset(&nladdr, 0, sizeof(nladdr));
+
+	fd = g_io_channel_unix_get_fd(chan);
+
+	status = recvfrom(fd, buf, sizeof(buf), 0,
+                       (struct sockaddr *) &nladdr, &addr_len);
+	if (status < 0) {
+		if (errno == EINTR || errno == EAGAIN)
+			return TRUE;
+
+		return FALSE;
+	}
+
+	if (status == 0)
+		return FALSE;
+
+	if (nladdr.nl_pid != 0) { /* not sent by kernel, ignore */
+		DBG("Received msg from %u, ignoring it", nladdr.nl_pid);
+		return TRUE;
+	}
+
+	rtnl_message(buf, status);
+
+	return TRUE;
+}
+
+static int send_getlink(void)
+{
+	struct rtnl_request *req;
+
+	debug("");
+
+	req = g_try_malloc0(RTNL_REQUEST_SIZE);
+	if (req == NULL)
+		return -ENOMEM;
+
+	req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
+	req->hdr.nlmsg_type = RTM_GETLINK;
+	req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+	req->hdr.nlmsg_pid = 0;
+	req->hdr.nlmsg_seq = request_seq++;
+	req->msg.rtgen_family = AF_INET;
+
+	return queue_request(req);
+}
+
+static int send_getaddr(void)
+{
+	struct rtnl_request *req;
+
+	debug("");
+
+	req = g_try_malloc0(RTNL_REQUEST_SIZE);
+	if (req == NULL)
+		return -ENOMEM;
+
+	req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
+	req->hdr.nlmsg_type = RTM_GETADDR;
+	req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+	req->hdr.nlmsg_pid = 0;
+	req->hdr.nlmsg_seq = request_seq++;
+	req->msg.rtgen_family = AF_INET;
+
+	return queue_request(req);
+}
+
+static int send_getroute(void)
+{
+	struct rtnl_request *req;
+
+	debug("");
+
+	req = g_try_malloc0(RTNL_REQUEST_SIZE);
+	if (req == NULL)
+		return -ENOMEM;
+
+	req->hdr.nlmsg_len = RTNL_REQUEST_SIZE;
+	req->hdr.nlmsg_type = RTM_GETROUTE;
+	req->hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+	req->hdr.nlmsg_pid = 0;
+	req->hdr.nlmsg_seq = request_seq++;
+	req->msg.rtgen_family = AF_INET;
+
+	return queue_request(req);
+}
+
+static gboolean update_timeout_cb(gpointer user_data)
+{
+	__vpn_rtnl_request_update();
+
+	return TRUE;
+}
+
+static void update_interval_callback(guint min)
+{
+	if (update_timeout > 0)
+		g_source_remove(update_timeout);
+
+	if (min < G_MAXUINT) {
+		update_interval = min;
+		update_timeout = g_timeout_add_seconds(update_interval,
+						update_timeout_cb, NULL);
+	} else {
+		update_timeout = 0;
+		update_interval = G_MAXUINT;
+	}
+}
+
+static gint compare_interval(gconstpointer a, gconstpointer b)
+{
+	guint val_a = GPOINTER_TO_UINT(a);
+	guint val_b = GPOINTER_TO_UINT(b);
+
+	return val_a - val_b;
+}
+
+unsigned int __vpn_rtnl_update_interval_add(unsigned int interval)
+{
+	guint min;
+
+	if (interval == 0)
+		return 0;
+
+	update_list = g_slist_insert_sorted(update_list,
+			GUINT_TO_POINTER(interval), compare_interval);
+
+	min = GPOINTER_TO_UINT(g_slist_nth_data(update_list, 0));
+	if (min < update_interval) {
+		update_interval_callback(min);
+		__vpn_rtnl_request_update();
+	}
+
+	return update_interval;
+}
+
+unsigned int __vpn_rtnl_update_interval_remove(unsigned int interval)
+{
+	guint min = G_MAXUINT;
+
+	if (interval == 0)
+		return 0;
+
+	update_list = g_slist_remove(update_list, GINT_TO_POINTER(interval));
+
+	if (update_list != NULL)
+		min = GPOINTER_TO_UINT(g_slist_nth_data(update_list, 0));
+
+	if (min > update_interval)
+		update_interval_callback(min);
+
+	return min;
+}
+
+int __vpn_rtnl_request_update(void)
+{
+	return send_getlink();
+}
+
+int __vpn_rtnl_init(void)
+{
+	struct sockaddr_nl addr;
+	int sk;
+
+	DBG("");
+
+	interface_list = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+							NULL, free_interface);
+
+	sk = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
+	if (sk < 0)
+		return -1;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.nl_family = AF_NETLINK;
+	addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE |
+				RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE |
+				(1<<(RTNLGRP_ND_USEROPT-1));
+
+	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+		close(sk);
+		return -1;
+	}
+
+	channel = g_io_channel_unix_new(sk);
+	g_io_channel_set_close_on_unref(channel, TRUE);
+
+	g_io_channel_set_encoding(channel, NULL, NULL);
+	g_io_channel_set_buffered(channel, FALSE);
+
+	g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR,
+							netlink_event, NULL);
+
+	return 0;
+}
+
+void __vpn_rtnl_start(void)
+{
+	DBG("");
+
+	send_getlink();
+	send_getaddr();
+	send_getroute();
+}
+
+void __vpn_rtnl_cleanup(void)
+{
+	GSList *list;
+
+	DBG("");
+
+	for (list = watch_list; list; list = list->next) {
+		struct watch_data *watch = list->data;
+
+		DBG("removing watch %d", watch->id);
+
+		g_free(watch);
+		list->data = NULL;
+	}
+
+	g_slist_free(watch_list);
+	watch_list = NULL;
+
+	g_slist_free(update_list);
+	update_list = NULL;
+
+	for (list = request_list; list; list = list->next) {
+		struct rtnl_request *req = list->data;
+
+		debug("%s len %d type %d flags 0x%04x seq %d",
+				type2string(req->hdr.nlmsg_type),
+				req->hdr.nlmsg_len, req->hdr.nlmsg_type,
+				req->hdr.nlmsg_flags, req->hdr.nlmsg_seq);
+
+		g_free(req);
+		list->data = NULL;
+	}
+
+	g_slist_free(request_list);
+	request_list = NULL;
+
+	g_io_channel_shutdown(channel, TRUE, NULL);
+	g_io_channel_unref(channel);
+
+	channel = NULL;
+
+	g_hash_table_destroy(interface_list);
+}
diff --git a/vpn/vpn-rtnl.h b/vpn/vpn-rtnl.h
new file mode 100644
index 0000000..aa640a6
--- /dev/null
+++ b/vpn/vpn-rtnl.h
@@ -0,0 +1,65 @@
+/*
+ *
+ *  ConnMan VPN daemon
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __VPN_RTNL_H
+#define __VPN_RTNL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * SECTION:rtnl
+ * @title: RTNL premitives
+ * @short_description: Functions for registering RTNL modules
+ */
+
+typedef void (* vpn_rtnl_link_cb_t) (unsigned flags, unsigned change,
+							void *user_data);
+
+unsigned int vpn_rtnl_add_newlink_watch(int index,
+			vpn_rtnl_link_cb_t callback, void *user_data);
+
+void vpn_rtnl_remove_watch(unsigned int id);
+
+#define VPN_RTNL_PRIORITY_LOW      -100
+#define VPN_RTNL_PRIORITY_DEFAULT     0
+#define VPN_RTNL_PRIORITY_HIGH      100
+
+struct vpn_rtnl {
+	const char *name;
+	int priority;
+	void (*newlink) (unsigned short type, int index,
+					unsigned flags, unsigned change);
+	void (*dellink) (unsigned short type, int index,
+					unsigned flags, unsigned change);
+	void (*newgateway) (int index, const char *gateway);
+	void (*delgateway) (int index, const char *gateway);
+};
+
+int vpn_rtnl_register(struct vpn_rtnl *rtnl);
+void vpn_rtnl_unregister(struct vpn_rtnl *rtnl);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VPN_RTNL_H */
diff --git a/vpn/vpn.h b/vpn/vpn.h
new file mode 100644
index 0000000..10672e0
--- /dev/null
+++ b/vpn/vpn.h
@@ -0,0 +1,98 @@
+/*
+ *
+ *  ConnMan VPN daemon
+ *
+ *  Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <glib.h>
+
+#define VPN_API_SUBJECT_TO_CHANGE
+
+#include <connman/dbus.h>
+#include <connman/types.h>
+
+int __vpn_manager_init(void);
+void __vpn_manager_cleanup(void);
+
+struct vpn_ipconfig;
+
+unsigned char __vpn_ipconfig_netmask_prefix_len(const char *netmask);
+unsigned short __vpn_ipconfig_get_type_from_index(int index);
+unsigned int __vpn_ipconfig_get_flags_from_index(int index);
+void __vpn_ipconfig_foreach(void (*function) (int index,
+				    void *user_data), void *user_data);
+void __vpn_ipconfig_set_local(struct vpn_ipconfig *ipconfig,
+							const char *address);
+const char *__vpn_ipconfig_get_local(struct vpn_ipconfig *ipconfig);
+void __vpn_ipconfig_set_peer(struct vpn_ipconfig *ipconfig,
+						const char *address);
+const char *__vpn_ipconfig_get_peer(struct vpn_ipconfig *ipconfig);
+void __vpn_ipconfig_set_broadcast(struct vpn_ipconfig *ipconfig,
+						const char *broadcast);
+void __vpn_ipconfig_set_gateway(struct vpn_ipconfig *ipconfig,
+						const char *gateway);
+const char *__vpn_ipconfig_get_gateway(struct vpn_ipconfig *ipconfig);
+void __vpn_ipconfig_set_prefixlen(struct vpn_ipconfig *ipconfig,
+						unsigned char prefixlen);
+unsigned char __vpn_ipconfig_get_prefixlen(struct vpn_ipconfig *ipconfig);
+int __vpn_ipconfig_address_add(struct vpn_ipconfig *ipconfig, int family);
+int __vpn_ipconfig_gateway_add(struct vpn_ipconfig *ipconfig, int family);
+struct vpn_ipconfig *__vpn_ipconfig_create(int index, int family);
+void __vpn_ipconfig_set_index(struct vpn_ipconfig *ipconfig,
+								int index);
+struct rtnl_link_stats;
+
+void __vpn_ipconfig_newlink(int index, unsigned short type,
+				unsigned int flags, const char *address,
+				unsigned short mtu,
+				struct rtnl_link_stats *stats);
+void __vpn_ipconfig_dellink(int index, struct rtnl_link_stats *stats);
+int __vpn_ipconfig_init(void);
+void __vpn_ipconfig_cleanup(void);
+
+#include "vpn-provider.h"
+
+connman_bool_t __vpn_provider_check_routes(struct vpn_provider *provider);
+int __vpn_provider_append_user_route(struct vpn_provider *provider,
+			int family, const char *network, const char *netmask);
+void __vpn_provider_append_properties(struct vpn_provider *provider, DBusMessageIter *iter);
+void __vpn_provider_list(DBusMessageIter *iter, void *user_data);
+int __vpn_provider_create_and_connect(DBusMessage *msg);
+DBusMessage *__vpn_provider_get_connections(DBusMessage *msg);
+const char * __vpn_provider_get_ident(struct vpn_provider *provider);
+int __vpn_provider_indicate_state(struct vpn_provider *provider,
+					enum vpn_provider_state state);
+int __vpn_provider_indicate_error(struct vpn_provider *provider,
+					enum vpn_provider_error error);
+int __vpn_provider_connect(struct vpn_provider *provider);
+int __vpn_provider_connect_path(const char *path);
+int __vpn_provider_disconnect(struct vpn_provider *provider);
+int __vpn_provider_remove(const char *path);
+void __vpn_provider_cleanup(void);
+int __vpn_provider_init(void);
+
+#include "vpn-rtnl.h"
+
+int __vpn_rtnl_init(void);
+void __vpn_rtnl_start(void);
+void __vpn_rtnl_cleanup(void);
+
+unsigned int __vpn_rtnl_update_interval_add(unsigned int interval);
+unsigned int __vpn_rtnl_update_interval_remove(unsigned int interval);
+int __vpn_rtnl_request_update(void);
+int __vpn_rtnl_send(const void *buf, size_t len);
diff --git a/vpn/vpn.ver b/vpn/vpn.ver
new file mode 100644
index 0000000..b887706
--- /dev/null
+++ b/vpn/vpn.ver
@@ -0,0 +1,8 @@
+{
+	global:
+		connman_*;
+		vpn_*;
+		g_dbus_*;
+	local:
+		*;
+};
-- 
1.7.11.4




More information about the connman mailing list