diff options
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | Makefile.am | 92 | ||||
-rw-r--r-- | Makefile.plugins | 120 | ||||
-rw-r--r-- | configure.ac | 9 | ||||
-rw-r--r-- | include/vpn-dbus.h | 57 | ||||
-rw-r--r-- | src/plugin.c | 2 | ||||
-rw-r--r-- | vpn/connman-vpn.service.in | 12 | ||||
-rw-r--r-- | vpn/main.c | 257 | ||||
-rw-r--r-- | vpn/net.connman.vpn.service.in | 5 | ||||
-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.conf | 15 | ||||
-rw-r--r-- | vpn/vpn-ipconfig.c | 450 | ||||
-rw-r--r-- | vpn/vpn-manager.c | 142 | ||||
-rw-r--r-- | vpn/vpn-polkit.conf | 11 | ||||
-rw-r--r-- | vpn/vpn-polkit.policy | 29 | ||||
-rw-r--r-- | vpn/vpn-provider.c | 1680 | ||||
-rw-r--r-- | vpn/vpn-provider.h | 121 | ||||
-rw-r--r-- | vpn/vpn-rtnl.c | 1185 | ||||
-rw-r--r-- | vpn/vpn-rtnl.h | 65 | ||||
-rw-r--r-- | vpn/vpn.h | 98 | ||||
-rw-r--r-- | vpn/vpn.ver | 8 |
27 files changed, 4510 insertions, 234 deletions
@@ -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(ð, RTA_DATA(attr), ETH_ALEN); + print(" attr %s (len %d) %s\n", name, len, ether_ntoa(ð)); + } 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: + *; +}; |