summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJukka Rissanen <jukka.rissanen@linux.intel.com>2012-11-12 14:07:21 +0200
committerPatrik Flykt <patrik.flykt@linux.intel.com>2012-11-23 12:58:50 +0200
commit4ba04eb6172f898402e0aa66f0dc8f564a12279f (patch)
tree7a67e489ea3de6f65e65d6034b2a0951db709cd0
parenta426464354273a5586612b6577288e3662e3f8ac (diff)
downloadconnman-4ba04eb6172f898402e0aa66f0dc8f564a12279f.tar.gz
connman-4ba04eb6172f898402e0aa66f0dc8f564a12279f.tar.bz2
connman-4ba04eb6172f898402e0aa66f0dc8f564a12279f.zip
vpn: New vpn daemon that handles vpn connections and clients
-rw-r--r--.gitignore4
-rw-r--r--Makefile.am92
-rw-r--r--Makefile.plugins120
-rw-r--r--configure.ac9
-rw-r--r--include/vpn-dbus.h57
-rw-r--r--src/plugin.c2
-rw-r--r--vpn/connman-vpn.service.in12
-rw-r--r--vpn/main.c257
-rw-r--r--vpn/net.connman.vpn.service.in5
-rw-r--r--vpn/plugins/l2tp.c (renamed from plugins/l2tp.c)47
-rw-r--r--vpn/plugins/openconnect.c (renamed from plugins/openconnect.c)51
-rw-r--r--vpn/plugins/openvpn.c (renamed from plugins/openvpn.c)41
-rw-r--r--vpn/plugins/pptp.c (renamed from plugins/pptp.c)37
-rw-r--r--vpn/plugins/vpn.c (renamed from plugins/vpn.c)125
-rw-r--r--vpn/plugins/vpn.h (renamed from plugins/vpn.h)25
-rw-r--r--vpn/plugins/vpnc.c (renamed from plugins/vpnc.c)56
-rw-r--r--vpn/vpn-dbus.conf15
-rw-r--r--vpn/vpn-ipconfig.c450
-rw-r--r--vpn/vpn-manager.c142
-rw-r--r--vpn/vpn-polkit.conf11
-rw-r--r--vpn/vpn-polkit.policy29
-rw-r--r--vpn/vpn-provider.c1680
-rw-r--r--vpn/vpn-provider.h121
-rw-r--r--vpn/vpn-rtnl.c1185
-rw-r--r--vpn/vpn-rtnl.h65
-rw-r--r--vpn/vpn.h98
-rw-r--r--vpn/vpn.ver8
27 files changed, 4510 insertions, 234 deletions
diff --git a/.gitignore b/.gitignore
index b8f5d3ce..94b3b174 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 a489c852..6fc45d0f 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$(builddir)/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 a6cda480..2c5d66d4 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 286b71cf..51df9d60 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 00000000..fec925bb
--- /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/src/plugin.c b/src/plugin.c
index 65e0311a..adf8525b 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 00000000..ec02a867
--- /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 00000000..35daca77
--- /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 "connman/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 00000000..fc9e9bf3
--- /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/plugins/l2tp.c b/vpn/plugins/l2tp.c
index dfb56d9d..5a11e529 100644
--- a/plugins/l2tp.c
+++ b/vpn/plugins/l2tp.c
@@ -1,6 +1,6 @@
/*
*
- * Connection Manager
+ * ConnMan VPN daemon
*
* Copyright (C) 2010 BMW Car IT GmbH. All rights reserved.
* Copyright (C) 2012 Intel Corporation. All rights reserved.
@@ -34,6 +34,7 @@
#include <stdio.h>
#include <net/if.h>
+#include <dbus/dbus.h>
#include <glib.h>
#define CONNMAN_API_SUBJECT_TO_CHANGE
@@ -114,13 +115,13 @@ static DBusMessage *l2tp_get_sec(struct connman_task *task,
DBusMessage *msg, void *user_data)
{
const char *user, *passwd;
- struct connman_provider *provider = user_data;
+ struct vpn_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");
+ 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)
@@ -140,7 +141,7 @@ static DBusMessage *l2tp_get_sec(struct connman_task *task,
return NULL;
}
-static int l2tp_notify(DBusMessage *msg, struct connman_provider *provider)
+static int l2tp_notify(DBusMessage *msg, struct vpn_provider *provider)
{
DBusMessageIter iter, dict;
const char *reason, *key, *value;
@@ -177,17 +178,17 @@ static int l2tp_notify(DBusMessage *msg, struct connman_provider *provider)
DBG("%s = %s", key, value);
if (!strcmp(key, "INTERNAL_IP4_ADDRESS")) {
- connman_provider_set_string(provider, "Address", value);
+ vpn_provider_set_string(provider, "Address", value);
addressv4 = g_strdup(value);
}
if (!strcmp(key, "INTERNAL_IP4_NETMASK")) {
- connman_provider_set_string(provider, "Netmask", value);
+ vpn_provider_set_string(provider, "Netmask", value);
netmask = g_strdup(value);
}
if (!strcmp(key, "INTERNAL_IP4_DNS")) {
- connman_provider_set_string(provider, "DNS", value);
+ vpn_provider_set_string(provider, "DNS", value);
nameservers = g_strdup(value);
}
@@ -218,9 +219,9 @@ static int l2tp_notify(DBusMessage *msg, struct connman_provider *provider)
return VPN_STATE_FAILURE;
}
- value = connman_provider_get_string(provider, "HostIP");
+ value = vpn_provider_get_string(provider, "HostIP");
if (value != NULL) {
- connman_provider_set_string(provider, "Gateway", value);
+ vpn_provider_set_string(provider, "Gateway", value);
gateway = g_strdup(value);
}
@@ -228,8 +229,8 @@ static int l2tp_notify(DBusMessage *msg, struct connman_provider *provider)
connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask,
gateway);
- connman_provider_set_ipaddress(provider, ipaddress);
- connman_provider_set_nameservers(provider, nameservers);
+ vpn_provider_set_ipaddress(provider, ipaddress);
+ vpn_provider_set_nameservers(provider, nameservers);
g_free(addressv4);
g_free(netmask);
@@ -240,20 +241,20 @@ static int l2tp_notify(DBusMessage *msg, struct connman_provider *provider)
return VPN_STATE_CONNECT;
}
-static int l2tp_save(struct connman_provider *provider, GKeyFile *keyfile)
+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 = connman_provider_get_string(provider,
+ option = vpn_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),
+ vpn_provider_get_save_group(provider),
pppd_options[i].cm_opt, option);
}
}
@@ -332,7 +333,7 @@ static int l2tp_write_section(int fd, const char *key, const char *value)
return ret;
}
-static int write_pppd_option(struct connman_provider *provider, int fd)
+static int write_pppd_option(struct vpn_provider *provider, int fd)
{
int i;
const char *opt_s;
@@ -350,7 +351,7 @@ static int write_pppd_option(struct connman_provider *provider, int fd)
pppd_options[i].sub != OPT_PPPD)
continue;
- opt_s = connman_provider_get_string(provider,
+ opt_s = vpn_provider_get_string(provider,
pppd_options[i].cm_opt);
if (!opt_s)
opt_s = pppd_options[i].vpn_default;
@@ -373,7 +374,7 @@ static int write_pppd_option(struct connman_provider *provider, int fd)
}
-static int l2tp_write_fields(struct connman_provider *provider,
+static int l2tp_write_fields(struct vpn_provider *provider,
int fd, int sub)
{
int i;
@@ -383,7 +384,7 @@ static int l2tp_write_fields(struct connman_provider *provider,
if (pppd_options[i].sub != sub)
continue;
- opt_s = connman_provider_get_string(provider,
+ opt_s = vpn_provider_get_string(provider,
pppd_options[i].cm_opt);
if (!opt_s)
opt_s = pppd_options[i].vpn_default;
@@ -402,7 +403,7 @@ static int l2tp_write_fields(struct connman_provider *provider,
return 0;
}
-static int l2tp_write_config(struct connman_provider *provider,
+static int l2tp_write_config(struct vpn_provider *provider,
const char *pppd_name, int fd)
{
const char *option;
@@ -412,7 +413,7 @@ static int l2tp_write_config(struct connman_provider *provider,
l2tp_write_option(fd, "[lac l2tp]", NULL);
- option = connman_provider_get_string(provider, "Host");
+ option = vpn_provider_get_string(provider, "Host");
l2tp_write_option(fd, "lns =", option);
l2tp_write_fields(provider, fd, OPT_ALL);
@@ -438,7 +439,7 @@ static void l2tp_died(struct connman_task *task, int exit_code, void *user_data)
g_free(conf_file);
}
-static int l2tp_connect(struct connman_provider *provider,
+static int l2tp_connect(struct vpn_provider *provider,
struct connman_task *task, const char *if_name)
{
const char *host;
@@ -450,7 +451,7 @@ static int l2tp_connect(struct connman_provider *provider,
l2tp_get_sec, provider))
return -ENOMEM;
- host = connman_provider_get_string(provider, "Host");
+ host = vpn_provider_get_string(provider, "Host");
if (host == NULL) {
connman_error("Host not set; cannot enable VPN");
return -EINVAL;
diff --git a/plugins/openconnect.c b/vpn/plugins/openconnect.c
index 70be7ae9..0f54108c 100644
--- a/plugins/openconnect.c
+++ b/vpn/plugins/openconnect.c
@@ -1,6 +1,6 @@
/*
*
- * Connection Manager
+ * ConnMan VPN daemon
*
* Copyright (C) 2007-2012 Intel Corporation. All rights reserved.
*
@@ -33,14 +33,15 @@
#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-provider.h"
+
#include "vpn.h"
-static int oc_notify(DBusMessage *msg, struct connman_provider *provider)
+static int oc_notify(DBusMessage *msg, struct vpn_provider *provider)
{
DBusMessageIter iter, dict;
const char *reason, *key, *value;
@@ -63,7 +64,7 @@ static int oc_notify(DBusMessage *msg, struct connman_provider *provider)
if (strcmp(reason, "connect"))
return VPN_STATE_DISCONNECT;
- domain = connman_provider_get_string(provider, "VPN.Domain");
+ domain = vpn_provider_get_string(provider, "VPN.Domain");
dbus_message_iter_recurse(&iter, &dict);
@@ -108,17 +109,17 @@ static int oc_notify(DBusMessage *msg, struct connman_provider *provider)
if (!strcmp(key, "INTERNAL_IP4_DNS") ||
!strcmp(key, "INTERNAL_IP6_DNS"))
- connman_provider_set_nameservers(provider, value);
+ vpn_provider_set_nameservers(provider, value);
if (!strcmp(key, "CISCO_PROXY_PAC"))
- connman_provider_set_pac(provider, value);
+ 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)
- connman_provider_append_route(provider, key, value);
+ vpn_provider_append_route(provider, key, value);
dbus_message_iter_next(&dict);
}
@@ -147,8 +148,8 @@ static int oc_notify(DBusMessage *msg, struct connman_provider *provider)
else
connman_ipaddress_set_ipv6(ipaddress, addressv6,
prefix_len, gateway);
- connman_provider_set_ipaddress(provider, ipaddress);
- connman_provider_set_domain(provider, domain);
+ vpn_provider_set_ipaddress(provider, ipaddress);
+ vpn_provider_set_domain(provider, domain);
g_free(addressv4);
g_free(addressv6);
@@ -159,32 +160,32 @@ static int oc_notify(DBusMessage *msg, struct connman_provider *provider)
return VPN_STATE_CONNECT;
}
-static int oc_connect(struct connman_provider *provider,
+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 = connman_provider_get_string(provider, "Host");
+ vpnhost = vpn_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");
+ vpncookie = vpn_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,
+ certsha1 = vpn_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");
+ cafile = vpn_provider_get_string(provider, "OpenConnect.CACert");
+ mtu = vpn_provider_get_string(provider, "VPN.MTU");
if (cafile)
connman_task_add_argument(task, "--cafile",
@@ -219,29 +220,29 @@ static int oc_connect(struct connman_provider *provider,
return 0;
}
-static int oc_save (struct connman_provider *provider, GKeyFile *keyfile)
+static int oc_save(struct vpn_provider *provider, GKeyFile *keyfile)
{
const char *setting;
- setting = connman_provider_get_string(provider,
+ setting = vpn_provider_get_string(provider,
"OpenConnect.ServerCert");
if (setting != NULL)
g_key_file_set_string(keyfile,
- connman_provider_get_save_group(provider),
+ vpn_provider_get_save_group(provider),
"OpenConnect.ServerCert", setting);
- setting = connman_provider_get_string(provider,
+ setting = vpn_provider_get_string(provider,
"OpenConnect.CACert");
if (setting != NULL)
g_key_file_set_string(keyfile,
- connman_provider_get_save_group(provider),
+ vpn_provider_get_save_group(provider),
"OpenConnect.CACert", setting);
- setting = connman_provider_get_string(provider,
+ setting = vpn_provider_get_string(provider,
"VPN.MTU");
if (setting != NULL)
g_key_file_set_string(keyfile,
- connman_provider_get_save_group(provider),
+ vpn_provider_get_save_group(provider),
"VPN.MTU", setting);
return 0;
@@ -252,11 +253,11 @@ static int oc_error_code(int exit_code)
switch (exit_code) {
case 1:
- return CONNMAN_PROVIDER_ERROR_CONNECT_FAILED;
+ return VPN_PROVIDER_ERROR_CONNECT_FAILED;
case 2:
- return CONNMAN_PROVIDER_ERROR_LOGIN_FAILED;
+ return VPN_PROVIDER_ERROR_LOGIN_FAILED;
default:
- return CONNMAN_PROVIDER_ERROR_UNKNOWN;
+ return VPN_PROVIDER_ERROR_UNKNOWN;
}
}
diff --git a/plugins/openvpn.c b/vpn/plugins/openvpn.c
index bac1671b..dd654156 100644
--- a/plugins/openvpn.c
+++ b/vpn/plugins/openvpn.c
@@ -1,6 +1,6 @@
/*
*
- * Connection Manager
+ * ConnMan VPN daemon
*
* Copyright (C) 2010 BMW Car IT GmbH. All rights reserved.
*
@@ -33,12 +33,13 @@
#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-provider.h"
+
#include "vpn.h"
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
@@ -100,7 +101,7 @@ static void ov_append_dns_entries(const char *key, const char *value,
g_strfreev(options);
}
-static int ov_notify(DBusMessage *msg, struct connman_provider *provider)
+static int ov_notify(DBusMessage *msg, struct vpn_provider *provider)
{
DBusMessageIter iter, dict;
const char *reason, *key, *value;
@@ -134,22 +135,22 @@ static int ov_notify(DBusMessage *msg, struct connman_provider *provider)
DBG("%s = %s", key, value);
if (!strcmp(key, "trusted_ip")) {
- connman_provider_set_string(provider, "Gateway", value);
+ vpn_provider_set_string(provider, "Gateway", value);
gateway = g_strdup(value);
}
if (!strcmp(key, "ifconfig_local")) {
- connman_provider_set_string(provider, "Address", value);
+ vpn_provider_set_string(provider, "Address", value);
address = g_strdup(value);
}
if (!strcmp(key, "ifconfig_remote")) {
- connman_provider_set_string(provider, "Peer", value);
+ vpn_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);
+ vpn_provider_append_route(provider, key, value);
ov_append_dns_entries(key, value, &nameservers);
@@ -168,9 +169,9 @@ static int ov_notify(DBusMessage *msg, struct connman_provider *provider)
connman_ipaddress_set_ipv4(ipaddress, address, NULL, gateway);
connman_ipaddress_set_peer(ipaddress, peer);
- connman_provider_set_ipaddress(provider, ipaddress);
+ vpn_provider_set_ipaddress(provider, ipaddress);
- connman_provider_set_nameservers(provider, nameservers);
+ vpn_provider_set_nameservers(provider, nameservers);
g_free(nameservers);
g_free(address);
@@ -181,27 +182,27 @@ static int ov_notify(DBusMessage *msg, struct connman_provider *provider)
return VPN_STATE_CONNECT;
}
-static int ov_save(struct connman_provider *provider, GKeyFile *keyfile)
+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 = connman_provider_get_string(provider,
+ option = vpn_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),
+ vpn_provider_get_save_group(provider),
ov_options[i].cm_opt, option);
}
}
return 0;
}
-static int task_append_config_data(struct connman_provider *provider,
+static int task_append_config_data(struct vpn_provider *provider,
struct connman_task *task)
{
const char *option;
@@ -211,14 +212,14 @@ static int task_append_config_data(struct connman_provider *provider,
if (ov_options[i].ov_opt == NULL)
continue;
- option = connman_provider_get_string(provider,
+ 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) {
+ ov_options[i].ov_opt,
+ ov_options[i].has_value ? option : NULL) < 0) {
return -EIO;
}
}
@@ -226,13 +227,13 @@ static int task_append_config_data(struct connman_provider *provider,
return 0;
}
-static int ov_connect(struct connman_provider *provider,
+static int ov_connect(struct vpn_provider *provider,
struct connman_task *task, const char *if_name)
{
const char *option;
int err, fd;
- option = connman_provider_get_string(provider, "Host");
+ option = vpn_provider_get_string(provider, "Host");
if (option == NULL) {
connman_error("Host not set; cannot enable VPN");
return -EINVAL;
@@ -240,10 +241,10 @@ static int ov_connect(struct connman_provider *provider,
task_append_config_data(provider, task);
- option = connman_provider_get_string(provider, "OpenVPN.TLSAuth");
+ option = vpn_provider_get_string(provider, "OpenVPN.TLSAuth");
if (option != NULL) {
connman_task_add_argument(task, "--tls-auth", option);
- option = connman_provider_get_string(provider,
+ option = vpn_provider_get_string(provider,
"OpenVPN.TLSAuthDir");
if (option != NULL)
connman_task_add_argument(task, option, NULL);
diff --git a/plugins/pptp.c b/vpn/plugins/pptp.c
index 7fafc059..f737c316 100644
--- a/plugins/pptp.c
+++ b/vpn/plugins/pptp.c
@@ -1,6 +1,6 @@
/*
*
- * Connection Manager
+ * ConnMan VPN daemon
*
* Copyright (C) 2010 BMW Car IT GmbH. All rights reserved.
* Copyright (C) 2012 Intel Corporation. All rights reserved.
@@ -30,6 +30,7 @@
#include <stdio.h>
#include <net/if.h>
+#include <dbus/dbus.h>
#include <glib.h>
#define CONNMAN_API_SUBJECT_TO_CHANGE
@@ -79,14 +80,14 @@ static DBusMessage *pptp_get_sec(struct connman_task *task,
DBusMessage *msg, void *user_data)
{
const char *user, *passwd;
- struct connman_provider *provider = user_data;
+ struct vpn_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");
+ 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;
@@ -101,7 +102,7 @@ static DBusMessage *pptp_get_sec(struct connman_task *task,
return reply;
}
-static int pptp_notify(DBusMessage *msg, struct connman_provider *provider)
+static int pptp_notify(DBusMessage *msg, struct vpn_provider *provider)
{
DBusMessageIter iter, dict;
const char *reason, *key, *value;
@@ -138,17 +139,17 @@ static int pptp_notify(DBusMessage *msg, struct connman_provider *provider)
DBG("%s = %s", key, value);
if (!strcmp(key, "INTERNAL_IP4_ADDRESS")) {
- connman_provider_set_string(provider, "Address", value);
+ vpn_provider_set_string(provider, "Address", value);
addressv4 = g_strdup(value);
}
if (!strcmp(key, "INTERNAL_IP4_NETMASK")) {
- connman_provider_set_string(provider, "Netmask", value);
+ vpn_provider_set_string(provider, "Netmask", value);
netmask = g_strdup(value);
}
if (!strcmp(key, "INTERNAL_IP4_DNS")) {
- connman_provider_set_string(provider, "DNS", value);
+ vpn_provider_set_string(provider, "DNS", value);
nameservers = g_strdup(value);
}
@@ -179,9 +180,9 @@ static int pptp_notify(DBusMessage *msg, struct connman_provider *provider)
return VPN_STATE_FAILURE;
}
- value = connman_provider_get_string(provider, "HostIP");
+ value = vpn_provider_get_string(provider, "HostIP");
if (value != NULL) {
- connman_provider_set_string(provider, "Gateway", value);
+ vpn_provider_set_string(provider, "Gateway", value);
gateway = g_strdup(value);
}
@@ -189,8 +190,8 @@ static int pptp_notify(DBusMessage *msg, struct connman_provider *provider)
connman_ipaddress_set_ipv4(ipaddress, addressv4, netmask,
gateway);
- connman_provider_set_ipaddress(provider, ipaddress);
- connman_provider_set_nameservers(provider, nameservers);
+ vpn_provider_set_ipaddress(provider, ipaddress);
+ vpn_provider_set_nameservers(provider, nameservers);
g_free(addressv4);
g_free(netmask);
@@ -201,20 +202,20 @@ static int pptp_notify(DBusMessage *msg, struct connman_provider *provider)
return VPN_STATE_CONNECT;
}
-static int pptp_save(struct connman_provider *provider, GKeyFile *keyfile)
+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 = connman_provider_get_string(provider,
+ option = vpn_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),
+ vpn_provider_get_save_group(provider),
pptp_options[i].cm_opt, option);
}
}
@@ -232,7 +233,7 @@ static void pptp_write_bool_option(struct connman_task *task,
}
}
-static int pptp_connect(struct connman_provider *provider,
+static int pptp_connect(struct vpn_provider *provider,
struct connman_task *task, const char *if_name)
{
const char *opt_s, *host;
@@ -243,7 +244,7 @@ static int pptp_connect(struct connman_provider *provider,
pptp_get_sec, provider))
return -ENOMEM;
- host = connman_provider_get_string(provider, "Host");
+ host = vpn_provider_get_string(provider, "Host");
if (host == NULL) {
connman_error("Host not set; cannot enable VPN");
return -EINVAL;
@@ -268,7 +269,7 @@ static int pptp_connect(struct connman_provider *provider,
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,
+ opt_s = vpn_provider_get_string(provider,
pptp_options[i].cm_opt);
if (opt_s == NULL)
opt_s = pptp_options[i].vpnc_default;
diff --git a/plugins/vpn.c b/vpn/plugins/vpn.c
index 165c3251..2a0fdaf7 100644
--- a/plugins/vpn.c
+++ b/vpn/plugins/vpn.c
@@ -1,6 +1,6 @@
/*
*
- * Connection Manager
+ * ConnMan VPN daemon
*
* Copyright (C) 2007-2012 Intel Corporation. All rights reserved.
*
@@ -39,16 +39,18 @@
#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-rtnl.h"
+#include "../vpn-provider.h"
+
#include "vpn.h"
struct vpn_data {
- struct connman_provider *provider;
+ struct vpn_provider *provider;
char *if_name;
unsigned flags;
unsigned int watch;
@@ -60,14 +62,14 @@ struct vpn_driver_data {
const char *name;
const char *program;
struct vpn_driver *vpn_driver;
- struct connman_provider_driver provider_driver;
+ struct vpn_provider_driver provider_driver;
};
GHashTable *driver_hash = NULL;
-static int stop_vpn(struct connman_provider *provider)
+static int stop_vpn(struct vpn_provider *provider)
{
- struct vpn_data *data = connman_provider_get_data(provider);
+ struct vpn_data *data = vpn_provider_get_data(provider);
struct vpn_driver_data *vpn_driver_data;
const char *name;
struct ifreq ifr;
@@ -76,7 +78,7 @@ static int stop_vpn(struct connman_provider *provider)
if (data == NULL)
return -EINVAL;
- name = connman_provider_get_driver_name(provider);
+ name = vpn_provider_get_driver_name(provider);
if (name == NULL)
return -EINVAL;
@@ -120,10 +122,10 @@ static int stop_vpn(struct connman_provider *provider)
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);
+ struct vpn_provider *provider = user_data;
+ struct vpn_data *data = vpn_provider_get_data(provider);
int state = VPN_STATE_FAILURE;
- enum connman_provider_error ret;
+ enum vpn_provider_error ret;
DBG("provider %p data %p", provider, data);
@@ -133,11 +135,11 @@ void vpn_died(struct connman_task *task, int exit_code, void *user_data)
state = data->state;
stop_vpn(provider);
- connman_provider_set_data(provider, NULL);
+ vpn_provider_set_data(provider, NULL);
if (data->watch != 0) {
- connman_provider_unref(provider);
- connman_rtnl_remove_watch(data->watch);
+ vpn_provider_unref(provider);
+ vpn_rtnl_remove_watch(data->watch);
data->watch = 0;
}
@@ -146,7 +148,7 @@ vpn_exit:
const char *name;
struct vpn_driver_data *vpn_data = NULL;
- name = connman_provider_get_driver_name(provider);
+ name = vpn_provider_get_driver_name(provider);
if (name != NULL)
vpn_data = g_hash_table_lookup(driver_hash, name);
@@ -154,17 +156,16 @@ vpn_exit:
vpn_data->vpn_driver->error_code != NULL)
ret = vpn_data->vpn_driver->error_code(exit_code);
else
- ret = CONNMAN_PROVIDER_ERROR_UNKNOWN;
+ ret = VPN_PROVIDER_ERROR_UNKNOWN;
- connman_provider_indicate_error(provider, ret);
+ vpn_provider_indicate_error(provider, ret);
} else
- connman_provider_set_state(provider,
- CONNMAN_PROVIDER_STATE_IDLE);
+ vpn_provider_set_state(provider, VPN_PROVIDER_STATE_IDLE);
- connman_provider_set_index(provider, -1);
+ vpn_provider_set_index(provider, -1);
if (data != NULL) {
- connman_provider_unref(data->provider);
+ vpn_provider_unref(data->provider);
g_free(data->if_name);
g_free(data);
}
@@ -172,9 +173,9 @@ vpn_exit:
connman_task_destroy(task);
}
-int vpn_set_ifname(struct connman_provider *provider, const char *ifname)
+int vpn_set_ifname(struct vpn_provider *provider, const char *ifname)
{
- struct vpn_data *data = connman_provider_get_data(provider);
+ struct vpn_data *data = vpn_provider_get_data(provider);
int index;
if (ifname == NULL || data == NULL)
@@ -188,21 +189,21 @@ int vpn_set_ifname(struct connman_provider *provider, const char *ifname)
g_free(data->if_name);
data->if_name = (char *)g_strdup(ifname);
- connman_provider_set_index(provider, index);
+ vpn_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);
+ 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;
- connman_provider_set_state(provider,
- CONNMAN_PROVIDER_STATE_READY);
+ vpn_provider_set_state(provider,
+ VPN_PROVIDER_STATE_READY);
}
}
data->flags = flags;
@@ -211,15 +212,15 @@ static void vpn_newlink(unsigned flags, unsigned change, void *user_data)
static DBusMessage *vpn_notify(struct connman_task *task,
DBusMessage *msg, void *user_data)
{
- struct connman_provider *provider = 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 = connman_provider_get_data(provider);
+ data = vpn_provider_get_data(provider);
- name = connman_provider_get_driver_name(provider);
+ name = vpn_provider_get_driver_name(provider);
if (name == NULL)
return NULL;
@@ -231,9 +232,9 @@ static DBusMessage *vpn_notify(struct connman_task *task,
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,
+ 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;
@@ -242,22 +243,22 @@ static DBusMessage *vpn_notify(struct connman_task *task,
case VPN_STATE_IDLE:
case VPN_STATE_DISCONNECT:
case VPN_STATE_FAILURE:
- connman_provider_set_state(provider,
- CONNMAN_PROVIDER_STATE_DISCONNECT);
+ vpn_provider_set_state(provider,
+ VPN_PROVIDER_STATE_DISCONNECT);
break;
case VPN_STATE_AUTH_FAILURE:
- connman_provider_indicate_error(provider,
- CONNMAN_PROVIDER_ERROR_AUTH_FAILED);
+ vpn_provider_indicate_error(provider,
+ VPN_PROVIDER_ERROR_AUTH_FAILED);
break;
}
return NULL;
}
-static int vpn_create_tun(struct connman_provider *provider)
+static int vpn_create_tun(struct vpn_provider *provider)
{
- struct vpn_data *data = connman_provider_get_data(provider);
+ struct vpn_data *data = vpn_provider_get_data(provider);
struct ifreq ifr;
int i, fd, index;
int ret = 0;
@@ -317,7 +318,7 @@ static int vpn_create_tun(struct connman_provider *provider)
ret = -EIO;
goto exist_err;
}
- connman_provider_set_index(provider, index);
+ vpn_provider_set_index(provider, index);
return 0;
@@ -325,9 +326,9 @@ exist_err:
return ret;
}
-static int vpn_connect(struct connman_provider *provider)
+static int vpn_connect(struct vpn_provider *provider)
{
- struct vpn_data *data = connman_provider_get_data(provider);
+ struct vpn_data *data = vpn_provider_get_data(provider);
struct vpn_driver_data *vpn_driver_data;
const char *name;
int ret = 0;
@@ -339,15 +340,15 @@ static int vpn_connect(struct connman_provider *provider)
if (data == NULL)
return -ENOMEM;
- data->provider = connman_provider_ref(provider);
+ data->provider = vpn_provider_ref(provider);
data->watch = 0;
data->flags = 0;
data->task = NULL;
data->state = VPN_STATE_IDLE;
- connman_provider_set_data(provider, data);
+ vpn_provider_set_data(provider, data);
- name = connman_provider_get_driver_name(provider);
+ name = vpn_provider_get_driver_name(provider);
if (name == NULL)
return -EINVAL;
@@ -398,23 +399,23 @@ static int vpn_connect(struct connman_provider *provider)
return -EINPROGRESS;
exist_err:
- connman_provider_set_index(provider, -1);
- connman_provider_set_data(provider, NULL);
- connman_provider_unref(data->provider);
+ 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 connman_provider *provider)
+static int vpn_probe(struct vpn_provider *provider)
{
return 0;
}
-static int vpn_disconnect(struct connman_provider *provider)
+static int vpn_disconnect(struct vpn_provider *provider)
{
- struct vpn_data *data = connman_provider_get_data(provider);
+ struct vpn_data *data = vpn_provider_get_data(provider);
struct vpn_driver_data *vpn_driver_data;
const char *name;
@@ -423,7 +424,7 @@ static int vpn_disconnect(struct connman_provider *provider)
if (data == NULL)
return 0;
- name = connman_provider_get_driver_name(provider);
+ name = vpn_provider_get_driver_name(provider);
if (name == NULL)
return 0;
@@ -432,8 +433,8 @@ static int vpn_disconnect(struct connman_provider *provider)
vpn_driver_data->vpn_driver->disconnect();
if (data->watch != 0) {
- connman_provider_unref(provider);
- connman_rtnl_remove_watch(data->watch);
+ vpn_provider_unref(provider);
+ vpn_rtnl_remove_watch(data->watch);
data->watch = 0;
}
@@ -443,17 +444,17 @@ static int vpn_disconnect(struct connman_provider *provider)
return 0;
}
-static int vpn_remove(struct connman_provider *provider)
+static int vpn_remove(struct vpn_provider *provider)
{
struct vpn_data *data;
- data = connman_provider_get_data(provider);
+ data = vpn_provider_get_data(provider);
if (data == NULL)
return 0;
if (data->watch != 0) {
- connman_provider_unref(provider);
- connman_rtnl_remove_watch(data->watch);
+ vpn_provider_unref(provider);
+ vpn_rtnl_remove_watch(data->watch);
data->watch = 0;
}
@@ -464,12 +465,12 @@ static int vpn_remove(struct connman_provider *provider)
return 0;
}
-static int vpn_save (struct connman_provider *provider, GKeyFile *keyfile)
+static int vpn_save(struct vpn_provider *provider, GKeyFile *keyfile)
{
struct vpn_driver_data *vpn_driver_data;
const char *name;
- name = connman_provider_get_driver_name(provider);
+ 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)
@@ -512,7 +513,7 @@ int vpn_register(const char *name, struct vpn_driver *vpn_driver,
g_hash_table_replace(driver_hash, (char *)name, data);
- connman_provider_driver_register(&data->provider_driver);
+ vpn_provider_driver_register(&data->provider_driver);
return 0;
}
@@ -525,7 +526,7 @@ void vpn_unregister(const char *name)
if (data == NULL)
return;
- connman_provider_driver_unregister(&data->provider_driver);
+ vpn_provider_driver_unregister(&data->provider_driver);
g_hash_table_remove(driver_hash, name);
diff --git a/plugins/vpn.h b/vpn/plugins/vpn.h
index 1ecfba22..6693cdba 100644
--- a/plugins/vpn.h
+++ b/vpn/plugins/vpn.h
@@ -1,6 +1,6 @@
/*
*
- * Connection Manager
+ * ConnMan VPN daemon
*
* Copyright (C) 2010 BMW Car IT GmbH. All rights reserved.
*
@@ -19,6 +19,15 @@
*
*/
+#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 {
@@ -33,16 +42,22 @@ enum vpn_state {
struct vpn_driver {
int flags;
- int (*notify) (DBusMessage *msg, struct connman_provider *provider);
- int (*connect) (struct connman_provider *provider,
+ 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 connman_provider *provider, GKeyFile *keyfile);
+ 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 connman_provider *provider, const char *ifname);
+int vpn_set_ifname(struct vpn_provider *provider, const char *ifname);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_VPND_VPN_H */
diff --git a/plugins/vpnc.c b/vpn/plugins/vpnc.c
index 6b7a02be..9fd1dec8 100644
--- a/plugins/vpnc.c
+++ b/vpn/plugins/vpnc.c
@@ -1,8 +1,9 @@
/*
*
- * Connection Manager
+ * 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
@@ -33,12 +34,13 @@
#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-provider.h"
+
#include "vpn.h"
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
@@ -68,19 +70,19 @@ struct {
{ "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.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 },
+ TRUE },
{ "VPNC.DPDTimeout", "DPD idle timeout (our side)", NULL, OPT_STRING,
- TRUE },
+ TRUE },
{ "VPNC.SingleDES", "Enable Single DES", NULL, OPT_BOOLEAN, TRUE },
{ "VPNC.NoEncryption", "Enable no encryption", NULL, OPT_BOOLEAN,
- TRUE },
+ TRUE },
};
-static int vc_notify(DBusMessage *msg, struct connman_provider *provider)
+static int vc_notify(DBusMessage *msg, struct vpn_provider *provider)
{
DBusMessageIter iter, dict;
char *address = NULL, *netmask = NULL, *gateway = NULL;
@@ -122,14 +124,14 @@ static int vc_notify(DBusMessage *msg, struct connman_provider *provider)
netmask = g_strdup(value);
if (!strcmp(key, "INTERNAL_IP4_DNS"))
- connman_provider_set_nameservers(provider, value);
+ vpn_provider_set_nameservers(provider, value);
if (!strcmp(key, "CISCO_DEF_DOMAIN"))
- connman_provider_set_domain(provider, value);
+ 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)
- connman_provider_append_route(provider, key, value);
+ vpn_provider_append_route(provider, key, value);
dbus_message_iter_next(&dict);
}
@@ -145,7 +147,7 @@ static int vc_notify(DBusMessage *msg, struct connman_provider *provider)
}
connman_ipaddress_set_ipv4(ipaddress, address, netmask, gateway);
- connman_provider_set_ipaddress(provider, ipaddress);
+ vpn_provider_set_ipaddress(provider, ipaddress);
g_free(address);
g_free(netmask);
@@ -207,18 +209,18 @@ static ssize_t write_bool_option(int fd, const char *key, const char *value)
return ret;
}
-static int vc_write_config_data(struct connman_provider *provider, int fd)
+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 = connman_provider_get_string(provider,
+ opt_s = vpn_provider_get_string(provider,
vpnc_options[i].cm_opt);
- if (!opt_s)
- opt_s= vpnc_options[i].vpnc_default;
+ if (opt_s == FALSE)
+ opt_s = vpnc_options[i].vpnc_default;
- if(!opt_s)
+ if (opt_s == FALSE)
continue;
if (vpnc_options[i].type == OPT_STRING) {
@@ -236,7 +238,7 @@ static int vc_write_config_data(struct connman_provider *provider, int fd)
return 0;
}
-static int vc_save(struct connman_provider *provider, GKeyFile *keyfile)
+static int vc_save(struct vpn_provider *provider, GKeyFile *keyfile)
{
const char *option;
int i;
@@ -247,31 +249,31 @@ static int vc_save(struct connman_provider *provider, GKeyFile *keyfile)
if (vpnc_options[i].cm_save == FALSE)
continue;
- option = connman_provider_get_string(provider,
+ option = vpn_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),
+ vpn_provider_get_save_group(provider),
vpnc_options[i].cm_opt, option);
}
}
return 0;
}
-static int vc_connect(struct connman_provider *provider,
+static int vc_connect(struct vpn_provider *provider,
struct connman_task *task, const char *if_name)
{
const char *option;
int err, fd;
- option = connman_provider_get_string(provider, "Host");
+ option = vpn_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");
+ option = vpn_provider_get_string(provider, "VPNC.IPSec.ID");
if (option == NULL) {
connman_error("Group not set; cannot enable VPN");
return -EINVAL;
@@ -286,7 +288,7 @@ static int vc_connect(struct connman_provider *provider,
connman_task_add_argument(task, "--script",
SCRIPTDIR "/openconnect-script");
- option = connman_provider_get_string(provider, "VPNC.Debug");
+ option = vpn_provider_get_string(provider, "VPNC.Debug");
if (option != NULL)
connman_task_add_argument(task, "--debug", option);
@@ -310,11 +312,11 @@ static int vc_error_code(int exit_code)
{
switch (exit_code) {
case 1:
- return CONNMAN_PROVIDER_ERROR_CONNECT_FAILED;
+ return VPN_PROVIDER_ERROR_CONNECT_FAILED;
case 2:
- return CONNMAN_PROVIDER_ERROR_LOGIN_FAILED;
+ return VPN_PROVIDER_ERROR_LOGIN_FAILED;
default:
- return CONNMAN_PROVIDER_ERROR_UNKNOWN;
+ return VPN_PROVIDER_ERROR_UNKNOWN;
}
}
diff --git a/vpn/vpn-dbus.conf b/vpn/vpn-dbus.conf
new file mode 100644
index 00000000..0f0c8da4
--- /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 00000000..cb5167f0
--- /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 00000000..680f2fd9
--- /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 "connman/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 00000000..a1dc6177
--- /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 00000000..0c427220
--- /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 00000000..462dc26c
--- /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 "connman/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, "Peer",
+ 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, "Peer",
+ 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 00000000..0f139b09
--- /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 00000000..5ce14e42
--- /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 00000000..aa640a66
--- /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 00000000..10672e02
--- /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 00000000..b8877064
--- /dev/null
+++ b/vpn/vpn.ver
@@ -0,0 +1,8 @@
+{
+ global:
+ connman_*;
+ vpn_*;
+ g_dbus_*;
+ local:
+ *;
+};