aboutsummaryrefslogtreecommitdiff
path: root/src/vpn
diff options
context:
space:
mode:
authorBertrand Marc <beberking@gmail.com>2012-05-02 21:43:37 +0200
committerBertrand Marc <beberking@gmail.com>2012-05-02 21:43:37 +0200
commit2b81464a43485fcc8ce079fafdee7b7a171835f4 (patch)
tree394774c0f735199b57d51a2d3840356317853fe1 /src/vpn
Imported Upstream version 0.9.2upstream/0.9.2
Diffstat (limited to 'src/vpn')
-rw-r--r--src/vpn/Makefile.am110
-rw-r--r--src/vpn/Makefile.in1018
-rw-r--r--src/vpn/gnunet-helper-vpn.c616
-rw-r--r--src/vpn/gnunet-service-vpn.c3202
-rw-r--r--src/vpn/gnunet-vpn.c334
-rw-r--r--src/vpn/test_gnunet_vpn.c605
-rw-r--r--src/vpn/test_gnunet_vpn.conf37
-rw-r--r--src/vpn/vpn.conf.in21
-rw-r--r--src/vpn/vpn.h159
-rw-r--r--src/vpn/vpn_api.c605
10 files changed, 6707 insertions, 0 deletions
diff --git a/src/vpn/Makefile.am b/src/vpn/Makefile.am
new file mode 100644
index 0000000..2af34f8
--- /dev/null
+++ b/src/vpn/Makefile.am
@@ -0,0 +1,110 @@
+INCLUDES = -I$(top_srcdir)/src/include
+
+if MINGW
+ WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
+endif
+
+if USE_COVERAGE
+ AM_CFLAGS = --coverage -O0
+endif
+
+pkgcfgdir= $(pkgdatadir)/config.d/
+
+plugindir = $(libdir)/gnunet
+
+pkgcfg_DATA = \
+ vpn.conf
+
+if LINUX
+VPNBIN = gnunet-helper-vpn
+install-exec-hook:
+ $(SUDO_BINARY) chown root:root $(bindir)/gnunet-helper-vpn || true
+ $(SUDO_BINARY) chmod u+s $(bindir)/gnunet-helper-vpn || true
+if HAVE_MHD
+ VPN_TEST = \
+ test_gnunet_vpn-4_to_6 \
+ test_gnunet_vpn-6_to_4 \
+ test_gnunet_vpn-6_over \
+ test_gnunet_vpn-4_over
+endif
+else
+install-exec-hook:
+endif
+
+
+lib_LTLIBRARIES = \
+ libgnunetvpn.la
+
+
+bin_PROGRAMS = \
+ $(VPNBIN) gnunet-service-vpn gnunet-vpn
+
+
+
+check_PROGRAMS = $(VPN_TEST)
+
+if ENABLE_TEST_RUN
+TESTS = $(check_PROGRAMS)
+endif
+
+EXTRA_DIST = \
+ test_gnunet_vpn.conf
+
+gnunet_helper_vpn_SOURCES = \
+ gnunet-helper-vpn.c
+
+gnunet_service_vpn_SOURCES = \
+ gnunet-service-vpn.c
+gnunet_service_vpn_LDADD = \
+ $(top_builddir)/src/statistics/libgnunetstatistics.la \
+ $(top_builddir)/src/tun/libgnunettun.la \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/mesh/libgnunetmesh.la \
+ $(GN_LIBINTL)
+gnunet_service_vpn_CFLAGS = \
+ -I$(top_srcdir)/src/exit $(CFLAGS)
+
+gnunet_vpn_SOURCES = \
+ gnunet-vpn.c
+gnunet_vpn_LDADD = \
+ $(top_builddir)/src/vpn/libgnunetvpn.la \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(GN_LIBINTL)
+gnunet_vpn_DEPENDENCIES = \
+ libgnunetvpn.la
+
+libgnunetvpn_la_SOURCES = \
+ vpn_api.c vpn.h
+libgnunetvpn_la_LIBADD = \
+ $(top_builddir)/src/util/libgnunetutil.la $(XLIB)
+libgnunetvpn_la_LDFLAGS = \
+ $(GN_LIB_LDFLAGS)
+
+
+test_gnunet_vpn_4_over_SOURCES = \
+ test_gnunet_vpn.c
+test_gnunet_vpn_4_over_LDADD = -lmicrohttpd @LIBCURL@ \
+ $(top_builddir)/src/vpn/libgnunetvpn.la \
+ $(top_builddir)/src/arm/libgnunetarm.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_gnunet_vpn_6_over_SOURCES = \
+ test_gnunet_vpn.c
+test_gnunet_vpn_6_over_LDADD = -lmicrohttpd @LIBCURL@ \
+ $(top_builddir)/src/vpn/libgnunetvpn.la \
+ $(top_builddir)/src/arm/libgnunetarm.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_gnunet_vpn_4_to_6_SOURCES = \
+ test_gnunet_vpn.c
+test_gnunet_vpn_4_to_6_LDADD = -lmicrohttpd @LIBCURL@ \
+ $(top_builddir)/src/vpn/libgnunetvpn.la \
+ $(top_builddir)/src/arm/libgnunetarm.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_gnunet_vpn_6_to_4_SOURCES = \
+ test_gnunet_vpn.c
+test_gnunet_vpn_6_to_4_LDADD = -lmicrohttpd @LIBCURL@ \
+ $(top_builddir)/src/vpn/libgnunetvpn.la \
+ $(top_builddir)/src/arm/libgnunetarm.la \
+ $(top_builddir)/src/util/libgnunetutil.la
diff --git a/src/vpn/Makefile.in b/src/vpn/Makefile.in
new file mode 100644
index 0000000..575375b
--- /dev/null
+++ b/src/vpn/Makefile.in
@@ -0,0 +1,1018 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+bin_PROGRAMS = $(am__EXEEXT_1) gnunet-service-vpn$(EXEEXT) \
+ gnunet-vpn$(EXEEXT)
+check_PROGRAMS = $(am__EXEEXT_2)
+subdir = src/vpn
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(srcdir)/vpn.conf.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/absolute-header.m4 \
+ $(top_srcdir)/m4/align.m4 $(top_srcdir)/m4/argz.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libcurl.m4 \
+ $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/libunistring.m4 $(top_srcdir)/m4/ltdl.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/gnunet_config.h
+CONFIG_CLEAN_FILES = vpn.conf
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \
+ "$(DESTDIR)$(pkgcfgdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libgnunetvpn_la_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_libgnunetvpn_la_OBJECTS = vpn_api.lo
+libgnunetvpn_la_OBJECTS = $(am_libgnunetvpn_la_OBJECTS)
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+libgnunetvpn_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libgnunetvpn_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+@LINUX_TRUE@am__EXEEXT_1 = gnunet-helper-vpn$(EXEEXT)
+@HAVE_MHD_TRUE@@LINUX_TRUE@am__EXEEXT_2 = \
+@HAVE_MHD_TRUE@@LINUX_TRUE@ test_gnunet_vpn-4_to_6$(EXEEXT) \
+@HAVE_MHD_TRUE@@LINUX_TRUE@ test_gnunet_vpn-6_to_4$(EXEEXT) \
+@HAVE_MHD_TRUE@@LINUX_TRUE@ test_gnunet_vpn-6_over$(EXEEXT) \
+@HAVE_MHD_TRUE@@LINUX_TRUE@ test_gnunet_vpn-4_over$(EXEEXT)
+PROGRAMS = $(bin_PROGRAMS)
+am_gnunet_helper_vpn_OBJECTS = gnunet-helper-vpn.$(OBJEXT)
+gnunet_helper_vpn_OBJECTS = $(am_gnunet_helper_vpn_OBJECTS)
+gnunet_helper_vpn_LDADD = $(LDADD)
+am_gnunet_service_vpn_OBJECTS = \
+ gnunet_service_vpn-gnunet-service-vpn.$(OBJEXT)
+gnunet_service_vpn_OBJECTS = $(am_gnunet_service_vpn_OBJECTS)
+am__DEPENDENCIES_1 =
+gnunet_service_vpn_DEPENDENCIES = \
+ $(top_builddir)/src/statistics/libgnunetstatistics.la \
+ $(top_builddir)/src/tun/libgnunettun.la \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/mesh/libgnunetmesh.la \
+ $(am__DEPENDENCIES_1)
+gnunet_service_vpn_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(gnunet_service_vpn_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+am_gnunet_vpn_OBJECTS = gnunet-vpn.$(OBJEXT)
+gnunet_vpn_OBJECTS = $(am_gnunet_vpn_OBJECTS)
+am_test_gnunet_vpn_4_over_OBJECTS = test_gnunet_vpn.$(OBJEXT)
+test_gnunet_vpn_4_over_OBJECTS = $(am_test_gnunet_vpn_4_over_OBJECTS)
+test_gnunet_vpn_4_over_DEPENDENCIES = \
+ $(top_builddir)/src/vpn/libgnunetvpn.la \
+ $(top_builddir)/src/arm/libgnunetarm.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_gnunet_vpn_4_to_6_OBJECTS = test_gnunet_vpn.$(OBJEXT)
+test_gnunet_vpn_4_to_6_OBJECTS = $(am_test_gnunet_vpn_4_to_6_OBJECTS)
+test_gnunet_vpn_4_to_6_DEPENDENCIES = \
+ $(top_builddir)/src/vpn/libgnunetvpn.la \
+ $(top_builddir)/src/arm/libgnunetarm.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_gnunet_vpn_6_over_OBJECTS = test_gnunet_vpn.$(OBJEXT)
+test_gnunet_vpn_6_over_OBJECTS = $(am_test_gnunet_vpn_6_over_OBJECTS)
+test_gnunet_vpn_6_over_DEPENDENCIES = \
+ $(top_builddir)/src/vpn/libgnunetvpn.la \
+ $(top_builddir)/src/arm/libgnunetarm.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_gnunet_vpn_6_to_4_OBJECTS = test_gnunet_vpn.$(OBJEXT)
+test_gnunet_vpn_6_to_4_OBJECTS = $(am_test_gnunet_vpn_6_to_4_OBJECTS)
+test_gnunet_vpn_6_to_4_DEPENDENCIES = \
+ $(top_builddir)/src/vpn/libgnunetvpn.la \
+ $(top_builddir)/src/arm/libgnunetarm.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+SOURCES = $(libgnunetvpn_la_SOURCES) $(gnunet_helper_vpn_SOURCES) \
+ $(gnunet_service_vpn_SOURCES) $(gnunet_vpn_SOURCES) \
+ $(test_gnunet_vpn_4_over_SOURCES) \
+ $(test_gnunet_vpn_4_to_6_SOURCES) \
+ $(test_gnunet_vpn_6_over_SOURCES) \
+ $(test_gnunet_vpn_6_to_4_SOURCES)
+DIST_SOURCES = $(libgnunetvpn_la_SOURCES) $(gnunet_helper_vpn_SOURCES) \
+ $(gnunet_service_vpn_SOURCES) $(gnunet_vpn_SOURCES) \
+ $(test_gnunet_vpn_4_over_SOURCES) \
+ $(test_gnunet_vpn_4_to_6_SOURCES) \
+ $(test_gnunet_vpn_6_over_SOURCES) \
+ $(test_gnunet_vpn_6_to_4_SOURCES)
+DATA = $(pkgcfg_DATA)
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors = \
+red=; grn=; lgn=; blu=; std=
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ARGZ_H = @ARGZ_H@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFAULT_INTERFACE = @DEFAULT_INTERFACE@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLDIR = @DLLDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXT_LIBS = @EXT_LIBS@
+EXT_LIB_PATH = @EXT_LIB_PATH@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNUNETDNS_GROUP = @GNUNETDNS_GROUP@
+GN_DAEMON_CONFIG_DIR = @GN_DAEMON_CONFIG_DIR@
+GN_DAEMON_HOME_DIR = @GN_DAEMON_HOME_DIR@
+GN_INTLINCL = @GN_INTLINCL@
+GN_LIBINTL = @GN_LIBINTL@
+GN_LIB_LDFLAGS = @GN_LIB_LDFLAGS@
+GN_PLUGIN_LDFLAGS = @GN_PLUGIN_LDFLAGS@
+GN_USER_HOME_DIR = @GN_USER_HOME_DIR@
+GREP = @GREP@
+HAVE_LIBUNISTRING = @HAVE_LIBUNISTRING@
+INCLTDL = @INCLTDL@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBADD_DL = @LIBADD_DL@
+LIBADD_DLD_LINK = @LIBADD_DLD_LINK@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
+LIBADD_SHL_LOAD = @LIBADD_SHL_LOAD@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBLTDL = @LIBLTDL@
+LIBOBJS = @LIBOBJS@
+LIBPREFIX = @LIBPREFIX@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNISTRING = @LIBUNISTRING@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTDLDEPS = @LTDLDEPS@
+LTDLINCL = @LTDLINCL@
+LTDLOPEN = @LTDLOPEN@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+LTLIBUNISTRING = @LTLIBUNISTRING@
+LT_CONFIG_H = @LT_CONFIG_H@
+LT_DLLOADERS = @LT_DLLOADERS@
+LT_DLPREOPEN = @LT_DLPREOPEN@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@
+MYSQL_LDFLAGS = @MYSQL_LDFLAGS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJC = @OBJC@
+OBJCDEPMODE = @OBJCDEPMODE@
+OBJCFLAGS = @OBJCFLAGS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSTGRES_CPPFLAGS = @POSTGRES_CPPFLAGS@
+POSTGRES_LDFLAGS = @POSTGRES_LDFLAGS@
+POSUB = @POSUB@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SQLITE_CPPFLAGS = @SQLITE_CPPFLAGS@
+SQLITE_LDFLAGS = @SQLITE_LDFLAGS@
+STRIP = @STRIP@
+SUDO_BINARY = @SUDO_BINARY@
+UNIXONLY = @UNIXONLY@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XMKMF = @XMKMF@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+_libcurl_config = @_libcurl_config@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_OBJC = @ac_ct_OBJC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_target = @build_target@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+ltdl_LIBOBJS = @ltdl_LIBOBJS@
+ltdl_LTLIBOBJS = @ltdl_LTLIBOBJS@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sys_symbol_underscore = @sys_symbol_underscore@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = -I$(top_srcdir)/src/include
+@MINGW_TRUE@WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
+@USE_COVERAGE_TRUE@AM_CFLAGS = --coverage -O0
+pkgcfgdir = $(pkgdatadir)/config.d/
+plugindir = $(libdir)/gnunet
+pkgcfg_DATA = \
+ vpn.conf
+
+@LINUX_TRUE@VPNBIN = gnunet-helper-vpn
+@HAVE_MHD_TRUE@@LINUX_TRUE@VPN_TEST = \
+@HAVE_MHD_TRUE@@LINUX_TRUE@ test_gnunet_vpn-4_to_6 \
+@HAVE_MHD_TRUE@@LINUX_TRUE@ test_gnunet_vpn-6_to_4 \
+@HAVE_MHD_TRUE@@LINUX_TRUE@ test_gnunet_vpn-6_over \
+@HAVE_MHD_TRUE@@LINUX_TRUE@ test_gnunet_vpn-4_over
+
+lib_LTLIBRARIES = \
+ libgnunetvpn.la
+
+@ENABLE_TEST_RUN_TRUE@TESTS = $(check_PROGRAMS)
+EXTRA_DIST = \
+ test_gnunet_vpn.conf
+
+gnunet_helper_vpn_SOURCES = \
+ gnunet-helper-vpn.c
+
+gnunet_service_vpn_SOURCES = \
+ gnunet-service-vpn.c
+
+gnunet_service_vpn_LDADD = \
+ $(top_builddir)/src/statistics/libgnunetstatistics.la \
+ $(top_builddir)/src/tun/libgnunettun.la \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(top_builddir)/src/mesh/libgnunetmesh.la \
+ $(GN_LIBINTL)
+
+gnunet_service_vpn_CFLAGS = \
+ -I$(top_srcdir)/src/exit $(CFLAGS)
+
+gnunet_vpn_SOURCES = \
+ gnunet-vpn.c
+
+gnunet_vpn_LDADD = \
+ $(top_builddir)/src/vpn/libgnunetvpn.la \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(GN_LIBINTL)
+
+gnunet_vpn_DEPENDENCIES = \
+ libgnunetvpn.la
+
+libgnunetvpn_la_SOURCES = \
+ vpn_api.c vpn.h
+
+libgnunetvpn_la_LIBADD = \
+ $(top_builddir)/src/util/libgnunetutil.la $(XLIB)
+
+libgnunetvpn_la_LDFLAGS = \
+ $(GN_LIB_LDFLAGS)
+
+test_gnunet_vpn_4_over_SOURCES = \
+ test_gnunet_vpn.c
+
+test_gnunet_vpn_4_over_LDADD = -lmicrohttpd @LIBCURL@ \
+ $(top_builddir)/src/vpn/libgnunetvpn.la \
+ $(top_builddir)/src/arm/libgnunetarm.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_gnunet_vpn_6_over_SOURCES = \
+ test_gnunet_vpn.c
+
+test_gnunet_vpn_6_over_LDADD = -lmicrohttpd @LIBCURL@ \
+ $(top_builddir)/src/vpn/libgnunetvpn.la \
+ $(top_builddir)/src/arm/libgnunetarm.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_gnunet_vpn_4_to_6_SOURCES = \
+ test_gnunet_vpn.c
+
+test_gnunet_vpn_4_to_6_LDADD = -lmicrohttpd @LIBCURL@ \
+ $(top_builddir)/src/vpn/libgnunetvpn.la \
+ $(top_builddir)/src/arm/libgnunetarm.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_gnunet_vpn_6_to_4_SOURCES = \
+ test_gnunet_vpn.c
+
+test_gnunet_vpn_6_to_4_LDADD = -lmicrohttpd @LIBCURL@ \
+ $(top_builddir)/src/vpn/libgnunetvpn.la \
+ $(top_builddir)/src/arm/libgnunetarm.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/vpn/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/vpn/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+vpn.conf: $(top_builddir)/config.status $(srcdir)/vpn.conf.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libgnunetvpn.la: $(libgnunetvpn_la_OBJECTS) $(libgnunetvpn_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libgnunetvpn_la_LINK) -rpath $(libdir) $(libgnunetvpn_la_OBJECTS) $(libgnunetvpn_la_LIBADD) $(LIBS)
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+clean-checkPROGRAMS:
+ @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+gnunet-helper-vpn$(EXEEXT): $(gnunet_helper_vpn_OBJECTS) $(gnunet_helper_vpn_DEPENDENCIES)
+ @rm -f gnunet-helper-vpn$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(gnunet_helper_vpn_OBJECTS) $(gnunet_helper_vpn_LDADD) $(LIBS)
+gnunet-service-vpn$(EXEEXT): $(gnunet_service_vpn_OBJECTS) $(gnunet_service_vpn_DEPENDENCIES)
+ @rm -f gnunet-service-vpn$(EXEEXT)
+ $(AM_V_CCLD)$(gnunet_service_vpn_LINK) $(gnunet_service_vpn_OBJECTS) $(gnunet_service_vpn_LDADD) $(LIBS)
+gnunet-vpn$(EXEEXT): $(gnunet_vpn_OBJECTS) $(gnunet_vpn_DEPENDENCIES)
+ @rm -f gnunet-vpn$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(gnunet_vpn_OBJECTS) $(gnunet_vpn_LDADD) $(LIBS)
+test_gnunet_vpn-4_over$(EXEEXT): $(test_gnunet_vpn_4_over_OBJECTS) $(test_gnunet_vpn_4_over_DEPENDENCIES)
+ @rm -f test_gnunet_vpn-4_over$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_gnunet_vpn_4_over_OBJECTS) $(test_gnunet_vpn_4_over_LDADD) $(LIBS)
+test_gnunet_vpn-4_to_6$(EXEEXT): $(test_gnunet_vpn_4_to_6_OBJECTS) $(test_gnunet_vpn_4_to_6_DEPENDENCIES)
+ @rm -f test_gnunet_vpn-4_to_6$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_gnunet_vpn_4_to_6_OBJECTS) $(test_gnunet_vpn_4_to_6_LDADD) $(LIBS)
+test_gnunet_vpn-6_over$(EXEEXT): $(test_gnunet_vpn_6_over_OBJECTS) $(test_gnunet_vpn_6_over_DEPENDENCIES)
+ @rm -f test_gnunet_vpn-6_over$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_gnunet_vpn_6_over_OBJECTS) $(test_gnunet_vpn_6_over_LDADD) $(LIBS)
+test_gnunet_vpn-6_to_4$(EXEEXT): $(test_gnunet_vpn_6_to_4_OBJECTS) $(test_gnunet_vpn_6_to_4_DEPENDENCIES)
+ @rm -f test_gnunet_vpn-6_to_4$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_gnunet_vpn_6_to_4_OBJECTS) $(test_gnunet_vpn_6_to_4_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-helper-vpn.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-vpn.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet_service_vpn-gnunet-service-vpn.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_gnunet_vpn.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vpn_api.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+gnunet_service_vpn-gnunet-service-vpn.o: gnunet-service-vpn.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_vpn_CFLAGS) $(CFLAGS) -MT gnunet_service_vpn-gnunet-service-vpn.o -MD -MP -MF $(DEPDIR)/gnunet_service_vpn-gnunet-service-vpn.Tpo -c -o gnunet_service_vpn-gnunet-service-vpn.o `test -f 'gnunet-service-vpn.c' || echo '$(srcdir)/'`gnunet-service-vpn.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gnunet_service_vpn-gnunet-service-vpn.Tpo $(DEPDIR)/gnunet_service_vpn-gnunet-service-vpn.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gnunet-service-vpn.c' object='gnunet_service_vpn-gnunet-service-vpn.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_vpn_CFLAGS) $(CFLAGS) -c -o gnunet_service_vpn-gnunet-service-vpn.o `test -f 'gnunet-service-vpn.c' || echo '$(srcdir)/'`gnunet-service-vpn.c
+
+gnunet_service_vpn-gnunet-service-vpn.obj: gnunet-service-vpn.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_vpn_CFLAGS) $(CFLAGS) -MT gnunet_service_vpn-gnunet-service-vpn.obj -MD -MP -MF $(DEPDIR)/gnunet_service_vpn-gnunet-service-vpn.Tpo -c -o gnunet_service_vpn-gnunet-service-vpn.obj `if test -f 'gnunet-service-vpn.c'; then $(CYGPATH_W) 'gnunet-service-vpn.c'; else $(CYGPATH_W) '$(srcdir)/gnunet-service-vpn.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gnunet_service_vpn-gnunet-service-vpn.Tpo $(DEPDIR)/gnunet_service_vpn-gnunet-service-vpn.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='gnunet-service-vpn.c' object='gnunet_service_vpn-gnunet-service-vpn.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_vpn_CFLAGS) $(CFLAGS) -c -o gnunet_service_vpn-gnunet-service-vpn.obj `if test -f 'gnunet-service-vpn.c'; then $(CYGPATH_W) 'gnunet-service-vpn.c'; else $(CYGPATH_W) '$(srcdir)/gnunet-service-vpn.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-pkgcfgDATA: $(pkgcfg_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(pkgcfgdir)" || $(MKDIR_P) "$(DESTDIR)$(pkgcfgdir)"
+ @list='$(pkgcfg_DATA)'; test -n "$(pkgcfgdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgcfgdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgcfgdir)" || exit $$?; \
+ done
+
+uninstall-pkgcfgDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkgcfg_DATA)'; test -n "$(pkgcfgdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(pkgcfgdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(pkgcfgdir)" && rm -f $$files
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ $(am__tty_colors); \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=XPASS; \
+ ;; \
+ *) \
+ col=$$grn; res=PASS; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xfail=`expr $$xfail + 1`; \
+ col=$$lgn; res=XFAIL; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=FAIL; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ col=$$blu; res=SKIP; \
+ fi; \
+ echo "$${col}$$res$${std}: $$tst"; \
+ done; \
+ if test "$$all" -eq 1; then \
+ tests="test"; \
+ All=""; \
+ else \
+ tests="tests"; \
+ All="All "; \
+ fi; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="$$All$$all $$tests passed"; \
+ else \
+ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all $$tests failed"; \
+ else \
+ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ if test "$$skip" -eq 1; then \
+ skipped="($$skip test was not run)"; \
+ else \
+ skipped="($$skip tests were not run)"; \
+ fi; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ if test "$$failed" -eq 0; then \
+ echo "$$grn$$dashes"; \
+ else \
+ echo "$$red$$dashes"; \
+ fi; \
+ echo "$$banner"; \
+ test -z "$$skipped" || echo "$$skipped"; \
+ test -z "$$report" || echo "$$report"; \
+ echo "$$dashes$$std"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(DATA)
+install-binPROGRAMS: install-libLTLIBRARIES
+
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgcfgdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
+ clean-libLTLIBRARIES clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-pkgcfgDATA
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS install-libLTLIBRARIES
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-libLTLIBRARIES \
+ uninstall-pkgcfgDATA
+
+.MAKE: check-am install-am install-exec-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \
+ clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
+ clean-libLTLIBRARIES clean-libtool ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-binPROGRAMS install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-exec-hook install-html install-html-am \
+ install-info install-info-am install-libLTLIBRARIES \
+ install-man install-pdf install-pdf-am install-pkgcfgDATA \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-binPROGRAMS \
+ uninstall-libLTLIBRARIES uninstall-pkgcfgDATA
+
+@LINUX_TRUE@install-exec-hook:
+@LINUX_TRUE@ $(SUDO_BINARY) chown root:root $(bindir)/gnunet-helper-vpn || true
+@LINUX_TRUE@ $(SUDO_BINARY) chmod u+s $(bindir)/gnunet-helper-vpn || true
+@LINUX_FALSE@install-exec-hook:
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/vpn/gnunet-helper-vpn.c b/src/vpn/gnunet-helper-vpn.c
new file mode 100644
index 0000000..7c09827
--- /dev/null
+++ b/src/vpn/gnunet-helper-vpn.c
@@ -0,0 +1,616 @@
+/*
+ This file is part of GNUnet.
+ (C) 2010, 2012 Christian Grothoff
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet 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 GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file vpn/gnunet-helper-vpn.c
+ * @brief the helper for the VPN service. Opens a virtual network-interface,
+ * sends data received on the if to stdout, sends data received on stdin to the
+ * interface
+ * @author Philipp Tölke
+ * @author Christian Grothoff
+ *
+ * The following list of people have reviewed this code and considered
+ * it safe since the last modification (if you reviewed it, please
+ * have your name added to the list):
+ *
+ * - Philipp Tölke
+ */
+#include "platform.h"
+#include <linux/if_tun.h>
+
+/**
+ * Need 'struct GNUNET_MessageHeader'.
+ */
+#include "gnunet_common.h"
+
+/**
+ * Need VPN message types.
+ */
+#include "gnunet_protocols.h"
+
+/**
+ * Should we print (interesting|debug) messages that can happen during
+ * normal operation?
+ */
+#define DEBUG GNUNET_NO
+
+/**
+ * Maximum size of a GNUnet message (GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ */
+#define MAX_SIZE 65536
+
+#ifndef _LINUX_IN6_H
+/**
+ * This is in linux/include/net/ipv6.h, but not always exported...
+ */
+struct in6_ifreq
+{
+ struct in6_addr ifr6_addr;
+ uint32_t ifr6_prefixlen;
+ unsigned int ifr6_ifindex;
+};
+#endif
+
+
+/**
+ * Creates a tun-interface called dev;
+ *
+ * @param dev is asumed to point to a char[IFNAMSIZ]
+ * if *dev == '\\0', uses the name supplied by the kernel;
+ * @return the fd to the tun or -1 on error
+ */
+static int
+init_tun (char *dev)
+{
+ struct ifreq ifr;
+ int fd;
+
+ if (NULL == dev)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (-1 == (fd = open ("/dev/net/tun", O_RDWR)))
+ {
+ fprintf (stderr, "Error opening `%s': %s\n", "/dev/net/tun",
+ strerror (errno));
+ return -1;
+ }
+
+ if (fd >= FD_SETSIZE)
+ {
+ fprintf (stderr, "File descriptor to large: %d", fd);
+ (void) close (fd);
+ return -1;
+ }
+
+ memset (&ifr, 0, sizeof (ifr));
+ ifr.ifr_flags = IFF_TUN;
+
+ if ('\0' != *dev)
+ strncpy (ifr.ifr_name, dev, IFNAMSIZ);
+
+ if (-1 == ioctl (fd, TUNSETIFF, (void *) &ifr))
+ {
+ fprintf (stderr, "Error with ioctl on `%s': %s\n", "/dev/net/tun",
+ strerror (errno));
+ (void) close (fd);
+ return -1;
+ }
+ strcpy (dev, ifr.ifr_name);
+ return fd;
+}
+
+
+/**
+ * @brief Sets the IPv6-Address given in address on the interface dev
+ *
+ * @param dev the interface to configure
+ * @param address the IPv6-Address
+ * @param prefix_len the length of the network-prefix
+ */
+static void
+set_address6 (const char *dev, const char *address, unsigned long prefix_len)
+{
+ struct ifreq ifr;
+ struct in6_ifreq ifr6;
+ struct sockaddr_in6 sa6;
+ int fd;
+
+ /*
+ * parse the new address
+ */
+ memset (&sa6, 0, sizeof (struct sockaddr_in6));
+ sa6.sin6_family = AF_INET6;
+ if (1 != inet_pton (AF_INET6, address, sa6.sin6_addr.s6_addr))
+ {
+ fprintf (stderr, "Failed to parse address `%s': %s\n", address,
+ strerror (errno));
+ exit (1);
+ }
+
+ if (-1 == (fd = socket (PF_INET6, SOCK_DGRAM, 0)))
+ {
+ fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
+ exit (1);
+ }
+
+ memset (&ifr, 0, sizeof (struct ifreq));
+ /*
+ * Get the index of the if
+ */
+ strncpy (ifr.ifr_name, dev, IFNAMSIZ);
+ if (-1 == ioctl (fd, SIOGIFINDEX, &ifr))
+ {
+ fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ memset (&ifr6, 0, sizeof (struct in6_ifreq));
+ ifr6.ifr6_addr = sa6.sin6_addr;
+ ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+ ifr6.ifr6_prefixlen = prefix_len;
+
+ /*
+ * Set the address
+ */
+ if (-1 == ioctl (fd, SIOCSIFADDR, &ifr6))
+ {
+ fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+ strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ /*
+ * Get the flags
+ */
+ if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
+ {
+ fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+ strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ /*
+ * Add the UP and RUNNING flags
+ */
+ ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
+ if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
+ {
+ fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+ strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ if (0 != close (fd))
+ {
+ fprintf (stderr, "close failed: %s\n", strerror (errno));
+ exit (1);
+ }
+}
+
+
+/**
+ * @brief Sets the IPv4-Address given in address on the interface dev
+ *
+ * @param dev the interface to configure
+ * @param address the IPv4-Address
+ * @param mask the netmask
+ */
+static void
+set_address4 (const char *dev, const char *address, const char *mask)
+{
+ int fd;
+ struct sockaddr_in *addr;
+ struct ifreq ifr;
+
+ memset (&ifr, 0, sizeof (struct ifreq));
+ addr = (struct sockaddr_in *) &(ifr.ifr_addr);
+ addr->sin_family = AF_INET;
+
+ /*
+ * Parse the address
+ */
+ if (1 != inet_pton (AF_INET, address, &addr->sin_addr.s_addr))
+ {
+ fprintf (stderr, "Failed to parse address `%s': %s\n", address,
+ strerror (errno));
+ exit (1);
+ }
+
+ if (-1 == (fd = socket (PF_INET, SOCK_DGRAM, 0)))
+ {
+ fprintf (stderr, "Error creating socket: %s\n", strerror (errno));
+ exit (1);
+ }
+
+ strncpy (ifr.ifr_name, dev, IFNAMSIZ);
+
+ /*
+ * Set the address
+ */
+ if (-1 == ioctl (fd, SIOCSIFADDR, &ifr))
+ {
+ fprintf (stderr, "ioctl failed at %d: %s\n", __LINE__, strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ /*
+ * Parse the netmask
+ */
+ addr = (struct sockaddr_in *) &(ifr.ifr_netmask);
+ if (1 != inet_pton (AF_INET, mask, &addr->sin_addr.s_addr))
+ {
+ fprintf (stderr, "Failed to parse address `%s': %s\n", mask,
+ strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ /*
+ * Set the netmask
+ */
+ if (-1 == ioctl (fd, SIOCSIFNETMASK, &ifr))
+ {
+ fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+ strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ /*
+ * Get the flags
+ */
+ if (-1 == ioctl (fd, SIOCGIFFLAGS, &ifr))
+ {
+ fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+ strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ /*
+ * Add the UP and RUNNING flags
+ */
+ ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
+ if (-1 == ioctl (fd, SIOCSIFFLAGS, &ifr))
+ {
+ fprintf (stderr, "ioctl failed at line %d: %s\n", __LINE__,
+ strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+
+ if (0 != close (fd))
+ {
+ fprintf (stderr, "close failed: %s\n", strerror (errno));
+ (void) close (fd);
+ exit (1);
+ }
+}
+
+
+/**
+ * Start forwarding to and from the tunnel.
+ *
+ * @param fd_tun tunnel FD
+ */
+static void
+run (int fd_tun)
+{
+ /*
+ * The buffer filled by reading from fd_tun
+ */
+ unsigned char buftun[MAX_SIZE];
+ ssize_t buftun_size = 0;
+ unsigned char *buftun_read = NULL;
+
+ /*
+ * The buffer filled by reading from stdin
+ */
+ unsigned char bufin[MAX_SIZE];
+ ssize_t bufin_size = 0;
+ size_t bufin_rpos = 0;
+ unsigned char *bufin_read = NULL;
+
+ fd_set fds_w;
+ fd_set fds_r;
+
+ /* read refers to reading from fd_tun, writing to stdout */
+ int read_open = 1;
+
+ /* write refers to reading from stdin, writing to fd_tun */
+ int write_open = 1;
+
+ while ((1 == read_open) || (1 == write_open))
+ {
+ FD_ZERO (&fds_w);
+ FD_ZERO (&fds_r);
+
+ /*
+ * We are supposed to read and the buffer is empty
+ * -> select on read from tun
+ */
+ if (read_open && (0 == buftun_size))
+ FD_SET (fd_tun, &fds_r);
+
+ /*
+ * We are supposed to read and the buffer is not empty
+ * -> select on write to stdout
+ */
+ if (read_open && (0 != buftun_size))
+ FD_SET (1, &fds_w);
+
+ /*
+ * We are supposed to write and the buffer is empty
+ * -> select on read from stdin
+ */
+ if (write_open && (NULL == bufin_read))
+ FD_SET (0, &fds_r);
+
+ /*
+ * We are supposed to write and the buffer is not empty
+ * -> select on write to tun
+ */
+ if (write_open && (NULL != bufin_read))
+ FD_SET (fd_tun, &fds_w);
+
+ int r = select (fd_tun + 1, &fds_r, &fds_w, NULL, NULL);
+
+ if (-1 == r)
+ {
+ if (EINTR == errno)
+ continue;
+ fprintf (stderr, "select failed: %s\n", strerror (errno));
+ exit (1);
+ }
+
+ if (r > 0)
+ {
+ if (FD_ISSET (fd_tun, &fds_r))
+ {
+ buftun_size =
+ read (fd_tun, buftun + sizeof (struct GNUNET_MessageHeader),
+ MAX_SIZE - sizeof (struct GNUNET_MessageHeader));
+ if (-1 == buftun_size)
+ {
+ fprintf (stderr, "read-error: %s\n", strerror (errno));
+ shutdown (fd_tun, SHUT_RD);
+ shutdown (1, SHUT_WR);
+ read_open = 0;
+ buftun_size = 0;
+ }
+ else if (0 == buftun_size)
+ {
+ fprintf (stderr, "EOF on tun\n");
+ shutdown (fd_tun, SHUT_RD);
+ shutdown (1, SHUT_WR);
+ read_open = 0;
+ buftun_size = 0;
+ }
+ else
+ {
+ buftun_read = buftun;
+ struct GNUNET_MessageHeader *hdr =
+ (struct GNUNET_MessageHeader *) buftun;
+ buftun_size += sizeof (struct GNUNET_MessageHeader);
+ hdr->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ hdr->size = htons (buftun_size);
+ }
+ }
+ else if (FD_ISSET (1, &fds_w))
+ {
+ ssize_t written = write (1, buftun_read, buftun_size);
+
+ if (-1 == written)
+ {
+#if !DEBUG
+ if (errno != EPIPE)
+#endif
+ fprintf (stderr, "write-error to stdout: %s\n", strerror (errno));
+ shutdown (fd_tun, SHUT_RD);
+ shutdown (1, SHUT_WR);
+ read_open = 0;
+ buftun_size = 0;
+ }
+ else if (0 == written)
+ {
+ fprintf (stderr, "write returned 0!?\n");
+ exit (1);
+ }
+ else
+ {
+ buftun_size -= written;
+ buftun_read += written;
+ }
+ }
+
+ if (FD_ISSET (0, &fds_r))
+ {
+ bufin_size = read (0, bufin + bufin_rpos, MAX_SIZE - bufin_rpos);
+ if (-1 == bufin_size)
+ {
+ fprintf (stderr, "read-error: %s\n", strerror (errno));
+ shutdown (0, SHUT_RD);
+ shutdown (fd_tun, SHUT_WR);
+ write_open = 0;
+ bufin_size = 0;
+ }
+ else if (0 == bufin_size)
+ {
+#if DEBUG
+ fprintf (stderr, "EOF on stdin\n");
+#endif
+ shutdown (0, SHUT_RD);
+ shutdown (fd_tun, SHUT_WR);
+ write_open = 0;
+ bufin_size = 0;
+ }
+ else
+ {
+ struct GNUNET_MessageHeader *hdr;
+
+PROCESS_BUFFER:
+ bufin_rpos += bufin_size;
+ if (bufin_rpos < sizeof (struct GNUNET_MessageHeader))
+ continue;
+ hdr = (struct GNUNET_MessageHeader *) bufin;
+ if (ntohs (hdr->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER)
+ {
+ fprintf (stderr, "protocol violation!\n");
+ exit (1);
+ }
+ if (ntohs (hdr->size) > bufin_rpos)
+ continue;
+ bufin_read = bufin + sizeof (struct GNUNET_MessageHeader);
+ bufin_size = ntohs (hdr->size) - sizeof (struct GNUNET_MessageHeader);
+ bufin_rpos -= bufin_size + sizeof (struct GNUNET_MessageHeader);
+ }
+ }
+ else if (FD_ISSET (fd_tun, &fds_w))
+ {
+ ssize_t written = write (fd_tun, bufin_read, bufin_size);
+
+ if (-1 == written)
+ {
+ fprintf (stderr, "write-error to tun: %s\n", strerror (errno));
+ shutdown (0, SHUT_RD);
+ shutdown (fd_tun, SHUT_WR);
+ write_open = 0;
+ bufin_size = 0;
+ }
+ else if (0 == written)
+ {
+ fprintf (stderr, "write returned 0!?\n");
+ exit (1);
+ }
+ else
+ {
+ bufin_size -= written;
+ bufin_read += written;
+ if (0 == bufin_size)
+ {
+ memmove (bufin, bufin_read, bufin_rpos);
+ bufin_read = NULL; /* start reading again */
+ bufin_size = 0;
+ goto PROCESS_BUFFER;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * Open VPN tunnel interface.
+ *
+ * @param argc must be 6
+ * @param argv 0: binary name (gnunet-helper-vpn)
+ * 1: tunnel interface name (gnunet-vpn)
+ * 2: IPv6 address (::1), "-" to disable
+ * 3: IPv6 netmask length in bits (64), ignored if #2 is "-"
+ * 4: IPv4 address (1.2.3.4), "-" to disable
+ * 5: IPv4 netmask (255.255.0.0), ignored if #4 is "-"
+ */
+int
+main (int argc, char **argv)
+{
+ char dev[IFNAMSIZ];
+ int fd_tun;
+ int global_ret;
+
+ if (6 != argc)
+ {
+ fprintf (stderr, "Fatal: must supply 5 arguments!\n");
+ return 1;
+ }
+
+ strncpy (dev, argv[1], IFNAMSIZ);
+ dev[IFNAMSIZ - 1] = '\0';
+
+ if (-1 == (fd_tun = init_tun (dev)))
+ {
+ fprintf (stderr, "Fatal: could not initialize tun-interface `%s' with IPv6 %s/%s and IPv4 %s/%s\n",
+ dev,
+ argv[2],
+ argv[3],
+ argv[4],
+ argv[5]);
+ return 1;
+ }
+
+ if (0 != strcmp (argv[2], "-"))
+ {
+ const char *address = argv[2];
+ long prefix_len = atol (argv[3]);
+
+ if ((prefix_len < 1) || (prefix_len > 127))
+ {
+ fprintf (stderr, "Fatal: prefix_len out of range\n");
+ return 1;
+ }
+
+ set_address6 (dev, address, prefix_len);
+ }
+
+ if (0 != strcmp (argv[4], "-"))
+ {
+ const char *address = argv[4];
+ const char *mask = argv[5];
+
+ set_address4 (dev, address, mask);
+ }
+
+ uid_t uid = getuid ();
+#ifdef HAVE_SETRESUID
+ if (0 != setresuid (uid, uid, uid))
+ {
+ fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
+ global_ret = 2;
+ goto cleanup;
+ }
+#else
+ if (0 != (setuid (uid) | seteuid (uid)))
+ {
+ fprintf (stderr, "Failed to setuid: %s\n", strerror (errno));
+ global_ret = 2;
+ goto cleanup;
+ }
+#endif
+
+ if (SIG_ERR == signal (SIGPIPE, SIG_IGN))
+ {
+ fprintf (stderr, "Failed to protect against SIGPIPE: %s\n",
+ strerror (errno));
+ /* no exit, we might as well die with SIGPIPE should it ever happen */
+ }
+ run (fd_tun);
+ global_ret = 0;
+ cleanup:
+ close (fd_tun);
+ return global_ret;
+}
diff --git a/src/vpn/gnunet-service-vpn.c b/src/vpn/gnunet-service-vpn.c
new file mode 100644
index 0000000..26deeee
--- /dev/null
+++ b/src/vpn/gnunet-service-vpn.c
@@ -0,0 +1,3202 @@
+/*
+ This file is part of GNUnet.
+ (C) 2010, 2011, 2012 Christian Grothoff
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet 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 GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file vpn/gnunet-service-vpn.c
+ * @brief service that opens a virtual interface and allows its clients
+ * to allocate IPs on the virtual interface and to then redirect
+ * IP traffic received on those IPs via the GNUnet mesh
+ * @author Philipp Toelke
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_common.h"
+#include "gnunet_protocols.h"
+#include "gnunet_applications.h"
+#include "gnunet_mesh_service.h"
+#include "gnunet_statistics_service.h"
+#include "gnunet_constants.h"
+#include "gnunet_tun_lib.h"
+#include "vpn.h"
+#include "exit.h"
+
+
+/**
+ * Maximum number of messages we allow in the queue for mesh.
+ */
+#define MAX_MESSAGE_QUEUE_SIZE 4
+
+
+/**
+ * State we keep for each of our tunnels.
+ */
+struct TunnelState;
+
+
+/**
+ * Information we track for each IP address to determine which tunnel
+ * to send the traffic over to the destination.
+ */
+struct DestinationEntry
+{
+
+ /**
+ * Key under which this entry is in the 'destination_map' (only valid
+ * if 'heap_node != NULL').
+ */
+ GNUNET_HashCode key;
+
+ /**
+ * Pre-allocated tunnel for this destination, or NULL for none.
+ */
+ struct TunnelState *ts;
+
+ /**
+ * Entry for this entry in the destination_heap.
+ */
+ struct GNUNET_CONTAINER_HeapNode *heap_node;
+
+ /**
+ * GNUNET_NO if this is a tunnel to an Internet-exit,
+ * GNUNET_YES if this tunnel is to a service.
+ */
+ int is_service;
+
+ /**
+ * Details about the connection (depending on is_service).
+ */
+ union
+ {
+
+ struct
+ {
+ /**
+ * The description of the service (only used for service tunnels).
+ */
+ GNUNET_HashCode service_descriptor;
+
+ /**
+ * Peer offering the service.
+ */
+ struct GNUNET_PeerIdentity target;
+
+ } service_destination;
+
+ struct
+ {
+
+ /**
+ * Address family used (AF_INET or AF_INET6).
+ */
+ int af;
+
+ /**
+ * IP address of the ultimate destination (only used for exit tunnels).
+ */
+ union
+ {
+ /**
+ * Address if af is AF_INET.
+ */
+ struct in_addr v4;
+
+ /**
+ * Address if af is AF_INET6.
+ */
+ struct in6_addr v6;
+ } ip;
+
+ } exit_destination;
+
+ } details;
+
+};
+
+
+/**
+ * A messages we have in queue for a particular tunnel.
+ */
+struct TunnelMessageQueueEntry
+{
+ /**
+ * This is a doubly-linked list.
+ */
+ struct TunnelMessageQueueEntry *next;
+
+ /**
+ * This is a doubly-linked list.
+ */
+ struct TunnelMessageQueueEntry *prev;
+
+ /**
+ * Number of bytes in 'msg'.
+ */
+ size_t len;
+
+ /**
+ * Message to transmit, allocated at the end of this struct.
+ */
+ const void *msg;
+};
+
+
+/**
+ * State we keep for each of our tunnels.
+ */
+struct TunnelState
+{
+
+ /**
+ * Information about the tunnel to use, NULL if no tunnel
+ * is available right now.
+ */
+ struct GNUNET_MESH_Tunnel *tunnel;
+
+ /**
+ * Active transmission handle, NULL for none.
+ */
+ struct GNUNET_MESH_TransmitHandle *th;
+
+ /**
+ * Entry for this entry in the tunnel_heap, NULL as long as this
+ * tunnel state is not fully bound.
+ */
+ struct GNUNET_CONTAINER_HeapNode *heap_node;
+
+ /**
+ * Head of list of messages scheduled for transmission.
+ */
+ struct TunnelMessageQueueEntry *tmq_head;
+
+ /**
+ * Tail of list of messages scheduled for transmission.
+ */
+ struct TunnelMessageQueueEntry *tmq_tail;
+
+ /**
+ * Client that needs to be notified about the tunnel being
+ * up as soon as a peer is connected; NULL for none.
+ */
+ struct GNUNET_SERVER_Client *client;
+
+ /**
+ * Destination entry that has a pointer to this tunnel state;
+ * NULL if this tunnel state is in the tunnel map.
+ */
+ struct DestinationEntry *destination_container;
+
+ /**
+ * ID of the client request that caused us to setup this entry.
+ */
+ uint64_t request_id;
+
+ /**
+ * Destination to which this tunnel leads. Note that
+ * this struct is NOT in the destination_map (but a
+ * local copy) and that the 'heap_node' should always
+ * be NULL.
+ */
+ struct DestinationEntry destination;
+
+ /**
+ * Task scheduled to destroy the tunnel (or NO_TASK).
+ */
+ GNUNET_SCHEDULER_TaskIdentifier destroy_task;
+
+ /**
+ * Addess family used for this tunnel on the local TUN interface.
+ */
+ int af;
+
+ /**
+ * Length of the doubly linked 'tmq_head/tmq_tail' list.
+ */
+ unsigned int tmq_length;
+
+ /**
+ * IPPROTO_TCP or IPPROTO_UDP once bound.
+ */
+ uint8_t protocol;
+
+ /**
+ * IP address of the source on our end, initially uninitialized.
+ */
+ union
+ {
+ /**
+ * Address if af is AF_INET.
+ */
+ struct in_addr v4;
+
+ /**
+ * Address if af is AF_INET6.
+ */
+ struct in6_addr v6;
+
+ } source_ip;
+
+ /**
+ * Destination IP address used by the source on our end (this is the IP
+ * that we pick freely within the VPN's tunnel IP range).
+ */
+ union
+ {
+ /**
+ * Address if af is AF_INET.
+ */
+ struct in_addr v4;
+
+ /**
+ * Address if af is AF_INET6.
+ */
+ struct in6_addr v6;
+
+ } destination_ip;
+
+ /**
+ * Source port used by the sender on our end; 0 for uninitialized.
+ */
+ uint16_t source_port;
+
+ /**
+ * Destination port used by the sender on our end; 0 for uninitialized.
+ */
+ uint16_t destination_port;
+
+};
+
+
+/**
+ * Return value from 'main'.
+ */
+static int global_ret;
+
+/**
+ * Configuration we use.
+ */
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Handle to the mesh service.
+ */
+static struct GNUNET_MESH_Handle *mesh_handle;
+
+/**
+ * Map from IP address to destination information (possibly with a
+ * MESH tunnel handle for fast setup).
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *destination_map;
+
+/**
+ * Min-Heap sorted by activity time to expire old mappings.
+ */
+static struct GNUNET_CONTAINER_Heap *destination_heap;
+
+/**
+ * Map from source and destination address (IP+port) to connection
+ * information (mostly with the respective MESH tunnel handle).
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *tunnel_map;
+
+/**
+ * Min-Heap sorted by activity time to expire old mappings; values are
+ * of type 'struct TunnelState'.
+ */
+static struct GNUNET_CONTAINER_Heap *tunnel_heap;
+
+/**
+ * Statistics.
+ */
+static struct GNUNET_STATISTICS_Handle *stats;
+
+/**
+ * The handle to the VPN helper process "gnunet-helper-vpn".
+ */
+static struct GNUNET_HELPER_Handle *helper_handle;
+
+/**
+ * Arguments to the vpn helper.
+ */
+static char *vpn_argv[7];
+
+/**
+ * Length of the prefix of the VPN's IPv6 network.
+ */
+static unsigned long long ipv6prefix;
+
+/**
+ * Notification context for sending replies to clients.
+ */
+static struct GNUNET_SERVER_NotificationContext *nc;
+
+/**
+ * If there are more than this number of address-mappings, old ones
+ * will be removed
+ */
+static unsigned long long max_destination_mappings;
+
+/**
+ * If there are more than this number of open tunnels, old ones
+ * will be removed
+ */
+static unsigned long long max_tunnel_mappings;
+
+
+/**
+ * Compute the key under which we would store an entry in the
+ * destination_map for the given IP address.
+ *
+ * @param af address family (AF_INET or AF_INET6)
+ * @param address IP address, struct in_addr or struct in6_addr
+ * @param key where to store the key
+ */
+static void
+get_destination_key_from_ip (int af,
+ const void *address,
+ GNUNET_HashCode *key)
+{
+ switch (af)
+ {
+ case AF_INET:
+ GNUNET_CRYPTO_hash (address,
+ sizeof (struct in_addr),
+ key);
+ break;
+ case AF_INET6:
+ GNUNET_CRYPTO_hash (address,
+ sizeof (struct in6_addr),
+ key);
+ break;
+ default:
+ GNUNET_assert (0);
+ break;
+ }
+}
+
+
+/**
+ * Compute the key under which we would store an entry in the
+ * tunnel_map for the given socket address pair.
+ *
+ * @param af address family (AF_INET or AF_INET6)
+ * @param protocol IPPROTO_TCP or IPPROTO_UDP
+ * @param source_ip sender's source IP, struct in_addr or struct in6_addr
+ * @param source_port sender's source port
+ * @param destination_ip sender's destination IP, struct in_addr or struct in6_addr
+ * @param destination_port sender's destination port
+ * @param key where to store the key
+ */
+static void
+get_tunnel_key_from_ips (int af,
+ uint8_t protocol,
+ const void *source_ip,
+ uint16_t source_port,
+ const void *destination_ip,
+ uint16_t destination_port,
+ GNUNET_HashCode *key)
+{
+ char *off;
+
+ memset (key, 0, sizeof (GNUNET_HashCode));
+ /* the GNUnet hashmap only uses the first sizeof(unsigned int) of the hash,
+ so we put the ports in there (and hope for few collisions) */
+ off = (char*) key;
+ memcpy (off, &source_port, sizeof (uint16_t));
+ off += sizeof (uint16_t);
+ memcpy (off, &destination_port, sizeof (uint16_t));
+ off += sizeof (uint16_t);
+ switch (af)
+ {
+ case AF_INET:
+ memcpy (off, source_ip, sizeof (struct in_addr));
+ off += sizeof (struct in_addr);
+ memcpy (off, destination_ip, sizeof (struct in_addr));
+ off += sizeof (struct in_addr);
+ break;
+ case AF_INET6:
+ memcpy (off, source_ip, sizeof (struct in6_addr));
+ off += sizeof (struct in6_addr);
+ memcpy (off, destination_ip, sizeof (struct in6_addr));
+ off += sizeof (struct in6_addr);
+ break;
+ default:
+ GNUNET_assert (0);
+ break;
+ }
+ memcpy (off, &protocol, sizeof (uint8_t));
+ off += sizeof (uint8_t);
+}
+
+
+/**
+ * Notify the client about the result of its request.
+ *
+ * @param client client to notify
+ * @param request_id original request ID to include in response
+ * @param result_af resulting address family
+ * @param addr resulting IP address
+ */
+static void
+send_client_reply (struct GNUNET_SERVER_Client *client,
+ uint64_t request_id,
+ int result_af,
+ const void *addr)
+{
+ char buf[sizeof (struct RedirectToIpResponseMessage) + sizeof (struct in6_addr)];
+ struct RedirectToIpResponseMessage *res;
+ size_t rlen;
+
+ switch (result_af)
+ {
+ case AF_INET:
+ rlen = sizeof (struct in_addr);
+ break;
+ case AF_INET6:
+ rlen = sizeof (struct in6_addr);
+ break;
+ case AF_UNSPEC:
+ rlen = 0;
+ break;
+ default:
+ GNUNET_assert (0);
+ return;
+ }
+ res = (struct RedirectToIpResponseMessage *) buf;
+ res->header.size = htons (sizeof (struct RedirectToIpResponseMessage) + rlen);
+ res->header.type = htons (GNUNET_MESSAGE_TYPE_VPN_CLIENT_USE_IP);
+ res->result_af = htonl (result_af);
+ res->request_id = request_id;
+ memcpy (&res[1], addr, rlen);
+ GNUNET_SERVER_notification_context_add (nc, client);
+ GNUNET_SERVER_notification_context_unicast (nc,
+ client,
+ &res->header,
+ GNUNET_NO);
+}
+
+
+/**
+ * Free resources associated with a tunnel state.
+ *
+ * @param ts state to free
+ */
+static void
+free_tunnel_state (struct TunnelState *ts)
+{
+ GNUNET_HashCode key;
+ struct TunnelMessageQueueEntry *tnq;
+ struct GNUNET_MESH_Tunnel *tunnel;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Cleaning up tunnel state\n");
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Active tunnels"),
+ -1, GNUNET_NO);
+ while (NULL != (tnq = ts->tmq_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (ts->tmq_head,
+ ts->tmq_tail,
+ tnq);
+ ts->tmq_length--;
+ GNUNET_free (tnq);
+ }
+ GNUNET_assert (0 == ts->tmq_length);
+ if (NULL != ts->client)
+ {
+ GNUNET_SERVER_client_drop (ts->client);
+ ts->client = NULL;
+ }
+ if (NULL != ts->th)
+ {
+ GNUNET_MESH_notify_transmit_ready_cancel (ts->th);
+ ts->th = NULL;
+ }
+ GNUNET_assert (NULL == ts->destination.heap_node);
+ if (NULL != (tunnel = ts->tunnel))
+ {
+ ts->tunnel = NULL;
+ GNUNET_MESH_tunnel_destroy (tunnel);
+ }
+ if (GNUNET_SCHEDULER_NO_TASK != ts->destroy_task)
+ {
+ GNUNET_SCHEDULER_cancel (ts->destroy_task);
+ ts->destroy_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (NULL != ts->heap_node)
+ {
+ GNUNET_CONTAINER_heap_remove_node (ts->heap_node);
+ ts->heap_node = NULL;
+ get_tunnel_key_from_ips (ts->af,
+ ts->protocol,
+ &ts->source_ip,
+ ts->source_port,
+ &ts->destination_ip,
+ ts->destination_port,
+ &key);
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (tunnel_map,
+ &key,
+ ts));
+ }
+ if (NULL != ts->destination_container)
+ {
+ GNUNET_assert (ts == ts->destination_container->ts);
+ ts->destination_container->ts = NULL;
+ ts->destination_container = NULL;
+ }
+ GNUNET_free (ts);
+}
+
+
+/**
+ * Destroy the mesh tunnel.
+ *
+ * @param cls the 'struct TunnelState' with the tunnel to destroy
+ * @param tc scheduler context
+ */
+static void
+destroy_tunnel_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct TunnelState *ts = cls;
+ struct GNUNET_MESH_Tunnel *tunnel;
+
+ ts->destroy_task = GNUNET_SCHEDULER_NO_TASK;
+ GNUNET_assert (NULL != ts->tunnel);
+ tunnel = ts->tunnel;
+ ts->tunnel = NULL;
+ GNUNET_MESH_tunnel_destroy (tunnel);
+ free_tunnel_state (ts);
+}
+
+
+/**
+ * Method called whenever a peer has disconnected from the tunnel.
+ *
+ * @param cls closure
+ * @param peer peer identity the tunnel stopped working with
+ */
+static void
+tunnel_peer_disconnect_handler (void *cls,
+ const struct
+ GNUNET_PeerIdentity * peer)
+{
+ struct TunnelState *ts = cls;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Peer %s disconnected from tunnel.\n",
+ GNUNET_i2s (peer));
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Peers connected to mesh tunnels"),
+ -1, GNUNET_NO);
+ if (NULL != ts->th)
+ {
+ GNUNET_MESH_notify_transmit_ready_cancel (ts->th);
+ ts->th = NULL;
+ }
+ if (ts->destination.is_service)
+ return; /* hope for reconnect eventually */
+ /* as we are most likely going to change the exit node now,
+ we should just destroy the tunnel entirely... */
+ if (GNUNET_SCHEDULER_NO_TASK == ts->destroy_task)
+ ts->destroy_task = GNUNET_SCHEDULER_add_now (&destroy_tunnel_task, ts);
+}
+
+
+/**
+ * Method called whenever a peer has connected to the tunnel. Notifies
+ * the waiting client that the tunnel is now up.
+ *
+ * @param cls closure
+ * @param peer peer identity the tunnel was created to, NULL on timeout
+ * @param atsi performance data for the connection
+ */
+static void
+tunnel_peer_connect_handler (void *cls,
+ const struct GNUNET_PeerIdentity
+ * peer,
+ const struct
+ GNUNET_ATS_Information * atsi)
+{
+ struct TunnelState *ts = cls;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Peer %s connected to tunnel.\n",
+ GNUNET_i2s (peer));
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Peers connected to mesh tunnels"),
+ 1, GNUNET_NO);
+ if (NULL == ts->client)
+ return; /* nothing to do */
+ send_client_reply (ts->client,
+ ts->request_id,
+ ts->af,
+ &ts->destination_ip);
+ GNUNET_SERVER_client_drop (ts->client);
+ ts->client = NULL;
+}
+
+
+/**
+ * Send a message from the message queue via mesh.
+ *
+ * @param cls the 'struct TunnelState' with the message queue
+ * @param size number of bytes available in buf
+ * @param buf where to copy the message
+ * @return number of bytes copied to buf
+ */
+static size_t
+send_to_peer_notify_callback (void *cls, size_t size, void *buf)
+{
+ struct TunnelState *ts = cls;
+ struct TunnelMessageQueueEntry *tnq;
+ size_t ret;
+
+ ts->th = NULL;
+ if (NULL == buf)
+ return 0;
+ tnq = ts->tmq_head;
+ GNUNET_assert (NULL != tnq);
+ GNUNET_assert (size >= tnq->len);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending %u bytes via mesh tunnel\n",
+ tnq->len);
+ GNUNET_CONTAINER_DLL_remove (ts->tmq_head,
+ ts->tmq_tail,
+ tnq);
+ ts->tmq_length--;
+ memcpy (buf, tnq->msg, tnq->len);
+ ret = tnq->len;
+ GNUNET_free (tnq);
+ if (NULL != (tnq = ts->tmq_head))
+ ts->th = GNUNET_MESH_notify_transmit_ready (ts->tunnel,
+ GNUNET_NO /* cork */,
+ 42 /* priority */,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ NULL,
+ tnq->len,
+ &send_to_peer_notify_callback,
+ ts);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Bytes given to mesh for transmission"),
+ ret, GNUNET_NO);
+ return ret;
+}
+
+
+/**
+ * Add the given message to the given tunnel and trigger the
+ * transmission process.
+ *
+ * @param tnq message to queue
+ * @param ts tunnel to queue the message for
+ */
+static void
+send_to_tunnel (struct TunnelMessageQueueEntry *tnq,
+ struct TunnelState *ts)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Queueing %u bytes for transmission via mesh tunnel\n",
+ tnq->len);
+ GNUNET_assert (NULL != ts->tunnel);
+ GNUNET_CONTAINER_DLL_insert_tail (ts->tmq_head,
+ ts->tmq_tail,
+ tnq);
+ ts->tmq_length++;
+ if (ts->tmq_length > MAX_MESSAGE_QUEUE_SIZE)
+ {
+ struct TunnelMessageQueueEntry *dq;
+
+ dq = ts->tmq_head;
+ GNUNET_assert (dq != tnq);
+ GNUNET_CONTAINER_DLL_remove (ts->tmq_head,
+ ts->tmq_tail,
+ dq);
+ ts->tmq_length--;
+ GNUNET_MESH_notify_transmit_ready_cancel (ts->th);
+ ts->th = NULL;
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Bytes dropped in mesh queue (overflow)"),
+ dq->len,
+ GNUNET_NO);
+ GNUNET_free (dq);
+ }
+ if (NULL == ts->th)
+ ts->th = GNUNET_MESH_notify_transmit_ready (ts->tunnel,
+ GNUNET_NO /* cork */,
+ 42 /* priority */,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ NULL,
+ tnq->len,
+ &send_to_peer_notify_callback,
+ ts);
+}
+
+
+/**
+ * Initialize the given destination entry's mesh tunnel.
+ *
+ * @param de destination entry for which we need to setup a tunnel
+ * @param client client to notify on successful tunnel setup, or NULL for none
+ * @param client_af address family of the address returned to the client
+ * @param request_id request ID to send in client notification (unused if client is NULL)
+ * @return tunnel state of the tunnel that was created
+ */
+static struct TunnelState *
+create_tunnel_to_destination (struct DestinationEntry *de,
+ struct GNUNET_SERVER_Client *client,
+ int client_af,
+ uint64_t request_id)
+{
+ struct TunnelState *ts;
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Mesh tunnels created"),
+ 1, GNUNET_NO);
+ GNUNET_assert (NULL == de->ts);
+ ts = GNUNET_malloc (sizeof (struct TunnelState));
+ ts->af = client_af;
+ if (NULL != client)
+ {
+ ts->request_id = request_id;
+ ts->client = client;
+ GNUNET_SERVER_client_keep (client);
+ }
+ ts->destination = *de;
+ ts->destination.heap_node = NULL; /* copy is NOT in destination heap */
+ de->ts = ts;
+ ts->destination_container = de; /* we are referenced from de */
+ ts->tunnel = GNUNET_MESH_tunnel_create (mesh_handle,
+ ts,
+ &tunnel_peer_connect_handler,
+ &tunnel_peer_disconnect_handler,
+ ts);
+ if (NULL == ts->tunnel)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Failed to setup mesh tunnel!\n"));
+ if (NULL != client)
+ GNUNET_SERVER_client_drop (client);
+ GNUNET_free (ts);
+ return NULL;
+ }
+ if (de->is_service)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Creating tunnel to peer %s offering service %s\n",
+ GNUNET_i2s (&de->details.service_destination.target),
+ GNUNET_h2s (&de->details.service_destination.service_descriptor));
+ GNUNET_MESH_peer_request_connect_add (ts->tunnel,
+ &de->details.service_destination.target);
+ }
+ else
+ {
+ switch (de->details.exit_destination.af)
+ {
+ case AF_INET:
+ GNUNET_MESH_peer_request_connect_by_type (ts->tunnel,
+ GNUNET_APPLICATION_TYPE_IPV4_GATEWAY);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Creating tunnel to exit peer for %s\n",
+ "IPv4");
+ break;
+ case AF_INET6:
+ GNUNET_MESH_peer_request_connect_by_type (ts->tunnel,
+ GNUNET_APPLICATION_TYPE_IPV6_GATEWAY);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Creating tunnel to exit peer for %s\n",
+ "IPv6");
+ break;
+ default:
+ GNUNET_assert (0);
+ break;
+ }
+ }
+ return ts;
+}
+
+
+/**
+ * We have too many active tunnels. Clean up the oldest tunnel.
+ *
+ * @param except tunnel that must NOT be cleaned up, even if it is the oldest
+ */
+static void
+expire_tunnel (struct TunnelState *except)
+{
+ struct TunnelState *ts;
+
+ ts = GNUNET_CONTAINER_heap_peek (tunnel_heap);
+ GNUNET_assert (NULL != ts);
+ if (except == ts)
+ return; /* can't do this */
+ free_tunnel_state (ts);
+}
+
+
+/**
+ * Route a packet via mesh to the given destination.
+ *
+ * @param destination description of the destination
+ * @param af address family on this end (AF_INET or AF_INET6)
+ * @param protocol IPPROTO_TCP or IPPROTO_UDP or IPPROTO_ICMP or IPPROTO_ICMPV6
+ * @param source_ip source IP used by the sender (struct in_addr or struct in6_addr)
+ * @param destination_ip destination IP used by the sender (struct in_addr or struct in6_addr)
+ * @param payload payload of the packet after the IP header
+ * @param payload_length number of bytes in payload
+ */
+static void
+route_packet (struct DestinationEntry *destination,
+ int af,
+ uint8_t protocol,
+ const void *source_ip,
+ const void *destination_ip,
+ const void *payload,
+ size_t payload_length)
+{
+ GNUNET_HashCode key;
+ struct TunnelState *ts;
+ struct TunnelMessageQueueEntry *tnq;
+ size_t alen;
+ size_t mlen;
+ int is_new;
+ const struct GNUNET_TUN_UdpHeader *udp;
+ const struct GNUNET_TUN_TcpHeader *tcp;
+ const struct GNUNET_TUN_IcmpHeader *icmp;
+ uint16_t source_port;
+ uint16_t destination_port;
+
+ switch (protocol)
+ {
+ case IPPROTO_UDP:
+ {
+ if (payload_length < sizeof (struct GNUNET_TUN_UdpHeader))
+ {
+ /* blame kernel? */
+ GNUNET_break (0);
+ return;
+ }
+ udp = payload;
+ if (udp->len < sizeof (struct GNUNET_TUN_UdpHeader))
+ {
+ GNUNET_break_op (0);
+ return;
+ }
+ source_port = ntohs (udp->source_port);
+ destination_port = ntohs (udp->destination_port);
+ get_tunnel_key_from_ips (af,
+ IPPROTO_UDP,
+ source_ip,
+ source_port,
+ destination_ip,
+ destination_port,
+ &key);
+ }
+ break;
+ case IPPROTO_TCP:
+ {
+ if (payload_length < sizeof (struct GNUNET_TUN_TcpHeader))
+ {
+ /* blame kernel? */
+ GNUNET_break (0);
+ return;
+ }
+ tcp = payload;
+ if (tcp->off * 4 < sizeof (struct GNUNET_TUN_TcpHeader))
+ {
+ GNUNET_break_op (0);
+ return;
+ }
+ source_port = ntohs (tcp->source_port);
+ destination_port = ntohs (tcp->destination_port);
+ get_tunnel_key_from_ips (af,
+ IPPROTO_TCP,
+ source_ip,
+ source_port,
+ destination_ip,
+ destination_port,
+ &key);
+ }
+ break;
+ case IPPROTO_ICMP:
+ case IPPROTO_ICMPV6:
+ {
+ if ( (AF_INET == af) ^ (protocol == IPPROTO_ICMP) )
+ {
+ GNUNET_break (0);
+ return;
+ }
+ if (payload_length < sizeof (struct GNUNET_TUN_IcmpHeader))
+ {
+ /* blame kernel? */
+ GNUNET_break (0);
+ return;
+ }
+ icmp = payload;
+ source_port = 0;
+ destination_port = 0;
+ get_tunnel_key_from_ips (af,
+ protocol,
+ source_ip,
+ 0,
+ destination_ip,
+ 0,
+ &key);
+ }
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Protocol %u not supported, dropping\n"),
+ (unsigned int) protocol);
+ return;
+ }
+ if (! destination->is_service)
+ {
+ switch (destination->details.exit_destination.af)
+ {
+ case AF_INET:
+ alen = sizeof (struct in_addr);
+ break;
+ case AF_INET6:
+ alen = sizeof (struct in6_addr);
+ break;
+ default:
+ alen = 0;
+ GNUNET_assert (0);
+ }
+
+ {
+ char sbuf[INET6_ADDRSTRLEN];
+ char dbuf[INET6_ADDRSTRLEN];
+ char xbuf[INET6_ADDRSTRLEN];
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Routing %s packet from %s:%u -> %s:%u to destination %s:%u\n",
+ (protocol == IPPROTO_TCP) ? "TCP" : "UDP",
+ inet_ntop (af, source_ip, sbuf, sizeof (sbuf)),
+ source_port,
+ inet_ntop (af, destination_ip, dbuf, sizeof (dbuf)),
+ destination_port,
+ inet_ntop (destination->details.exit_destination.af,
+ &destination->details.exit_destination.ip,
+ xbuf, sizeof (xbuf)),
+ destination_port);
+ }
+ }
+ else
+ {
+ /* make compiler happy */
+ alen = 0;
+ {
+ char sbuf[INET6_ADDRSTRLEN];
+ char dbuf[INET6_ADDRSTRLEN];
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Routing %s packet from %s:%u -> %s:%u to service %s at peer %s\n",
+ (protocol == IPPROTO_TCP) ? "TCP" : "UDP",
+ inet_ntop (af, source_ip, sbuf, sizeof (sbuf)),
+ source_port,
+ inet_ntop (af, destination_ip, dbuf, sizeof (dbuf)),
+ destination_port,
+ GNUNET_h2s (&destination->details.service_destination.service_descriptor),
+ GNUNET_i2s (&destination->details.service_destination.target));
+ }
+
+ }
+
+ /* see if we have an existing tunnel for this destination */
+ ts = GNUNET_CONTAINER_multihashmap_get (tunnel_map,
+ &key);
+ if (NULL == ts)
+ {
+ /* need to either use the existing tunnel from the destination (if still
+ available) or create a fresh one */
+ is_new = GNUNET_YES;
+ if (NULL == destination->ts)
+ ts = create_tunnel_to_destination (destination, NULL, af, 0);
+ else
+ ts = destination->ts;
+ if (NULL == ts)
+ return;
+ destination->ts = NULL;
+ ts->destination_container = NULL; /* no longer 'contained' */
+ /* now bind existing "unbound" tunnel to our IP/port tuple */
+ ts->protocol = protocol;
+ ts->af = af;
+ if (af == AF_INET)
+ {
+ ts->source_ip.v4 = * (const struct in_addr *) source_ip;
+ ts->destination_ip.v4 = * (const struct in_addr *) destination_ip;
+ }
+ else
+ {
+ ts->source_ip.v6 = * (const struct in6_addr *) source_ip;
+ ts->destination_ip.v6 = * (const struct in6_addr *) destination_ip;
+ }
+ ts->source_port = source_port;
+ ts->destination_port = destination_port;
+ ts->heap_node = GNUNET_CONTAINER_heap_insert (tunnel_heap,
+ ts,
+ GNUNET_TIME_absolute_get ().abs_value);
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_put (tunnel_map,
+ &key,
+ ts,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Active tunnels"),
+ 1, GNUNET_NO);
+ while (GNUNET_CONTAINER_multihashmap_size (tunnel_map) > max_tunnel_mappings)
+ expire_tunnel (ts);
+ }
+ else
+ {
+ is_new = GNUNET_NO;
+ GNUNET_CONTAINER_heap_update_cost (tunnel_heap,
+ ts->heap_node,
+ GNUNET_TIME_absolute_get ().abs_value);
+ }
+ GNUNET_assert (NULL != ts->tunnel);
+
+ /* send via tunnel */
+ switch (protocol)
+ {
+ case IPPROTO_UDP:
+ if (destination->is_service)
+ {
+ struct GNUNET_EXIT_UdpServiceMessage *usm;
+
+ mlen = sizeof (struct GNUNET_EXIT_UdpServiceMessage) +
+ payload_length - sizeof (struct GNUNET_TUN_UdpHeader);
+ if (mlen >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ tnq = GNUNET_malloc (sizeof (struct TunnelMessageQueueEntry) + mlen);
+ tnq->len = mlen;
+ tnq->msg = &tnq[1];
+ usm = (struct GNUNET_EXIT_UdpServiceMessage *) &tnq[1];
+ usm->header.size = htons ((uint16_t) mlen);
+ usm->header.type = htons (GNUNET_MESSAGE_TYPE_VPN_UDP_TO_SERVICE);
+ /* if the source port is below 32000, we assume it has a special
+ meaning; if not, we pick a random port (this is a heuristic) */
+ usm->source_port = (ntohs (udp->source_port) < 32000) ? udp->source_port : 0;
+ usm->destination_port = udp->destination_port;
+ usm->service_descriptor = destination->details.service_destination.service_descriptor;
+ memcpy (&usm[1],
+ &udp[1],
+ payload_length - sizeof (struct GNUNET_TUN_UdpHeader));
+ }
+ else
+ {
+ struct GNUNET_EXIT_UdpInternetMessage *uim;
+ struct in_addr *ip4dst;
+ struct in6_addr *ip6dst;
+ void *payload;
+
+ mlen = sizeof (struct GNUNET_EXIT_UdpInternetMessage) +
+ alen + payload_length - sizeof (struct GNUNET_TUN_UdpHeader);
+ if (mlen >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ tnq = GNUNET_malloc (sizeof (struct TunnelMessageQueueEntry) +
+ mlen);
+ tnq->len = mlen;
+ tnq->msg = &tnq[1];
+ uim = (struct GNUNET_EXIT_UdpInternetMessage *) &tnq[1];
+ uim->header.size = htons ((uint16_t) mlen);
+ uim->header.type = htons (GNUNET_MESSAGE_TYPE_VPN_UDP_TO_INTERNET);
+ uim->af = htonl (destination->details.exit_destination.af);
+ uim->source_port = (ntohs (udp->source_port) < 32000) ? udp->source_port : 0;
+ uim->destination_port = udp->destination_port;
+ switch (destination->details.exit_destination.af)
+ {
+ case AF_INET:
+ ip4dst = (struct in_addr *) &uim[1];
+ *ip4dst = destination->details.exit_destination.ip.v4;
+ payload = &ip4dst[1];
+ break;
+ case AF_INET6:
+ ip6dst = (struct in6_addr *) &uim[1];
+ *ip6dst = destination->details.exit_destination.ip.v6;
+ payload = &ip6dst[1];
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ memcpy (payload,
+ &udp[1],
+ payload_length - sizeof (struct GNUNET_TUN_UdpHeader));
+ }
+ break;
+ case IPPROTO_TCP:
+ if (is_new)
+ {
+ if (destination->is_service)
+ {
+ struct GNUNET_EXIT_TcpServiceStartMessage *tsm;
+
+ mlen = sizeof (struct GNUNET_EXIT_TcpServiceStartMessage) +
+ payload_length - sizeof (struct GNUNET_TUN_TcpHeader);
+ if (mlen >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ tnq = GNUNET_malloc (sizeof (struct TunnelMessageQueueEntry) + mlen);
+ tnq->len = mlen;
+ tnq->msg = &tnq[1];
+ tsm = (struct GNUNET_EXIT_TcpServiceStartMessage *) &tnq[1];
+ tsm->header.size = htons ((uint16_t) mlen);
+ tsm->header.type = htons (GNUNET_MESSAGE_TYPE_VPN_TCP_TO_SERVICE_START);
+ tsm->reserved = htonl (0);
+ tsm->service_descriptor = destination->details.service_destination.service_descriptor;
+ tsm->tcp_header = *tcp;
+ memcpy (&tsm[1],
+ &tcp[1],
+ payload_length - sizeof (struct GNUNET_TUN_TcpHeader));
+ }
+ else
+ {
+ struct GNUNET_EXIT_TcpInternetStartMessage *tim;
+ struct in_addr *ip4dst;
+ struct in6_addr *ip6dst;
+ void *payload;
+
+ mlen = sizeof (struct GNUNET_EXIT_TcpInternetStartMessage) +
+ alen + payload_length - sizeof (struct GNUNET_TUN_TcpHeader);
+ if (mlen >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ tnq = GNUNET_malloc (sizeof (struct TunnelMessageQueueEntry) + mlen);
+ tnq->len = mlen;
+ tnq->msg = &tnq[1];
+ tim = (struct GNUNET_EXIT_TcpInternetStartMessage *) &tnq[1];
+ tim->header.size = htons ((uint16_t) mlen);
+ tim->header.type = htons (GNUNET_MESSAGE_TYPE_VPN_TCP_TO_INTERNET_START);
+ tim->af = htonl (destination->details.exit_destination.af);
+ tim->tcp_header = *tcp;
+ switch (destination->details.exit_destination.af)
+ {
+ case AF_INET:
+ ip4dst = (struct in_addr *) &tim[1];
+ *ip4dst = destination->details.exit_destination.ip.v4;
+ payload = &ip4dst[1];
+ break;
+ case AF_INET6:
+ ip6dst = (struct in6_addr *) &tim[1];
+ *ip6dst = destination->details.exit_destination.ip.v6;
+ payload = &ip6dst[1];
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ memcpy (payload,
+ &tcp[1],
+ payload_length - sizeof (struct GNUNET_TUN_TcpHeader));
+ }
+ }
+ else
+ {
+ struct GNUNET_EXIT_TcpDataMessage *tdm;
+
+ mlen = sizeof (struct GNUNET_EXIT_TcpDataMessage) +
+ payload_length - sizeof (struct GNUNET_TUN_TcpHeader);
+ if (mlen >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ tnq = GNUNET_malloc (sizeof (struct TunnelMessageQueueEntry) + mlen);
+ tnq->len = mlen;
+ tnq->msg = &tnq[1];
+ tdm = (struct GNUNET_EXIT_TcpDataMessage *) &tnq[1];
+ tdm->header.size = htons ((uint16_t) mlen);
+ tdm->header.type = htons (GNUNET_MESSAGE_TYPE_VPN_TCP_DATA_TO_EXIT);
+ tdm->reserved = htonl (0);
+ tdm->tcp_header = *tcp;
+ memcpy (&tdm[1],
+ &tcp[1],
+ payload_length - sizeof (struct GNUNET_TUN_TcpHeader));
+ }
+ break;
+ case IPPROTO_ICMP:
+ case IPPROTO_ICMPV6:
+ if (destination->is_service)
+ {
+ struct GNUNET_EXIT_IcmpServiceMessage *ism;
+
+ mlen = sizeof (struct GNUNET_EXIT_IcmpServiceMessage) +
+ payload_length - sizeof (struct GNUNET_TUN_IcmpHeader);
+ if (mlen >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ tnq = GNUNET_malloc (sizeof (struct TunnelMessageQueueEntry) + mlen);
+ tnq->msg = &tnq[1];
+ ism = (struct GNUNET_EXIT_IcmpServiceMessage *) &tnq[1];
+ ism->header.type = htons (GNUNET_MESSAGE_TYPE_VPN_ICMP_TO_SERVICE);
+ ism->af = htonl (af); /* need to tell destination ICMP protocol family! */
+ ism->service_descriptor = destination->details.service_destination.service_descriptor;
+ ism->icmp_header = *icmp;
+ /* ICMP protocol translation will be done by the receiver (as we don't know
+ the target AF); however, we still need to possibly discard the payload
+ depending on the ICMP type */
+ switch (af)
+ {
+ case AF_INET:
+ switch (icmp->type)
+ {
+ case GNUNET_TUN_ICMPTYPE_ECHO_REPLY:
+ case GNUNET_TUN_ICMPTYPE_ECHO_REQUEST:
+ break;
+ case GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE:
+ case GNUNET_TUN_ICMPTYPE_SOURCE_QUENCH:
+ case GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED:
+ /* throw away ICMP payload, won't be useful for the other side anyway */
+ payload_length = sizeof (struct GNUNET_TUN_IcmpHeader);
+ break;
+ default:
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv4 packets dropped (not allowed)"),
+ 1, GNUNET_NO);
+ return;
+ }
+ /* end of AF_INET */
+ break;
+ case AF_INET6:
+ switch (icmp->type)
+ {
+ case GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE:
+ case GNUNET_TUN_ICMPTYPE6_PACKET_TOO_BIG:
+ case GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED:
+ case GNUNET_TUN_ICMPTYPE6_PARAMETER_PROBLEM:
+ /* throw away ICMP payload, won't be useful for the other side anyway */
+ payload_length = sizeof (struct GNUNET_TUN_IcmpHeader);
+ break;
+ case GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST:
+ case GNUNET_TUN_ICMPTYPE6_ECHO_REPLY:
+ break;
+ default:
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv6 packets dropped (not allowed)"),
+ 1, GNUNET_NO);
+ return;
+ }
+ /* end of AF_INET6 */
+ break;
+ default:
+ GNUNET_assert (0);
+ break;
+ }
+
+ /* update length calculations, as payload_length may have changed */
+ mlen = sizeof (struct GNUNET_EXIT_IcmpServiceMessage) +
+ alen + payload_length - sizeof (struct GNUNET_TUN_IcmpHeader);
+ tnq->len = mlen;
+ ism->header.size = htons ((uint16_t) mlen);
+ /* finally, copy payload (if there is any left...) */
+ memcpy (&ism[1],
+ &icmp[1],
+ payload_length - sizeof (struct GNUNET_TUN_IcmpHeader));
+ }
+ else
+ {
+ struct GNUNET_EXIT_IcmpInternetMessage *iim;
+ struct in_addr *ip4dst;
+ struct in6_addr *ip6dst;
+ void *payload;
+
+ mlen = sizeof (struct GNUNET_EXIT_IcmpInternetMessage) +
+ alen + payload_length - sizeof (struct GNUNET_TUN_IcmpHeader);
+ if (mlen >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ tnq = GNUNET_malloc (sizeof (struct TunnelMessageQueueEntry) +
+ mlen);
+ tnq->msg = &tnq[1];
+ iim = (struct GNUNET_EXIT_IcmpInternetMessage *) &tnq[1];
+ iim->header.type = htons (GNUNET_MESSAGE_TYPE_VPN_ICMP_TO_INTERNET);
+ iim->icmp_header = *icmp;
+ /* Perform ICMP protocol-translation (depending on destination AF and source AF)
+ and throw away ICMP payload depending on ICMP message type */
+ switch (af)
+ {
+ case AF_INET:
+ switch (icmp->type)
+ {
+ case GNUNET_TUN_ICMPTYPE_ECHO_REPLY:
+ if (destination->details.exit_destination.af == AF_INET6)
+ iim->icmp_header.type = GNUNET_TUN_ICMPTYPE6_ECHO_REPLY;
+ break;
+ case GNUNET_TUN_ICMPTYPE_ECHO_REQUEST:
+ if (destination->details.exit_destination.af == AF_INET6)
+ iim->icmp_header.type = GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST;
+ break;
+ case GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE:
+ if (destination->details.exit_destination.af == AF_INET6)
+ iim->icmp_header.type = GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE;
+ /* throw away IP-payload, exit will have to make it up anyway */
+ payload_length = sizeof (struct GNUNET_TUN_IcmpHeader);
+ break;
+ case GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED:
+ if (destination->details.exit_destination.af == AF_INET6)
+ iim->icmp_header.type = GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED;
+ /* throw away IP-payload, exit will have to make it up anyway */
+ payload_length = sizeof (struct GNUNET_TUN_IcmpHeader);
+ break;
+ case GNUNET_TUN_ICMPTYPE_SOURCE_QUENCH:
+ if (destination->details.exit_destination.af == AF_INET6)
+ {
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv4 packets dropped (impossible PT to v6)"),
+ 1, GNUNET_NO);
+ GNUNET_free (tnq);
+ return;
+ }
+ /* throw away IP-payload, exit will have to make it up anyway */
+ payload_length = sizeof (struct GNUNET_TUN_IcmpHeader);
+ break;
+ default:
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv4 packets dropped (type not allowed)"),
+ 1, GNUNET_NO);
+ GNUNET_free (tnq);
+ return;
+ }
+ /* end of AF_INET */
+ break;
+ case AF_INET6:
+ switch (icmp->type)
+ {
+ case GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE:
+ if (destination->details.exit_destination.af == AF_INET6)
+ iim->icmp_header.type = GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE;
+ /* throw away IP-payload, exit will have to make it up anyway */
+ payload_length = sizeof (struct GNUNET_TUN_IcmpHeader);
+ break;
+ case GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED:
+ if (destination->details.exit_destination.af == AF_INET)
+ iim->icmp_header.type = GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED;
+ /* throw away IP-payload, exit will have to make it up anyway */
+ payload_length = sizeof (struct GNUNET_TUN_IcmpHeader);
+ break;
+ case GNUNET_TUN_ICMPTYPE6_PACKET_TOO_BIG:
+ if (destination->details.exit_destination.af == AF_INET)
+ {
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv6 packets dropped (impossible PT to v4)"),
+ 1, GNUNET_NO);
+ GNUNET_free (tnq);
+ return;
+ }
+ /* throw away IP-payload, exit will have to make it up anyway */
+ payload_length = sizeof (struct GNUNET_TUN_IcmpHeader);
+ break;
+ case GNUNET_TUN_ICMPTYPE6_PARAMETER_PROBLEM:
+ if (destination->details.exit_destination.af == AF_INET)
+ {
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv6 packets dropped (impossible PT to v4)"),
+ 1, GNUNET_NO);
+ GNUNET_free (tnq);
+ return;
+ }
+ /* throw away IP-payload, exit will have to make it up anyway */
+ payload_length = sizeof (struct GNUNET_TUN_IcmpHeader);
+ break;
+ case GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST:
+ if (destination->details.exit_destination.af == AF_INET)
+ iim->icmp_header.type = GNUNET_TUN_ICMPTYPE_ECHO_REQUEST;
+ break;
+ case GNUNET_TUN_ICMPTYPE6_ECHO_REPLY:
+ if (destination->details.exit_destination.af == AF_INET)
+ iim->icmp_header.type = GNUNET_TUN_ICMPTYPE_ECHO_REPLY;
+ break;
+ default:
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv6 packets dropped (type not allowed)"),
+ 1, GNUNET_NO);
+ GNUNET_free (tnq);
+ return;
+ }
+ /* end of AF_INET6 */
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ /* update length calculations, as payload_length may have changed */
+ mlen = sizeof (struct GNUNET_EXIT_IcmpInternetMessage) +
+ alen + payload_length - sizeof (struct GNUNET_TUN_IcmpHeader);
+ tnq->len = mlen;
+ iim->header.size = htons ((uint16_t) mlen);
+
+ /* need to tell destination ICMP protocol family! */
+ iim->af = htonl (destination->details.exit_destination.af);
+ switch (destination->details.exit_destination.af)
+ {
+ case AF_INET:
+ ip4dst = (struct in_addr *) &iim[1];
+ *ip4dst = destination->details.exit_destination.ip.v4;
+ payload = &ip4dst[1];
+ break;
+ case AF_INET6:
+ ip6dst = (struct in6_addr *) &iim[1];
+ *ip6dst = destination->details.exit_destination.ip.v6;
+ payload = &ip6dst[1];
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ memcpy (payload,
+ &icmp[1],
+ payload_length - sizeof (struct GNUNET_TUN_IcmpHeader));
+ }
+ break;
+ default:
+ /* not supported above, how can we get here !? */
+ GNUNET_assert (0);
+ break;
+ }
+ send_to_tunnel (tnq, ts);
+}
+
+
+/**
+ * Receive packets from the helper-process (someone send to the local
+ * virtual tunnel interface). Find the destination mapping, and if it
+ * exists, identify the correct MESH tunnel (or possibly create it)
+ * and forward the packet.
+ *
+ * @param cls closure, NULL
+ * @param client NULL
+ * @param message message we got from the client (VPN tunnel interface)
+ */
+static void
+message_token (void *cls GNUNET_UNUSED, void *client GNUNET_UNUSED,
+ const struct GNUNET_MessageHeader *message)
+{
+ const struct GNUNET_TUN_Layer2PacketHeader *tun;
+ size_t mlen;
+ GNUNET_HashCode key;
+ struct DestinationEntry *de;
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Packets received from TUN interface"),
+ 1, GNUNET_NO);
+ mlen = ntohs (message->size);
+ if ( (ntohs (message->type) != GNUNET_MESSAGE_TYPE_VPN_HELPER) ||
+ (mlen < sizeof (struct GNUNET_MessageHeader) + sizeof (struct GNUNET_TUN_Layer2PacketHeader)) )
+ {
+ GNUNET_break (0);
+ return;
+ }
+ tun = (const struct GNUNET_TUN_Layer2PacketHeader *) &message[1];
+ mlen -= (sizeof (struct GNUNET_MessageHeader) + sizeof (struct GNUNET_TUN_Layer2PacketHeader));
+ switch (ntohs (tun->proto))
+ {
+ case ETH_P_IPV6:
+ {
+ const struct GNUNET_TUN_IPv6Header *pkt6;
+
+ if (mlen < sizeof (struct GNUNET_TUN_IPv6Header))
+ {
+ /* blame kernel */
+ GNUNET_break (0);
+ return;
+ }
+ pkt6 = (const struct GNUNET_TUN_IPv6Header *) &tun[1];
+ get_destination_key_from_ip (AF_INET6,
+ &pkt6->destination_address,
+ &key);
+ de = GNUNET_CONTAINER_multihashmap_get (destination_map, &key);
+ /* FIXME: do we need to guard against hash collision?
+ (if so, we need to also store the local destination IP in the
+ destination entry and then compare here; however, the risk
+ of collision seems minimal AND the impact is unlikely to be
+ super-problematic as well... */
+ if (NULL == de)
+ {
+ char buf[INET6_ADDRSTRLEN];
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Packet received for unmapped destination `%s' (dropping it)\n"),
+ inet_ntop (AF_INET6,
+ &pkt6->destination_address,
+ buf,
+ sizeof (buf)));
+ return;
+ }
+ route_packet (de,
+ AF_INET6,
+ pkt6->next_header,
+ &pkt6->source_address,
+ &pkt6->destination_address,
+ &pkt6[1],
+ mlen - sizeof (struct GNUNET_TUN_IPv6Header));
+ }
+ break;
+ case ETH_P_IPV4:
+ {
+ struct GNUNET_TUN_IPv4Header *pkt4;
+
+ if (mlen < sizeof (struct GNUNET_TUN_IPv4Header))
+ {
+ /* blame kernel */
+ GNUNET_break (0);
+ return;
+ }
+ pkt4 = (struct GNUNET_TUN_IPv4Header *) &tun[1];
+ get_destination_key_from_ip (AF_INET,
+ &pkt4->destination_address,
+ &key);
+ de = GNUNET_CONTAINER_multihashmap_get (destination_map, &key);
+ /* FIXME: do we need to guard against hash collision?
+ (if so, we need to also store the local destination IP in the
+ destination entry and then compare here; however, the risk
+ of collision seems minimal AND the impact is unlikely to be
+ super-problematic as well... */
+ if (NULL == de)
+ {
+ char buf[INET_ADDRSTRLEN];
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Packet received for unmapped destination `%s' (dropping it)\n"),
+ inet_ntop (AF_INET,
+ &pkt4->destination_address,
+ buf,
+ sizeof (buf)));
+ return;
+ }
+ if (pkt4->header_length * 4 != sizeof (struct GNUNET_TUN_IPv4Header))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Received IPv4 packet with options (dropping it)\n"));
+ return;
+ }
+ route_packet (de,
+ AF_INET,
+ pkt4->protocol,
+ &pkt4->source_address,
+ &pkt4->destination_address,
+ &pkt4[1],
+ mlen - sizeof (struct GNUNET_TUN_IPv4Header));
+ }
+ break;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Received packet of unknown protocol %d from TUN (dropping it)\n"),
+ (unsigned int) ntohs (tun->proto));
+ break;
+ }
+}
+
+
+/**
+ * Synthesize a plausible ICMP payload for an ICMP error
+ * response on the given tunnel.
+ *
+ * @param ts tunnel information
+ * @param ipp IPv4 header to fill in (ICMP payload)
+ * @param udp "UDP" header to fill in (ICMP payload); might actually
+ * also be the first 8 bytes of the TCP header
+ */
+static void
+make_up_icmpv4_payload (struct TunnelState *ts,
+ struct GNUNET_TUN_IPv4Header *ipp,
+ struct GNUNET_TUN_UdpHeader *udp)
+{
+ GNUNET_TUN_initialize_ipv4_header (ipp,
+ ts->protocol,
+ sizeof (struct GNUNET_TUN_TcpHeader),
+ &ts->source_ip.v4,
+ &ts->destination_ip.v4);
+ udp->source_port = htons (ts->source_port);
+ udp->destination_port = htons (ts->destination_port);
+ udp->len = htons (0);
+ udp->crc = htons (0);
+}
+
+
+/**
+ * Synthesize a plausible ICMP payload for an ICMP error
+ * response on the given tunnel.
+ *
+ * @param ts tunnel information
+ * @param ipp IPv6 header to fill in (ICMP payload)
+ * @param udp "UDP" header to fill in (ICMP payload); might actually
+ * also be the first 8 bytes of the TCP header
+ */
+static void
+make_up_icmpv6_payload (struct TunnelState *ts,
+ struct GNUNET_TUN_IPv6Header *ipp,
+ struct GNUNET_TUN_UdpHeader *udp)
+{
+ GNUNET_TUN_initialize_ipv6_header (ipp,
+ ts->protocol,
+ sizeof (struct GNUNET_TUN_TcpHeader),
+ &ts->source_ip.v6,
+ &ts->destination_ip.v6);
+ udp->source_port = htons (ts->source_port);
+ udp->destination_port = htons (ts->destination_port);
+ udp->len = htons (0);
+ udp->crc = htons (0);
+}
+
+
+/**
+ * We got an ICMP packet back from the MESH tunnel. Pass it on to the
+ * local virtual interface via the helper.
+ *
+ * @param cls closure, NULL
+ * @param tunnel connection to the other end
+ * @param tunnel_ctx pointer to our 'struct TunnelState *'
+ * @param sender who sent the message
+ * @param message the actual message
+ * @param atsi performance data for the connection
+ * @return GNUNET_OK to keep the connection open,
+ * GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+receive_icmp_back (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+ void **tunnel_ctx, const struct GNUNET_PeerIdentity *sender,
+ const struct GNUNET_MessageHeader *message,
+ const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
+{
+ struct TunnelState *ts = *tunnel_ctx;
+ const struct GNUNET_EXIT_IcmpToVPNMessage *i2v;
+ size_t mlen;
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMP packets received from mesh"),
+ 1, GNUNET_NO);
+ mlen = ntohs (message->size);
+ if (mlen < sizeof (struct GNUNET_EXIT_IcmpToVPNMessage))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (NULL == ts->heap_node)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (AF_UNSPEC == ts->af)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ i2v = (const struct GNUNET_EXIT_IcmpToVPNMessage *) message;
+ mlen -= sizeof (struct GNUNET_EXIT_IcmpToVPNMessage);
+ {
+ char sbuf[INET6_ADDRSTRLEN];
+ char dbuf[INET6_ADDRSTRLEN];
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received ICMP packet from mesh, sending %u bytes from %s -> %s via TUN\n",
+ (unsigned int) mlen,
+ inet_ntop (ts->af, &ts->destination_ip, sbuf, sizeof (sbuf)),
+ inet_ntop (ts->af, &ts->source_ip, dbuf, sizeof (dbuf)));
+ }
+ switch (ts->af)
+ {
+ case AF_INET:
+ {
+ size_t size = sizeof (struct GNUNET_TUN_IPv4Header)
+ + sizeof (struct GNUNET_TUN_IcmpHeader)
+ + sizeof (struct GNUNET_MessageHeader) +
+ sizeof (struct GNUNET_TUN_Layer2PacketHeader) +
+ mlen;
+ {
+ /* reserve some extra space in case we have an ICMP type here where
+ we will need to make up the payload ourselves */
+ char buf[size + sizeof (struct GNUNET_TUN_IPv4Header) + 8];
+ struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf;
+ struct GNUNET_TUN_Layer2PacketHeader *tun = (struct GNUNET_TUN_Layer2PacketHeader*) &msg[1];
+ struct GNUNET_TUN_IPv4Header *ipv4 = (struct GNUNET_TUN_IPv4Header *) &tun[1];
+ struct GNUNET_TUN_IcmpHeader *icmp = (struct GNUNET_TUN_IcmpHeader *) &ipv4[1];
+ msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ tun->flags = htons (0);
+ tun->proto = htons (ETH_P_IPV4);
+ GNUNET_TUN_initialize_ipv4_header (ipv4,
+ IPPROTO_ICMP,
+ sizeof (struct GNUNET_TUN_IcmpHeader) + mlen,
+ &ts->destination_ip.v4,
+ &ts->source_ip.v4);
+ *icmp = i2v->icmp_header;
+ memcpy (&icmp[1],
+ &i2v[1],
+ mlen);
+ /* For some ICMP types, we need to adjust (make up) the payload here.
+ Also, depending on the AF used on the other side, we have to
+ do ICMP PT (translate ICMP types) */
+ switch (ntohl (i2v->af))
+ {
+ case AF_INET:
+ switch (icmp->type)
+ {
+ case GNUNET_TUN_ICMPTYPE_ECHO_REPLY:
+ case GNUNET_TUN_ICMPTYPE_ECHO_REQUEST:
+ break;
+ case GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE:
+ case GNUNET_TUN_ICMPTYPE_SOURCE_QUENCH:
+ case GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED:
+ {
+ struct GNUNET_TUN_IPv4Header *ipp = (struct GNUNET_TUN_IPv4Header *) &icmp[1];
+ struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipp[1];
+
+ if (mlen != 0)
+ {
+ /* sender did not strip ICMP payload? */
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ size += sizeof (struct GNUNET_TUN_IPv4Header) + 8;
+ GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader));
+ make_up_icmpv4_payload (ts, ipp, udp);
+ }
+ break;
+ default:
+ GNUNET_break_op (0);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv4 packets dropped (type not allowed)"),
+ 1, GNUNET_NO);
+ return GNUNET_SYSERR;
+ }
+ /* end AF_INET */
+ break;
+ case AF_INET6:
+ /* ICMP PT 6-to-4 and possibly making up payloads */
+ switch (icmp->type)
+ {
+ case GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE:
+ icmp->type = GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE;
+ {
+ struct GNUNET_TUN_IPv4Header *ipp = (struct GNUNET_TUN_IPv4Header *) &icmp[1];
+ struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipp[1];
+
+ if (mlen != 0)
+ {
+ /* sender did not strip ICMP payload? */
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ size += sizeof (struct GNUNET_TUN_IPv4Header) + 8;
+ GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader));
+ make_up_icmpv4_payload (ts, ipp, udp);
+ }
+ break;
+ case GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED:
+ icmp->type = GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED;
+ {
+ struct GNUNET_TUN_IPv4Header *ipp = (struct GNUNET_TUN_IPv4Header *) &icmp[1];
+ struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipp[1];
+
+ if (mlen != 0)
+ {
+ /* sender did not strip ICMP payload? */
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ size += sizeof (struct GNUNET_TUN_IPv4Header) + 8;
+ GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader));
+ make_up_icmpv4_payload (ts, ipp, udp);
+ }
+ break;
+ case GNUNET_TUN_ICMPTYPE6_PACKET_TOO_BIG:
+ case GNUNET_TUN_ICMPTYPE6_PARAMETER_PROBLEM:
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv6 packets dropped (impossible PT to v4)"),
+ 1, GNUNET_NO);
+ return GNUNET_OK;
+ case GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST:
+ icmp->type = GNUNET_TUN_ICMPTYPE_ECHO_REQUEST;
+ break;
+ case GNUNET_TUN_ICMPTYPE6_ECHO_REPLY:
+ icmp->type = GNUNET_TUN_ICMPTYPE_ECHO_REPLY;
+ break;
+ default:
+ GNUNET_break_op (0);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv6 packets dropped (type not allowed)"),
+ 1, GNUNET_NO);
+ return GNUNET_SYSERR;
+ }
+ /* end AF_INET6 */
+ break;
+ default:
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ msg->size = htons (size);
+ GNUNET_TUN_calculate_icmp_checksum (icmp,
+ &i2v[1],
+ mlen);
+ (void) GNUNET_HELPER_send (helper_handle,
+ msg,
+ GNUNET_YES,
+ NULL, NULL);
+ }
+ }
+ break;
+ case AF_INET6:
+ {
+ size_t size = sizeof (struct GNUNET_TUN_IPv6Header)
+ + sizeof (struct GNUNET_TUN_IcmpHeader)
+ + sizeof (struct GNUNET_MessageHeader) +
+ sizeof (struct GNUNET_TUN_Layer2PacketHeader) +
+ mlen;
+ {
+ char buf[size + sizeof (struct GNUNET_TUN_IPv6Header) + 8];
+ struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf;
+ struct GNUNET_TUN_Layer2PacketHeader *tun = (struct GNUNET_TUN_Layer2PacketHeader*) &msg[1];
+ struct GNUNET_TUN_IPv6Header *ipv6 = (struct GNUNET_TUN_IPv6Header *) &tun[1];
+ struct GNUNET_TUN_IcmpHeader *icmp = (struct GNUNET_TUN_IcmpHeader *) &ipv6[1];
+ msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ tun->flags = htons (0);
+ tun->proto = htons (ETH_P_IPV6);
+ GNUNET_TUN_initialize_ipv6_header (ipv6,
+ IPPROTO_ICMPV6,
+ sizeof (struct GNUNET_TUN_IcmpHeader) + mlen,
+ &ts->destination_ip.v6,
+ &ts->source_ip.v6);
+ *icmp = i2v->icmp_header;
+ memcpy (&icmp[1],
+ &i2v[1],
+ mlen);
+
+ /* For some ICMP types, we need to adjust (make up) the payload here.
+ Also, depending on the AF used on the other side, we have to
+ do ICMP PT (translate ICMP types) */
+ switch (ntohl (i2v->af))
+ {
+ case AF_INET:
+ /* ICMP PT 4-to-6 and possibly making up payloads */
+ switch (icmp->type)
+ {
+ case GNUNET_TUN_ICMPTYPE_ECHO_REPLY:
+ icmp->type = GNUNET_TUN_ICMPTYPE6_ECHO_REPLY;
+ break;
+ case GNUNET_TUN_ICMPTYPE_ECHO_REQUEST:
+ icmp->type = GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST;
+ break;
+ case GNUNET_TUN_ICMPTYPE_DESTINATION_UNREACHABLE:
+ icmp->type = GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE;
+ {
+ struct GNUNET_TUN_IPv6Header *ipp = (struct GNUNET_TUN_IPv6Header *) &icmp[1];
+ struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipp[1];
+
+ if (mlen != 0)
+ {
+ /* sender did not strip ICMP payload? */
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ size += sizeof (struct GNUNET_TUN_IPv6Header) + 8;
+ GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader));
+ make_up_icmpv6_payload (ts, ipp, udp);
+ }
+ break;
+ case GNUNET_TUN_ICMPTYPE_TIME_EXCEEDED:
+ icmp->type = GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED;
+ {
+ struct GNUNET_TUN_IPv6Header *ipp = (struct GNUNET_TUN_IPv6Header *) &icmp[1];
+ struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipp[1];
+
+ if (mlen != 0)
+ {
+ /* sender did not strip ICMP payload? */
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ size += sizeof (struct GNUNET_TUN_IPv6Header) + 8;
+ GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader));
+ make_up_icmpv6_payload (ts, ipp, udp);
+ }
+ break;
+ case GNUNET_TUN_ICMPTYPE_SOURCE_QUENCH:
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv4 packets dropped (impossible PT to v6)"),
+ 1, GNUNET_NO);
+ return GNUNET_OK;
+ default:
+ GNUNET_break_op (0);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv4 packets dropped (type not allowed)"),
+ 1, GNUNET_NO);
+ return GNUNET_SYSERR;
+ }
+ /* end AF_INET */
+ break;
+ case AF_INET6:
+ switch (icmp->type)
+ {
+ case GNUNET_TUN_ICMPTYPE6_DESTINATION_UNREACHABLE:
+ case GNUNET_TUN_ICMPTYPE6_TIME_EXCEEDED:
+ case GNUNET_TUN_ICMPTYPE6_PACKET_TOO_BIG:
+ case GNUNET_TUN_ICMPTYPE6_PARAMETER_PROBLEM:
+ {
+ struct GNUNET_TUN_IPv6Header *ipp = (struct GNUNET_TUN_IPv6Header *) &icmp[1];
+ struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipp[1];
+
+ if (mlen != 0)
+ {
+ /* sender did not strip ICMP payload? */
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ size += sizeof (struct GNUNET_TUN_IPv6Header) + 8;
+ GNUNET_assert (8 == sizeof (struct GNUNET_TUN_UdpHeader));
+ make_up_icmpv6_payload (ts, ipp, udp);
+ }
+ break;
+ case GNUNET_TUN_ICMPTYPE6_ECHO_REQUEST:
+ break;
+ default:
+ GNUNET_break_op (0);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# ICMPv6 packets dropped (type not allowed)"),
+ 1, GNUNET_NO);
+ return GNUNET_SYSERR;
+ }
+ /* end AF_INET6 */
+ break;
+ default:
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ msg->size = htons (size);
+ GNUNET_TUN_calculate_icmp_checksum (icmp,
+ &i2v[1], mlen);
+ (void) GNUNET_HELPER_send (helper_handle,
+ msg,
+ GNUNET_YES,
+ NULL, NULL);
+ }
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ GNUNET_CONTAINER_heap_update_cost (tunnel_heap,
+ ts->heap_node,
+ GNUNET_TIME_absolute_get ().abs_value);
+ return GNUNET_OK;
+}
+
+
+/**
+ * We got a UDP packet back from the MESH tunnel. Pass it on to the
+ * local virtual interface via the helper.
+ *
+ * @param cls closure, NULL
+ * @param tunnel connection to the other end
+ * @param tunnel_ctx pointer to our 'struct TunnelState *'
+ * @param sender who sent the message
+ * @param message the actual message
+ * @param atsi performance data for the connection
+ * @return GNUNET_OK to keep the connection open,
+ * GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+receive_udp_back (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+ void **tunnel_ctx, const struct GNUNET_PeerIdentity *sender,
+ const struct GNUNET_MessageHeader *message,
+ const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
+{
+ struct TunnelState *ts = *tunnel_ctx;
+ const struct GNUNET_EXIT_UdpReplyMessage *reply;
+ size_t mlen;
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# UDP packets received from mesh"),
+ 1, GNUNET_NO);
+ mlen = ntohs (message->size);
+ if (mlen < sizeof (struct GNUNET_EXIT_UdpReplyMessage))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (NULL == ts->heap_node)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (AF_UNSPEC == ts->af)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ reply = (const struct GNUNET_EXIT_UdpReplyMessage *) message;
+ mlen -= sizeof (struct GNUNET_EXIT_UdpReplyMessage);
+ {
+ char sbuf[INET6_ADDRSTRLEN];
+ char dbuf[INET6_ADDRSTRLEN];
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received UDP reply from mesh, sending %u bytes from %s:%u -> %s:%u via TUN\n",
+ (unsigned int) mlen,
+ inet_ntop (ts->af, &ts->destination_ip, sbuf, sizeof (sbuf)),
+ ts->destination_port,
+ inet_ntop (ts->af, &ts->source_ip, dbuf, sizeof (dbuf)),
+ ts->source_port);
+ }
+ switch (ts->af)
+ {
+ case AF_INET:
+ {
+ size_t size = sizeof (struct GNUNET_TUN_IPv4Header)
+ + sizeof (struct GNUNET_TUN_UdpHeader)
+ + sizeof (struct GNUNET_MessageHeader) +
+ sizeof (struct GNUNET_TUN_Layer2PacketHeader) +
+ mlen;
+ {
+ char buf[size];
+ struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf;
+ struct GNUNET_TUN_Layer2PacketHeader *tun = (struct GNUNET_TUN_Layer2PacketHeader*) &msg[1];
+ struct GNUNET_TUN_IPv4Header *ipv4 = (struct GNUNET_TUN_IPv4Header *) &tun[1];
+ struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipv4[1];
+ msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ msg->size = htons (size);
+ tun->flags = htons (0);
+ tun->proto = htons (ETH_P_IPV4);
+ GNUNET_TUN_initialize_ipv4_header (ipv4,
+ IPPROTO_UDP,
+ sizeof (struct GNUNET_TUN_UdpHeader) + mlen,
+ &ts->destination_ip.v4,
+ &ts->source_ip.v4);
+ if (0 == ntohs (reply->source_port))
+ udp->source_port = htons (ts->destination_port);
+ else
+ udp->source_port = reply->source_port;
+ if (0 == ntohs (reply->destination_port))
+ udp->destination_port = htons (ts->source_port);
+ else
+ udp->destination_port = reply->destination_port;
+ udp->len = htons (mlen + sizeof (struct GNUNET_TUN_UdpHeader));
+ GNUNET_TUN_calculate_udp4_checksum (ipv4,
+ udp,
+ &reply[1],
+ mlen);
+ memcpy (&udp[1],
+ &reply[1],
+ mlen);
+ (void) GNUNET_HELPER_send (helper_handle,
+ msg,
+ GNUNET_YES,
+ NULL, NULL);
+ }
+ }
+ break;
+ case AF_INET6:
+ {
+ size_t size = sizeof (struct GNUNET_TUN_IPv6Header)
+ + sizeof (struct GNUNET_TUN_UdpHeader)
+ + sizeof (struct GNUNET_MessageHeader) +
+ sizeof (struct GNUNET_TUN_Layer2PacketHeader) +
+ mlen;
+ {
+ char buf[size];
+ struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf;
+ struct GNUNET_TUN_Layer2PacketHeader *tun = (struct GNUNET_TUN_Layer2PacketHeader*) &msg[1];
+ struct GNUNET_TUN_IPv6Header *ipv6 = (struct GNUNET_TUN_IPv6Header *) &tun[1];
+ struct GNUNET_TUN_UdpHeader *udp = (struct GNUNET_TUN_UdpHeader *) &ipv6[1];
+ msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ msg->size = htons (size);
+ tun->flags = htons (0);
+ tun->proto = htons (ETH_P_IPV6);
+ GNUNET_TUN_initialize_ipv6_header (ipv6,
+ IPPROTO_UDP,
+ sizeof (struct GNUNET_TUN_UdpHeader) + mlen,
+ &ts->destination_ip.v6,
+ &ts->source_ip.v6);
+ if (0 == ntohs (reply->source_port))
+ udp->source_port = htons (ts->destination_port);
+ else
+ udp->source_port = reply->source_port;
+ if (0 == ntohs (reply->destination_port))
+ udp->destination_port = htons (ts->source_port);
+ else
+ udp->destination_port = reply->destination_port;
+ udp->len = htons (mlen + sizeof (struct GNUNET_TUN_UdpHeader));
+ GNUNET_TUN_calculate_udp6_checksum (ipv6,
+ udp,
+ &reply[1], mlen);
+ memcpy (&udp[1],
+ &reply[1],
+ mlen);
+ (void) GNUNET_HELPER_send (helper_handle,
+ msg,
+ GNUNET_YES,
+ NULL, NULL);
+ }
+ }
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ GNUNET_CONTAINER_heap_update_cost (tunnel_heap,
+ ts->heap_node,
+ GNUNET_TIME_absolute_get ().abs_value);
+ return GNUNET_OK;
+}
+
+
+/**
+ * We got a TCP packet back from the MESH tunnel. Pass it on to the
+ * local virtual interface via the helper.
+ *
+ * @param cls closure, NULL
+ * @param tunnel connection to the other end
+ * @param tunnel_ctx pointer to our 'struct TunnelState *'
+ * @param sender who sent the message
+ * @param message the actual message
+ * @param atsi performance data for the connection
+ * @return GNUNET_OK to keep the connection open,
+ * GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+receive_tcp_back (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+ void **tunnel_ctx,
+ const struct GNUNET_PeerIdentity *sender GNUNET_UNUSED,
+ const struct GNUNET_MessageHeader *message,
+ const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
+{
+ struct TunnelState *ts = *tunnel_ctx;
+ const struct GNUNET_EXIT_TcpDataMessage *data;
+ size_t mlen;
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# TCP packets received from mesh"),
+ 1, GNUNET_NO);
+ mlen = ntohs (message->size);
+ if (mlen < sizeof (struct GNUNET_EXIT_TcpDataMessage))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (NULL == ts->heap_node)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ data = (const struct GNUNET_EXIT_TcpDataMessage *) message;
+ mlen -= sizeof (struct GNUNET_EXIT_TcpDataMessage);
+ {
+ char sbuf[INET6_ADDRSTRLEN];
+ char dbuf[INET6_ADDRSTRLEN];
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received TCP reply from mesh, sending %u bytes from %s:%u -> %s:%u via TUN\n",
+ (unsigned int) mlen,
+ inet_ntop (ts->af, &ts->destination_ip, sbuf, sizeof (sbuf)),
+ ts->destination_port,
+ inet_ntop (ts->af, &ts->source_ip, dbuf, sizeof (dbuf)),
+ ts->source_port);
+ }
+ if (data->tcp_header.off * 4 < sizeof (struct GNUNET_TUN_TcpHeader))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ switch (ts->af)
+ {
+ case AF_INET:
+ {
+ size_t size = sizeof (struct GNUNET_TUN_IPv4Header)
+ + sizeof (struct GNUNET_TUN_TcpHeader)
+ + sizeof (struct GNUNET_MessageHeader) +
+ sizeof (struct GNUNET_TUN_Layer2PacketHeader) +
+ mlen;
+ {
+ char buf[size];
+ struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf;
+ struct GNUNET_TUN_Layer2PacketHeader *tun = (struct GNUNET_TUN_Layer2PacketHeader*) &msg[1];
+ struct GNUNET_TUN_IPv4Header *ipv4 = (struct GNUNET_TUN_IPv4Header *) &tun[1];
+ struct GNUNET_TUN_TcpHeader *tcp = (struct GNUNET_TUN_TcpHeader *) &ipv4[1];
+ msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ msg->size = htons (size);
+ tun->flags = htons (0);
+ tun->proto = htons (ETH_P_IPV4);
+ GNUNET_TUN_initialize_ipv4_header (ipv4,
+ IPPROTO_TCP,
+ sizeof (struct GNUNET_TUN_TcpHeader) + mlen,
+ &ts->destination_ip.v4,
+ &ts->source_ip.v4);
+ *tcp = data->tcp_header;
+ tcp->source_port = htons (ts->destination_port);
+ tcp->destination_port = htons (ts->source_port);
+ GNUNET_TUN_calculate_tcp4_checksum (ipv4,
+ tcp,
+ &data[1],
+ mlen);
+ memcpy (&tcp[1],
+ &data[1],
+ mlen);
+ (void) GNUNET_HELPER_send (helper_handle,
+ msg,
+ GNUNET_YES,
+ NULL, NULL);
+ }
+ }
+ break;
+ case AF_INET6:
+ {
+ size_t size = sizeof (struct GNUNET_TUN_IPv6Header)
+ + sizeof (struct GNUNET_TUN_TcpHeader)
+ + sizeof (struct GNUNET_MessageHeader) +
+ sizeof (struct GNUNET_TUN_Layer2PacketHeader) +
+ mlen;
+ {
+ char buf[size];
+ struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) buf;
+ struct GNUNET_TUN_Layer2PacketHeader *tun = (struct GNUNET_TUN_Layer2PacketHeader*) &msg[1];
+ struct GNUNET_TUN_IPv6Header *ipv6 = (struct GNUNET_TUN_IPv6Header *) &tun[1];
+ struct GNUNET_TUN_TcpHeader *tcp = (struct GNUNET_TUN_TcpHeader *) &ipv6[1];
+ msg->type = htons (GNUNET_MESSAGE_TYPE_VPN_HELPER);
+ msg->size = htons (size);
+ tun->flags = htons (0);
+ tun->proto = htons (ETH_P_IPV6);
+ GNUNET_TUN_initialize_ipv6_header (ipv6,
+ IPPROTO_TCP,
+ sizeof (struct GNUNET_TUN_TcpHeader) + mlen,
+ &ts->destination_ip.v6,
+ &ts->source_ip.v6);
+ *tcp = data->tcp_header;
+ tcp->source_port = htons (ts->destination_port);
+ tcp->destination_port = htons (ts->source_port);
+ GNUNET_TUN_calculate_tcp6_checksum (ipv6,
+ tcp,
+ &data[1],
+ mlen);
+ memcpy (&tcp[1],
+ &data[1],
+ mlen);
+ (void) GNUNET_HELPER_send (helper_handle,
+ msg,
+ GNUNET_YES,
+ NULL, NULL);
+ }
+ }
+ break;
+ }
+ GNUNET_CONTAINER_heap_update_cost (tunnel_heap,
+ ts->heap_node,
+ GNUNET_TIME_absolute_get ().abs_value);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Allocate an IPv4 address from the range of the tunnel
+ * for a new redirection.
+ *
+ * @param v4 where to store the address
+ * @return GNUNET_OK on success,
+ * GNUNET_SYSERR on error
+ */
+static int
+allocate_v4_address (struct in_addr *v4)
+{
+ const char *ipv4addr = vpn_argv[4];
+ const char *ipv4mask = vpn_argv[5];
+ struct in_addr addr;
+ struct in_addr mask;
+ struct in_addr rnd;
+ GNUNET_HashCode key;
+ unsigned int tries;
+
+ GNUNET_assert (1 == inet_pton (AF_INET, ipv4addr, &addr));
+ GNUNET_assert (1 == inet_pton (AF_INET, ipv4mask, &mask));
+ /* Given 192.168.0.1/255.255.0.0, we want a mask
+ of '192.168.255.255', thus: */
+ mask.s_addr = addr.s_addr | ~mask.s_addr;
+ tries = 0;
+ do
+ {
+ tries++;
+ if (tries > 16)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed to find unallocated IPv4 address in VPN's range\n"));
+ return GNUNET_SYSERR;
+ }
+ /* Pick random IPv4 address within the subnet, except 'addr' or 'mask' itself */
+ rnd.s_addr = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
+ UINT32_MAX);
+ v4->s_addr = (addr.s_addr | rnd.s_addr) & mask.s_addr;
+ get_destination_key_from_ip (AF_INET,
+ v4,
+ &key);
+ }
+ while ( (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_contains (destination_map,
+ &key)) ||
+ (v4->s_addr == addr.s_addr) ||
+ (v4->s_addr == mask.s_addr) );
+ return GNUNET_OK;
+}
+
+
+/**
+ * Allocate an IPv6 address from the range of the tunnel
+ * for a new redirection.
+ *
+ * @param v6 where to store the address
+ * @return GNUNET_OK on success,
+ * GNUNET_SYSERR on error
+ */
+static int
+allocate_v6_address (struct in6_addr *v6)
+{
+ const char *ipv6addr = vpn_argv[2];
+ struct in6_addr addr;
+ struct in6_addr mask;
+ struct in6_addr rnd;
+ int i;
+ GNUNET_HashCode key;
+ unsigned int tries;
+
+ GNUNET_assert (1 == inet_pton (AF_INET6, ipv6addr, &addr));
+ GNUNET_assert (ipv6prefix < 128);
+ /* Given ABCD::/96, we want a mask of 'ABCD::FFFF:FFFF,
+ thus: */
+ mask = addr;
+ for (i=127;i>=ipv6prefix;i--)
+ mask.s6_addr[i / 8] |= (1 << (i % 8));
+
+ /* Pick random IPv6 address within the subnet, except 'addr' or 'mask' itself */
+ tries = 0;
+ do
+ {
+ tries++;
+ if (tries > 16)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed to find unallocated IPv6 address in VPN's range\n"));
+ return GNUNET_SYSERR;
+
+ }
+ for (i=0;i<16;i++)
+ {
+ rnd.s6_addr[i] = (unsigned char) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
+ 256);
+ v6->s6_addr[i]
+ = (addr.s6_addr[i] | rnd.s6_addr[i]) & mask.s6_addr[i];
+ }
+ get_destination_key_from_ip (AF_INET6,
+ v6,
+ &key);
+ }
+ while ( (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_contains (destination_map,
+ &key)) ||
+ (0 == memcmp (v6,
+ &addr,
+ sizeof (struct in6_addr))) ||
+ (0 == memcmp (v6,
+ &mask,
+ sizeof (struct in6_addr))) );
+ return GNUNET_OK;
+}
+
+
+/**
+ * Free resources occupied by a destination entry.
+ *
+ * @param de entry to free
+ */
+static void
+free_destination_entry (struct DestinationEntry *de)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Cleaning up destination entry\n");
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Active destinations"),
+ -1, GNUNET_NO);
+ if (NULL != de->ts)
+ {
+ free_tunnel_state (de->ts);
+ GNUNET_assert (NULL == de->ts);
+ }
+ if (NULL != de->heap_node)
+ {
+ GNUNET_CONTAINER_heap_remove_node (de->heap_node);
+ de->heap_node = NULL;
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (destination_map,
+ &de->key,
+ de));
+ }
+ GNUNET_free (de);
+}
+
+
+/**
+ * We have too many active destinations. Clean up the oldest destination.
+ *
+ * @param except destination that must NOT be cleaned up, even if it is the oldest
+ */
+static void
+expire_destination (struct DestinationEntry *except)
+{
+ struct DestinationEntry *de;
+
+ de = GNUNET_CONTAINER_heap_peek (destination_heap);
+ GNUNET_assert (NULL != de);
+ if (except == de)
+ return; /* can't do this */
+ free_destination_entry (de);
+}
+
+
+/**
+ * A client asks us to setup a redirection via some exit
+ * node to a particular IP. Setup the redirection and
+ * give the client the allocated IP.
+ *
+ * @param cls unused
+ * @param client requesting client
+ * @param message redirection request (a 'struct RedirectToIpRequestMessage')
+ */
+static void
+service_redirect_to_ip (void *cls GNUNET_UNUSED, struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ size_t mlen;
+ size_t alen;
+ const struct RedirectToIpRequestMessage *msg;
+ int addr_af;
+ int result_af;
+ struct in_addr v4;
+ struct in6_addr v6;
+ void *addr;
+ struct DestinationEntry *de;
+ GNUNET_HashCode key;
+ struct TunnelState *ts;
+
+ /* validate and parse request */
+ mlen = ntohs (message->size);
+ if (mlen < sizeof (struct RedirectToIpRequestMessage))
+ {
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ alen = mlen - sizeof (struct RedirectToIpRequestMessage);
+ msg = (const struct RedirectToIpRequestMessage *) message;
+ addr_af = (int) htonl (msg->addr_af);
+ switch (addr_af)
+ {
+ case AF_INET:
+ if (alen != sizeof (struct in_addr))
+ {
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ break;
+ case AF_INET6:
+ if (alen != sizeof (struct in6_addr))
+ {
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ break;
+ default:
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+
+ /* allocate response IP */
+ addr = NULL;
+ result_af = (int) htonl (msg->result_af);
+ switch (result_af)
+ {
+ case AF_INET:
+ if (GNUNET_OK !=
+ allocate_v4_address (&v4))
+ result_af = AF_UNSPEC;
+ else
+ addr = &v4;
+ break;
+ case AF_INET6:
+ if (GNUNET_OK !=
+ allocate_v6_address (&v6))
+ result_af = AF_UNSPEC;
+ else
+ addr = &v6;
+ break;
+ case AF_UNSPEC:
+ if (GNUNET_OK ==
+ allocate_v4_address (&v4))
+ {
+ addr = &v4;
+ result_af = AF_INET;
+ }
+ else if (GNUNET_OK ==
+ allocate_v6_address (&v6))
+ {
+ addr = &v6;
+ result_af = AF_INET6;
+ }
+ break;
+ default:
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ if ( (result_af == AF_UNSPEC) ||
+ (GNUNET_NO == ntohl (msg->nac)) )
+ {
+ /* send reply "instantly" */
+ send_client_reply (client,
+ msg->request_id,
+ result_af,
+ addr);
+ }
+ if (result_af == AF_UNSPEC)
+ {
+ /* failure, we're done */
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+ return;
+ }
+
+ {
+ char sbuf[INET6_ADDRSTRLEN];
+ char dbuf[INET6_ADDRSTRLEN];
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Allocated address %s for redirection via exit to %s\n",
+ inet_ntop (result_af, addr, sbuf, sizeof (sbuf)),
+ inet_ntop (addr_af,
+ &msg[1], dbuf, sizeof (dbuf)));
+ }
+
+ /* setup destination record */
+ de = GNUNET_malloc (sizeof (struct DestinationEntry));
+ de->is_service = GNUNET_NO;
+ de->details.exit_destination.af = addr_af;
+ memcpy (&de->details.exit_destination.ip,
+ &msg[1],
+ alen);
+ get_destination_key_from_ip (result_af,
+ addr,
+ &key);
+ de->key = key;
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (destination_map,
+ &key,
+ de,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
+ de->heap_node = GNUNET_CONTAINER_heap_insert (destination_heap,
+ de,
+ GNUNET_TIME_absolute_ntoh (msg->expiration_time).abs_value);
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Active destinations"),
+ 1, GNUNET_NO);
+ while (GNUNET_CONTAINER_multihashmap_size (destination_map) > max_destination_mappings)
+ expire_destination (de);
+
+ /* setup tunnel to destination */
+ ts = create_tunnel_to_destination (de,
+ (GNUNET_NO == ntohl (msg->nac)) ? NULL : client,
+ result_af,
+ msg->request_id);
+ switch (result_af)
+ {
+ case AF_INET:
+ ts->destination_ip.v4 = v4;
+ break;
+ case AF_INET6:
+ ts->destination_ip.v6 = v6;
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ /* we're done */
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
+
+
+/**
+ * A client asks us to setup a redirection to a particular peer
+ * offering a service. Setup the redirection and give the client the
+ * allocated IP.
+ *
+ * @param cls unused
+ * @param client requesting client
+ * @param message redirection request (a 'struct RedirectToPeerRequestMessage')
+ */
+static void
+service_redirect_to_service (void *cls GNUNET_UNUSED, struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ const struct RedirectToServiceRequestMessage *msg;
+ int result_af;
+ struct in_addr v4;
+ struct in6_addr v6;
+ void *addr;
+ struct DestinationEntry *de;
+ GNUNET_HashCode key;
+ struct TunnelState *ts;
+
+ /* parse request */
+ msg = (const struct RedirectToServiceRequestMessage *) message;
+
+ /* allocate response IP */
+ addr = NULL;
+ result_af = (int) htonl (msg->result_af);
+ switch (result_af)
+ {
+ case AF_INET:
+ if (GNUNET_OK !=
+ allocate_v4_address (&v4))
+ result_af = AF_UNSPEC;
+ else
+ addr = &v4;
+ break;
+ case AF_INET6:
+ if (GNUNET_OK !=
+ allocate_v6_address (&v6))
+ result_af = AF_UNSPEC;
+ else
+ addr = &v6;
+ break;
+ case AF_UNSPEC:
+ if (GNUNET_OK ==
+ allocate_v4_address (&v4))
+ {
+ addr = &v4;
+ result_af = AF_INET;
+ }
+ else if (GNUNET_OK ==
+ allocate_v6_address (&v6))
+ {
+ addr = &v6;
+ result_af = AF_INET6;
+ }
+ break;
+ default:
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ if ( (result_af == AF_UNSPEC) ||
+ (GNUNET_NO == ntohl (msg->nac)) )
+ {
+ /* send reply "instantly" */
+ send_client_reply (client,
+ msg->request_id,
+ result_af,
+ addr);
+ }
+ if (result_af == AF_UNSPEC)
+ {
+ /* failure, we're done */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Failed to allocate IP address for new destination\n"));
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+ return;
+ }
+
+ {
+ char sbuf[INET6_ADDRSTRLEN];
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Allocated address %s for redirection to service %s on peer %s\n",
+ inet_ntop (result_af, addr, sbuf, sizeof (sbuf)),
+ GNUNET_h2s (&msg->service_descriptor),
+ GNUNET_i2s (&msg->target));
+ }
+
+ /* setup destination record */
+ de = GNUNET_malloc (sizeof (struct DestinationEntry));
+ de->is_service = GNUNET_YES;
+ de->details.service_destination.service_descriptor = msg->service_descriptor;
+ de->details.service_destination.target = msg->target;
+ get_destination_key_from_ip (result_af,
+ addr,
+ &key);
+ de->key = key;
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (destination_map,
+ &key,
+ de,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
+ de->heap_node = GNUNET_CONTAINER_heap_insert (destination_heap,
+ de,
+ GNUNET_TIME_absolute_ntoh (msg->expiration_time).abs_value);
+ while (GNUNET_CONTAINER_multihashmap_size (destination_map) > max_destination_mappings)
+ expire_destination (de);
+ ts = create_tunnel_to_destination (de,
+ (GNUNET_NO == ntohl (msg->nac)) ? NULL : client,
+ result_af,
+ msg->request_id);
+ switch (result_af)
+ {
+ case AF_INET:
+ ts->destination_ip.v4 = v4;
+ break;
+ case AF_INET6:
+ ts->destination_ip.v6 = v6;
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ /* we're done */
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
+
+
+
+/**
+ * Function called for inbound tunnels. As we don't offer
+ * any mesh services, this function should never be called.
+ *
+ * @param cls closure
+ * @param tunnel new handle to the tunnel
+ * @param initiator peer that started the tunnel
+ * @param atsi performance information for the tunnel
+ * @return initial tunnel context for the tunnel
+ * (can be NULL -- that's not an error)
+ */
+static void *
+inbound_tunnel_cb (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
+ const struct GNUNET_PeerIdentity *initiator,
+ const struct GNUNET_ATS_Information *atsi)
+{
+ /* How can and why should anyone open an inbound tunnel to vpn? */
+ GNUNET_break (0);
+ return NULL;
+}
+
+
+/**
+ * Function called whenever an inbound tunnel is destroyed. Should clean up
+ * any associated state.
+ *
+ * @param cls closure (set from GNUNET_MESH_connect)
+ * @param tunnel connection to the other end (henceforth invalid)
+ * @param tunnel_ctx place where local state associated
+ * with the tunnel is stored (our 'struct TunnelState')
+ */
+static void
+tunnel_cleaner (void *cls, const struct GNUNET_MESH_Tunnel *tunnel, void *tunnel_ctx)
+{
+ /* we don't have inbound tunnels, so this function should never be called */
+ GNUNET_break (0);
+}
+
+
+/**
+ * Free memory occupied by an entry in the destination map.
+ *
+ * @param cls unused
+ * @param key unused
+ * @param value a 'struct DestinationEntry *'
+ * @return GNUNET_OK (continue to iterate)
+ */
+static int
+cleanup_destination (void *cls,
+ const GNUNET_HashCode *key,
+ void *value)
+{
+ struct DestinationEntry *de = value;
+
+ free_destination_entry (de);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Free memory occupied by an entry in the tunnel map.
+ *
+ * @param cls unused
+ * @param key unused
+ * @param value a 'struct TunnelState *'
+ * @return GNUNET_OK (continue to iterate)
+ */
+static int
+cleanup_tunnel (void *cls,
+ const GNUNET_HashCode *key,
+ void *value)
+{
+ struct TunnelState *ts = value;
+
+ free_tunnel_state (ts);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function scheduled as very last function, cleans up after us
+ *
+ * @param cls unused
+ * @param tc unused
+ */
+static void
+cleanup (void *cls GNUNET_UNUSED,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ unsigned int i;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "VPN is shutting down\n");
+ if (NULL != destination_map)
+ {
+ GNUNET_CONTAINER_multihashmap_iterate (destination_map,
+ &cleanup_destination,
+ NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (destination_map);
+ destination_map = NULL;
+ }
+ if (NULL != destination_heap)
+ {
+ GNUNET_CONTAINER_heap_destroy (destination_heap);
+ destination_heap = NULL;
+ }
+ if (NULL != tunnel_map)
+ {
+ GNUNET_CONTAINER_multihashmap_iterate (tunnel_map,
+ &cleanup_tunnel,
+ NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (tunnel_map);
+ tunnel_map = NULL;
+ }
+ if (NULL != tunnel_heap)
+ {
+ GNUNET_CONTAINER_heap_destroy (tunnel_heap);
+ tunnel_heap = NULL;
+ }
+ if (NULL != mesh_handle)
+ {
+ GNUNET_MESH_disconnect (mesh_handle);
+ mesh_handle = NULL;
+ }
+ if (NULL != helper_handle)
+ {
+ GNUNET_HELPER_stop (helper_handle);
+ helper_handle = NULL;
+ }
+ if (NULL != nc)
+ {
+ GNUNET_SERVER_notification_context_destroy (nc);
+ nc = NULL;
+ }
+ if (stats != NULL)
+ {
+ GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
+ stats = NULL;
+ }
+ for (i=0;i<5;i++)
+ GNUNET_free_non_null (vpn_argv[i]);
+}
+
+
+/**
+ * A client disconnected, clean up all references to it.
+ *
+ * @param cls the client that disconnected
+ * @param key unused
+ * @param value a 'struct TunnelState *'
+ * @return GNUNET_OK (continue to iterate)
+ */
+static int
+cleanup_tunnel_client (void *cls,
+ const GNUNET_HashCode *key,
+ void *value)
+{
+ struct GNUNET_SERVER_Client *client = cls;
+ struct TunnelState *ts = value;
+
+ if (client == ts->client)
+ {
+ GNUNET_SERVER_client_drop (ts->client);
+ ts->client = NULL;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * A client disconnected, clean up all references to it.
+ *
+ * @param cls the client that disconnected
+ * @param key unused
+ * @param value a 'struct DestinationEntry *'
+ * @return GNUNET_OK (continue to iterate)
+ */
+static int
+cleanup_destination_client (void *cls,
+ const GNUNET_HashCode *key,
+ void *value)
+{
+ struct GNUNET_SERVER_Client *client = cls;
+ struct DestinationEntry *de = value;
+ struct TunnelState *ts;
+
+ if (NULL == (ts = de->ts))
+ return GNUNET_OK;
+ if (client == ts->client)
+ {
+ GNUNET_SERVER_client_drop (ts->client);
+ ts->client = NULL;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * A client has disconnected from us. If we are currently building
+ * a tunnel for it, cancel the operation.
+ *
+ * @param cls unused
+ * @param client handle to the client that disconnected
+ */
+static void
+client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
+{
+ if (NULL != tunnel_map)
+ GNUNET_CONTAINER_multihashmap_iterate (tunnel_map,
+ &cleanup_tunnel_client,
+ client);
+ if (NULL != destination_map)
+ GNUNET_CONTAINER_multihashmap_iterate (destination_map,
+ &cleanup_destination_client,
+ client);
+}
+
+
+/**
+ * Test if the given AF is supported by this system.
+ *
+ * @param af to test
+ * @return GNUNET_OK if the AF is supported
+ */
+static int
+test_af (int af)
+{
+ int s;
+
+ s = socket (af, SOCK_STREAM, 0);
+ if (-1 == s)
+ {
+ if (EAFNOSUPPORT == errno)
+ return GNUNET_NO;
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "socket");
+ return GNUNET_SYSERR;
+ }
+ close (s);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param server the initialized server
+ * @param cfg_ configuration
+ */
+static void
+run (void *cls,
+ struct GNUNET_SERVER_Handle *server,
+ const struct GNUNET_CONFIGURATION_Handle *cfg_)
+{
+ static const struct GNUNET_SERVER_MessageHandler service_handlers[] = {
+ /* callback, cls, type, size */
+ { &service_redirect_to_ip, NULL, GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_IP, 0},
+ { &service_redirect_to_service, NULL,
+ GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_SERVICE,
+ sizeof (struct RedirectToServiceRequestMessage) },
+ {NULL, NULL, 0, 0}
+ };
+ static const struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
+ { &receive_udp_back, GNUNET_MESSAGE_TYPE_VPN_UDP_REPLY, 0},
+ { &receive_tcp_back, GNUNET_MESSAGE_TYPE_VPN_TCP_DATA_TO_VPN, 0},
+ { &receive_icmp_back, GNUNET_MESSAGE_TYPE_VPN_ICMP_TO_VPN, 0},
+ {NULL, 0, 0}
+ };
+ static const GNUNET_MESH_ApplicationType types[] = {
+ GNUNET_APPLICATION_TYPE_END
+ };
+ char *ifname;
+ char *ipv6addr;
+ char *ipv6prefix_s;
+ char *ipv4addr;
+ char *ipv4mask;
+ struct in_addr v4;
+ struct in6_addr v6;
+
+ if (GNUNET_YES !=
+ GNUNET_OS_check_helper_binary ("gnunet-helper-vpn"))
+ {
+ fprintf (stderr,
+ "`%s' is not SUID, refusing to run.\n",
+ "gnunet-helper-vpn");
+ global_ret = 1;
+ return;
+ }
+ cfg = cfg_;
+ stats = GNUNET_STATISTICS_create ("vpn", cfg);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg, "vpn", "MAX_MAPPING",
+ &max_destination_mappings))
+ max_destination_mappings = 200;
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg, "vpn", "MAX_TUNNELS",
+ &max_tunnel_mappings))
+ max_tunnel_mappings = 200;
+
+ destination_map = GNUNET_CONTAINER_multihashmap_create (max_destination_mappings * 2);
+ destination_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
+ tunnel_map = GNUNET_CONTAINER_multihashmap_create (max_tunnel_mappings * 2);
+ tunnel_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
+
+
+ vpn_argv[0] = GNUNET_strdup ("vpn-gnunet");
+ if (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, "vpn", "IFNAME", &ifname))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No entry 'IFNAME' in configuration!\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ vpn_argv[1] = ifname;
+ if (GNUNET_OK == test_af (AF_INET6))
+ {
+ if ( (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, "vpn", "IPV6ADDR",
+ &ipv6addr) ||
+ (1 != inet_pton (AF_INET6, ipv6addr, &v6))) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No valid entry 'IPV6ADDR' in configuration!\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ vpn_argv[2] = ipv6addr;
+ if (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, "vpn", "IPV6PREFIX",
+ &ipv6prefix_s))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No entry 'IPV6PREFIX' in configuration!\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ vpn_argv[3] = ipv6prefix_s;
+ if ( (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg, "vpn",
+ "IPV6PREFIX",
+ &ipv6prefix)) ||
+ (ipv6prefix >= 127) )
+ {
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("IPv6 support disabled as this system does not support IPv6\n"));
+ vpn_argv[2] = GNUNET_strdup ("-");
+ vpn_argv[3] = GNUNET_strdup ("-");
+ }
+ if (GNUNET_OK == test_af (AF_INET))
+ {
+ if ( (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, "vpn", "IPV4ADDR",
+ &ipv4addr) ||
+ (1 != inet_pton (AF_INET, ipv4addr, &v4))) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No valid entry for 'IPV4ADDR' in configuration!\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ vpn_argv[4] = ipv4addr;
+ if ( (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, "vpn", "IPV4MASK",
+ &ipv4mask) ||
+ (1 != inet_pton (AF_INET, ipv4mask, &v4))) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No valid entry 'IPV4MASK' in configuration!\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ vpn_argv[5] = ipv4mask;
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("IPv4 support disabled as this system does not support IPv4\n"));
+ vpn_argv[4] = GNUNET_strdup ("-");
+ vpn_argv[5] = GNUNET_strdup ("-");
+ }
+ vpn_argv[6] = NULL;
+
+ mesh_handle =
+ GNUNET_MESH_connect (cfg_, 42 /* queue length */, NULL,
+ &inbound_tunnel_cb,
+ &tunnel_cleaner,
+ mesh_handlers,
+ types);
+ helper_handle = GNUNET_HELPER_start ("gnunet-helper-vpn", vpn_argv,
+ &message_token, NULL);
+ nc = GNUNET_SERVER_notification_context_create (server, 1);
+ GNUNET_SERVER_add_handlers (server, service_handlers);
+ GNUNET_SERVER_disconnect_notify (server, &client_disconnect, NULL);
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup, cls);
+}
+
+
+/**
+ * The main function of the VPN service.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+ return (GNUNET_OK ==
+ GNUNET_SERVICE_run (argc, argv, "vpn",
+ GNUNET_SERVICE_OPTION_NONE,
+ &run, NULL)) ? global_ret : 1;
+}
+
+/* end of gnunet-service-vpn.c */
diff --git a/src/vpn/gnunet-vpn.c b/src/vpn/gnunet-vpn.c
new file mode 100644
index 0000000..ecc0442
--- /dev/null
+++ b/src/vpn/gnunet-vpn.c
@@ -0,0 +1,334 @@
+/*
+ This file is part of GNUnet.
+ (C) 2012 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet 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 GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/vpn/gnunet-vpn.c
+ * @brief Tool to manually request VPN tunnels to be created
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_vpn_service.h"
+
+
+/**
+ * Handle to vpn service.
+ */
+static struct GNUNET_VPN_Handle *handle;
+
+/**
+ * Opaque redirection request handle.
+ */
+static struct GNUNET_VPN_RedirectionRequest *request;
+
+/**
+ * Option -p: destination peer identity for service
+ */
+static char *peer_id;
+
+/**
+ * Option -s: service name (hash to get service descriptor)
+ */
+static char *service_name;
+
+/**
+ * Option -i: target IP
+ */
+static char *target_ip;
+
+/**
+ * Option -4: IPv4 requested.
+ */
+static int ipv4;
+
+/**
+ * Option -6: IPv6 requested.
+ */
+static int ipv6;
+
+/**
+ * Option -t: TCP requested.
+ */
+static int tcp;
+
+/**
+ * Option -u: UDP requested.
+ */
+static int udp;
+
+/**
+ * Selected level of verbosity.
+ */
+static int verbosity;
+
+/**
+ * Option '-a': Notify only once the tunnel is connected?
+ */
+static int nac;
+
+/**
+ * Global return value.
+ */
+static int ret;
+
+/**
+ * Option '-d': duration of the mapping
+ */
+static unsigned long long duration = 5 * 60;
+
+
+/**
+ * Shutdown.
+ */
+static void
+do_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ if (NULL != request)
+ {
+ GNUNET_VPN_cancel_request (request);
+ request = NULL;
+ }
+ if (NULL != handle)
+ {
+ GNUNET_VPN_disconnect (handle);
+ handle = NULL;
+ }
+ GNUNET_free_non_null (peer_id);
+ GNUNET_free_non_null (service_name);
+ GNUNET_free_non_null (target_ip);
+}
+
+
+/**
+ * Callback invoked from the VPN service once a redirection is
+ * available. Provides the IP address that can now be used to
+ * reach the requested destination.
+ *
+ * @param cls closure
+ * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
+ * will match 'result_af' from the request
+ * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
+ * that the VPN allocated for the redirection;
+ * traffic to this IP will now be redirected to the
+ * specified target peer; NULL on error
+ */
+static void
+allocation_cb (void *cls,
+ int af,
+ const void *address)
+{
+ char buf[INET6_ADDRSTRLEN];
+
+ request = NULL;
+ switch (af)
+ {
+ case AF_INET6:
+ case AF_INET:
+ FPRINTF (stdout,
+ "%s\n",
+ inet_ntop (af, address, buf, sizeof (buf)));
+ break;
+ case AF_UNSPEC:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Error creating tunnel\n"));
+ ret = 1;
+ break;
+ default:
+ break;
+ }
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param cfg configuration
+ */
+static void
+run (void *cls, char *const *args, const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ int dst_af;
+ int req_af;
+ struct GNUNET_PeerIdentity peer;
+ GNUNET_HashCode sd;
+ const void *addr;
+ struct in_addr v4;
+ struct in6_addr v6;
+ uint8_t protocol;
+ struct GNUNET_TIME_Absolute etime;
+
+ etime = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
+ (unsigned int) duration));
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
+ &do_disconnect, NULL);
+ handle = GNUNET_VPN_connect (cfg);
+ if (NULL == handle)
+ goto error;
+ req_af = AF_UNSPEC;
+ if (ipv4)
+ {
+ if (ipv6)
+ {
+ FPRINTF (stderr, _("Option `%s' makes no sense with option `%s'.\n"),
+ "-4", "-6");
+ goto error;
+ }
+ req_af = AF_INET;
+ }
+ if (ipv6)
+ req_af = AF_INET6;
+
+ if (NULL == target_ip)
+ {
+ if (NULL == service_name)
+ {
+ FPRINTF (stderr, _("Option `%s' or `%s' is required.\n"),
+ "-i", "-s");
+ goto error;
+ }
+ if (NULL == peer_id)
+ {
+ FPRINTF (stderr, _("Option `%s' is required when using option `%s'.\n"),
+ "-p", "-s");
+ goto error;
+ }
+ if (! (tcp | udp) )
+ {
+ FPRINTF (stderr, _("Option `%s' or `%s' is required when using option `%s'.\n"),
+ "-t", "-u", "-s");
+ goto error;
+ }
+ if (tcp & udp)
+ {
+ FPRINTF (stderr, _("Option `%s' makes no sense with option `%s'.\n"),
+ "-t", "-u");
+ goto error;
+ }
+ if (tcp)
+ protocol = IPPROTO_TCP;
+ if (udp)
+ protocol = IPPROTO_UDP;
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_hash_from_string (peer_id,
+ &peer.hashPubKey))
+ {
+ FPRINTF (stderr, _("`%s' is not a valid peer identifier.\n"),
+ peer_id);
+ goto error;
+ }
+ GNUNET_CRYPTO_hash (service_name,
+ strlen (service_name),
+ &sd);
+ request = GNUNET_VPN_redirect_to_peer (handle,
+ req_af,
+ protocol,
+ &peer,
+ &sd,
+ nac,
+ etime,
+ &allocation_cb, NULL);
+ }
+ else
+ {
+ if (1 != inet_pton (AF_INET6, target_ip, &v6))
+ {
+ if (1 != inet_pton (AF_INET, target_ip, &v4))
+ {
+ FPRINTF (stderr, _("`%s' is not a valid IP address.\n"),
+ target_ip);
+ goto error;
+ }
+ else
+ {
+ dst_af = AF_INET;
+ addr = &v4;
+ }
+ }
+ else
+ {
+ dst_af = AF_INET6;
+ addr = &v6;
+ }
+ request = GNUNET_VPN_redirect_to_ip (handle,
+ req_af,
+ dst_af,
+ addr,
+ nac,
+ etime,
+ &allocation_cb, NULL);
+ }
+ return;
+
+ error:
+ GNUNET_SCHEDULER_shutdown ();
+ ret = 1;
+}
+
+
+int
+main (int argc, char *const *argv)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ {'4', "ipv4", NULL,
+ gettext_noop ("request that result should be an IPv4 address"),
+ 0, &GNUNET_GETOPT_set_one, &ipv4},
+ {'6', "ipv6", NULL,
+ gettext_noop ("request that result should be an IPv6 address"),
+ 0, &GNUNET_GETOPT_set_one, &ipv6},
+ {'a', "after-connect", NULL,
+ gettext_noop ("print IP address only after mesh tunnel has been created"),
+ 0, &GNUNET_GETOPT_set_one, &ipv6},
+ {'d', "duration", "SECONDS",
+ gettext_noop ("how long should the mapping be valid for new tunnels?"),
+ 1, &GNUNET_GETOPT_set_ulong, &duration},
+ {'i', "ip", "IP",
+ gettext_noop ("destination IP for the tunnel"),
+ 1, &GNUNET_GETOPT_set_string, &target_ip},
+ {'p', "peer", "PEERID",
+ gettext_noop ("peer offering the service we would like to access"),
+ 1, &GNUNET_GETOPT_set_string, &peer_id},
+ {'s', "service", "NAME",
+ gettext_noop ("name of the service we would like to access"),
+ 1, &GNUNET_GETOPT_set_string, &service_name},
+ {'t', "tcp", NULL,
+ gettext_noop ("service is offered via TCP"),
+ 0, &GNUNET_GETOPT_set_one, &tcp},
+ {'u', "udp", NULL,
+ gettext_noop ("service is offered via UDP"),
+ 0, &GNUNET_GETOPT_set_one, &udp},
+
+ GNUNET_GETOPT_OPTION_VERBOSE (&verbosity),
+ GNUNET_GETOPT_OPTION_END
+ };
+ return (GNUNET_OK ==
+ GNUNET_PROGRAM_run (argc, argv, "gnunet-vpn",
+ gettext_noop
+ ("Setup tunnels via VPN."), options,
+ &run, NULL)) ? ret : 1;
+}
+
+
+/* end of gnunet-vpn.c */
diff --git a/src/vpn/test_gnunet_vpn.c b/src/vpn/test_gnunet_vpn.c
new file mode 100644
index 0000000..005c7bd
--- /dev/null
+++ b/src/vpn/test_gnunet_vpn.c
@@ -0,0 +1,605 @@
+/*
+ This file is part of GNUnet
+ (C) 2007, 2009, 2011, 2012 Christian Grothoff
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet 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 GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file test_gnunet_vpn.c
+ * @brief testcase for tunneling HTTP over the GNUnet VPN
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <curl/curl.h>
+#include <microhttpd.h>
+#include "gnunet_vpn_service.h"
+#include "gnunet_arm_service.h"
+
+#define PORT 48080
+
+#define START_ARM GNUNET_YES
+
+#define VERBOSE GNUNET_NO
+
+#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 45)
+
+struct PeerContext
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+ struct GNUNET_PeerIdentity id;
+#if START_ARM
+ struct GNUNET_OS_Process *arm_proc;
+#endif
+};
+
+static struct PeerContext p1;
+
+/**
+ * Return value for 'main'.
+ */
+static int global_ret;
+
+static struct GNUNET_VPN_Handle *vpn;
+
+static struct MHD_Daemon *mhd;
+
+static GNUNET_SCHEDULER_TaskIdentifier mhd_task_id;
+
+static GNUNET_SCHEDULER_TaskIdentifier curl_task_id;
+
+static GNUNET_SCHEDULER_TaskIdentifier ctrl_c_task_id;
+
+static struct GNUNET_VPN_RedirectionRequest *rr;
+
+static CURL *curl;
+
+static CURLM *multi;
+
+static char *url;
+
+/**
+ * IP address of the ultimate destination.
+ */
+static const char *dest_ip;
+
+/**
+ * Address family of the dest_ip.
+ */
+static int dest_af;
+
+/**
+ * Address family to use by the curl client.
+ */
+static int src_af;
+
+
+struct CBC
+{
+ char buf[1024];
+ size_t pos;
+};
+
+static struct CBC cbc;
+
+
+
+static size_t
+copy_buffer (void *ptr, size_t size, size_t nmemb, void *ctx)
+{
+ struct CBC *cbc = ctx;
+
+ if (cbc->pos + size * nmemb > sizeof(cbc->buf))
+ return 0; /* overflow */
+ memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb);
+ cbc->pos += size * nmemb;
+ return size * nmemb;
+}
+
+
+static int
+mhd_ahc (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data, size_t *upload_data_size,
+ void **unused)
+{
+ static int ptr;
+ struct MHD_Response *response;
+ int ret;
+
+ if (0 != strcmp ("GET", method))
+ return MHD_NO; /* unexpected method */
+ if (&ptr != *unused)
+ {
+ *unused = &ptr;
+ return MHD_YES;
+ }
+ *unused = NULL;
+#if VERBOSE
+ fprintf (stderr, "MHD sends respose for request to URL `%s'\n", url);
+#endif
+ response = MHD_create_response_from_buffer (strlen (url),
+ (void *) url,
+ MHD_RESPMEM_MUST_COPY);
+ ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
+ MHD_destroy_response (response);
+ if (ret == MHD_NO)
+ abort ();
+ return ret;
+}
+
+
+static void
+do_shutdown ()
+{
+ if (mhd_task_id != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (mhd_task_id);
+ mhd_task_id = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (curl_task_id != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (curl_task_id);
+ curl_task_id = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (ctrl_c_task_id != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (ctrl_c_task_id);
+ ctrl_c_task_id = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (NULL != mhd)
+ {
+ MHD_stop_daemon (mhd);
+ mhd = NULL;
+ }
+ if (NULL != rr)
+ {
+ GNUNET_VPN_cancel_request (rr);
+ rr = NULL;
+ }
+ if (NULL != vpn)
+ {
+ GNUNET_VPN_disconnect (vpn);
+ vpn = NULL;
+ }
+ GNUNET_free_non_null (url);
+ url = NULL;
+}
+
+
+/**
+ * Function to run the HTTP client.
+ */
+static void
+curl_main (void);
+
+
+static void
+curl_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ curl_task_id = GNUNET_SCHEDULER_NO_TASK;
+ curl_main ();
+}
+
+
+static void
+curl_main ()
+{
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
+ int max;
+ struct GNUNET_NETWORK_FDSet nrs;
+ struct GNUNET_NETWORK_FDSet nws;
+ struct GNUNET_TIME_Relative delay;
+ long timeout;
+ int running;
+ struct CURLMsg *msg;
+
+ max = 0;
+ FD_ZERO (&rs);
+ FD_ZERO (&ws);
+ FD_ZERO (&es);
+ curl_multi_perform (multi, &running);
+ if (running == 0)
+ {
+ GNUNET_assert (NULL != (msg = curl_multi_info_read (multi, &running)));
+ if (msg->msg == CURLMSG_DONE)
+ {
+ if (msg->data.result != CURLE_OK)
+ {
+ printf ("%s failed at %s:%d: `%s'\n",
+ "curl_multi_perform",
+ __FILE__,
+ __LINE__, curl_easy_strerror (msg->data.result));
+ global_ret = 1;
+ }
+ }
+ curl_multi_remove_handle (multi, curl);
+ curl_multi_cleanup (multi);
+ curl_easy_cleanup (curl);
+ curl = NULL;
+ multi = NULL;
+ if (cbc.pos != strlen ("/hello_world"))
+ global_ret = 2;
+ if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world")))
+ global_ret = 3;
+#if VERBOSE
+ fprintf (stderr, "Download complete, shutting down!\n");
+#endif
+ do_shutdown ();
+ return;
+ }
+ GNUNET_assert (CURLM_OK == curl_multi_fdset (multi, &rs, &ws, &es, &max));
+ if ( (CURLM_OK != curl_multi_timeout (multi, &timeout)) ||
+ (-1 == timeout) )
+ delay = GNUNET_TIME_UNIT_SECONDS;
+ else
+ delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, (unsigned int) timeout);
+ GNUNET_NETWORK_fdset_copy_native (&nrs,
+ &rs,
+ max + 1);
+ GNUNET_NETWORK_fdset_copy_native (&nws,
+ &ws,
+ max + 1);
+ curl_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ GNUNET_SCHEDULER_NO_TASK,
+ delay,
+ &nrs,
+ &nws,
+ &curl_task,
+ NULL);
+}
+
+
+/**
+ * Callback invoked from the VPN service once a redirection is
+ * available. Provides the IP address that can now be used to
+ * reach the requested destination (in our case, the MHD server)
+ *
+ * @param cls closure
+ * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
+ * will match 'result_af' from the request
+ * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
+ * that the VPN allocated for the redirection;
+ * traffic to this IP will now be redirected to the
+ * specified target peer; NULL on error
+ */
+static void
+allocation_cb (void *cls,
+ int af,
+ const void *address)
+{
+ char ips[INET6_ADDRSTRLEN];
+
+ rr = NULL;
+ if (src_af != af)
+ {
+ fprintf (stderr,
+ "VPN failed to allocate appropriate address\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ GNUNET_asprintf (&url,
+ "http://%s:%u/hello_world",
+ inet_ntop (af, address, ips, sizeof (ips)),
+ (unsigned int) PORT);
+ curl = curl_easy_init ();
+ curl_easy_setopt (curl, CURLOPT_URL, url);
+ curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, &copy_buffer);
+ curl_easy_setopt (curl, CURLOPT_WRITEDATA, &cbc);
+ curl_easy_setopt (curl, CURLOPT_FAILONERROR, 1);
+ curl_easy_setopt (curl, CURLOPT_TIMEOUT, 150L);
+ curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 15L);
+ curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1);
+
+ multi = curl_multi_init ();
+ GNUNET_assert (multi != NULL);
+ GNUNET_assert (CURLM_OK == curl_multi_add_handle (multi, curl));
+#if VERBOSE
+ fprintf (stderr, "Beginning HTTP download from `%s'\n", url);
+#endif
+ curl_main ();
+}
+
+
+/**
+ * Function to keep the HTTP server running.
+ */
+static void
+mhd_main (void);
+
+
+static void
+mhd_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ mhd_task_id = GNUNET_SCHEDULER_NO_TASK;
+ MHD_run (mhd);
+ mhd_main ();
+}
+
+
+static void
+ctrl_c_shutdown (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ ctrl_c_task_id = GNUNET_SCHEDULER_NO_TASK;
+ do_shutdown ();
+ global_ret = 1;
+}
+
+
+static void
+mhd_main ()
+{
+ struct GNUNET_NETWORK_FDSet nrs;
+ struct GNUNET_NETWORK_FDSet nws;
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
+ int max_fd;
+ unsigned MHD_LONG_LONG timeout;
+ struct GNUNET_TIME_Relative delay;
+
+ GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == mhd_task_id);
+ FD_ZERO (&rs);
+ FD_ZERO (&ws);
+ FD_ZERO (&es);
+ max_fd = -1;
+ GNUNET_assert (MHD_YES ==
+ MHD_get_fdset (mhd, &rs, &ws, &es, &max_fd));
+ if (MHD_YES == MHD_get_timeout (mhd, &timeout))
+ delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+ (unsigned int) timeout);
+ else
+ delay = GNUNET_TIME_UNIT_FOREVER_REL;
+ GNUNET_NETWORK_fdset_copy_native (&nrs,
+ &rs,
+ max_fd + 1);
+ GNUNET_NETWORK_fdset_copy_native (&nws,
+ &ws,
+ max_fd + 1);
+ mhd_task_id = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ GNUNET_SCHEDULER_NO_TASK,
+ delay,
+ &nrs,
+ &nws,
+ &mhd_task,
+ NULL);
+}
+
+
+static void
+run (void *cls, char *const *args, const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ struct in_addr v4;
+ struct in6_addr v6;
+ void *addr;
+ enum MHD_FLAG flags;
+
+ vpn = GNUNET_VPN_connect (cfg);
+ GNUNET_assert (NULL != vpn);
+ flags = MHD_USE_DEBUG;
+ if (AF_INET6 == dest_af)
+ flags |= MHD_USE_IPv6;
+ mhd = MHD_start_daemon (flags,
+ PORT,
+ NULL, NULL,
+ &mhd_ahc, NULL,
+ MHD_OPTION_END);
+ GNUNET_assert (NULL != mhd);
+ mhd_main ();
+ addr = NULL;
+ switch (dest_af)
+ {
+ case AF_INET:
+ GNUNET_assert (1 == inet_pton (dest_af, dest_ip, &v4));
+ addr = &v4;
+ break;
+ case AF_INET6:
+ GNUNET_assert (1 == inet_pton (dest_af, dest_ip, &v6));
+ addr = &v6;
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ rr = GNUNET_VPN_redirect_to_ip (vpn,
+ src_af,
+ dest_af,
+ addr,
+ GNUNET_YES,
+ GNUNET_TIME_UNIT_FOREVER_ABS,
+ &allocation_cb, NULL);
+ ctrl_c_task_id = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
+ &ctrl_c_shutdown,
+ NULL);
+}
+
+
+static void
+setup_peer (struct PeerContext *p, const char *cfgname)
+{
+ p->cfg = GNUNET_CONFIGURATION_create ();
+#if START_ARM
+ p->arm_proc =
+ GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-service-arm",
+ "gnunet-service-arm",
+#if VERBOSE
+ "-L", "DEBUG",
+#endif
+ "-c", cfgname, NULL);
+#endif
+ GNUNET_assert (NULL != p->arm_proc);
+ GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
+}
+
+
+static void
+stop_peer (struct PeerContext *p)
+{
+#if START_ARM
+ if (NULL != p->arm_proc)
+ {
+ if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM))
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
+ if (GNUNET_OS_process_wait (p->arm_proc) != GNUNET_OK)
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM process %u stopped\n",
+ GNUNET_OS_process_get_pid (p->arm_proc));
+ GNUNET_OS_process_close (p->arm_proc);
+ p->arm_proc = NULL;
+ }
+#endif
+ GNUNET_CONFIGURATION_destroy (p->cfg);
+}
+
+
+/**
+ * Test if the given AF is supported by this system.
+ *
+ * @param af to test
+ * @return GNUNET_OK if the AF is supported
+ */
+static int
+test_af (int af)
+{
+ int s;
+
+ s = socket (af, SOCK_STREAM, 0);
+ if (-1 == s)
+ {
+ if (EAFNOSUPPORT == errno)
+ return GNUNET_NO;
+ fprintf (stderr, "Failed to create test socket: %s\n", STRERROR (errno));
+ return GNUNET_SYSERR;
+ }
+ close (s);
+ return GNUNET_OK;
+}
+
+
+
+int
+main (int argc, char *const *argv)
+{
+ const char *type;
+ const char *bin;
+ char *const argvx[] = {
+ "test_gnunet_vpn",
+ "-c",
+ "test_gnunet_vpn.conf",
+#if VERBOSE
+ "-L", "DEBUG",
+#endif
+ NULL
+ };
+ struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ if (0 != ACCESS ("/dev/net/tun", R_OK))
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+ "access",
+ "/dev/net/tun");
+ fprintf (stderr,
+ "WARNING: System unable to run test, skipping.\n");
+ return 0;
+ }
+ if ( (GNUNET_YES !=
+ GNUNET_OS_check_helper_binary ("gnunet-helper-vpn")) ||
+ (GNUNET_YES !=
+ GNUNET_OS_check_helper_binary ("gnunet-helper-exit")) )
+ {
+ fprintf (stderr,
+ "WARNING: gnunet-helper-{exit,vpn} binaries in $PATH are not SUID, refusing to run test (as it would have to fail).\n");
+ fprintf (stderr,
+ "Change $PATH ('.' in $PATH before $GNUNET_PREFIX/bin is problematic) or permissions (run 'make install' as root) to fix this!\n");
+ return 0;
+ }
+ bin = argv[0];
+ if (NULL != strstr (bin, "lt-"))
+ bin = strstr (bin, "lt-") + 4;
+ type = strstr (bin, "-");
+ if (NULL == type)
+ {
+ fprintf (stderr, "invalid binary name\n");
+ return 1;
+ }
+ type++;
+ if (0 == strcmp (type, "4_to_6"))
+ {
+ dest_ip = "FC5A:04E1:C2BA::1";
+ dest_af = AF_INET6;
+ src_af = AF_INET;
+ }
+ else if (0 == strcmp (type, "6_to_4"))
+ {
+ dest_ip = "169.254.86.1";
+ dest_af = AF_INET;
+ src_af = AF_INET6;
+ }
+ else if (0 == strcmp (type, "4_over"))
+ {
+ dest_ip = "169.254.86.1";
+ dest_af = AF_INET;
+ src_af = AF_INET;
+ }
+ else if (0 == strcmp (type, "6_over"))
+ {
+ dest_ip = "FC5A:04E1:C2BA::1";
+ dest_af = AF_INET6;
+ src_af = AF_INET6;
+ }
+ else
+ {
+ fprintf (stderr, "invalid binary suffix `%s'\n", type);
+ return 1;
+ }
+ if ( (GNUNET_OK != test_af (src_af)) ||
+ (GNUNET_OK != test_af (dest_af)) )
+ {
+ fprintf (stderr,
+ "Required address families not supported by this system, skipping test.\n");
+ return 0;
+ }
+
+
+ if (0 != curl_global_init (CURL_GLOBAL_WIN32))
+ return 2;
+ setup_peer (&p1, "test_gnunet_vpn.conf");
+ GNUNET_log_setup ("test_gnunet_vpn",
+#if VERBOSE
+ "DEBUG",
+#else
+ "WARNING",
+#endif
+ NULL);
+ GNUNET_PROGRAM_run ((sizeof (argvx) / sizeof (char *)) - 1, argvx,
+ "test_gnunet_vpn", "nohelp", options, &run, NULL);
+ stop_peer (&p1);
+ GNUNET_DISK_directory_remove ("/tmp/gnunet-test-vpn");
+ return global_ret;
+}
+
+/* end of test_gnunet_vpn.c */
+
diff --git a/src/vpn/test_gnunet_vpn.conf b/src/vpn/test_gnunet_vpn.conf
new file mode 100644
index 0000000..5aec0c5
--- /dev/null
+++ b/src/vpn/test_gnunet_vpn.conf
@@ -0,0 +1,37 @@
+[PATHS]
+SERVICEHOME = /tmp/gnunet-test-vpn/
+DEFAULTCONFIG = test_gnunet_vpn.conf
+
+
+[arm]
+DEFAULTSERVICES = statistics exit vpn
+PORT = 0
+ALLOW_SHUTDOWN = YES
+
+[exit]
+EXIT_IPV4 = YES
+EXIT_IPV6 = YES
+
+# FIXME: can we use 'lo'?
+EXIT_IFNAME = eth1
+
+[testing]
+WEAKRANDOM = YES
+HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat
+
+
+
+# repeating some values from the default configurations
+# here as the respective network addresses are also
+# hard-wired in the tests and the MUST match (!)
+[vpn]
+IPV6ADDR = FC2D:FDAA:6A26::1
+IPV6PREFIX = 64
+IPV4ADDR = 169.254.20.1
+IPV4MASK = 255.255.255.0
+
+[exit]
+IPV6ADDR = FC5A:04E1:C2BA::1
+IPV6PREFIX = 96
+IPV4ADDR = 169.254.86.1
+IPV4MASK = 255.255.255.0
diff --git a/src/vpn/vpn.conf.in b/src/vpn/vpn.conf.in
new file mode 100644
index 0000000..f5eb224
--- /dev/null
+++ b/src/vpn/vpn.conf.in
@@ -0,0 +1,21 @@
+[vpn]
+AUTOSTART = YES
+@UNIXONLY@ PORT = 0
+HOSTNAME = localhost
+HOME = $SERVICEHOME
+CONFIG = $DEFAULTCONFIG
+BINARY = gnunet-service-vpn
+ACCEPT_FROM = 127.0.0.1;
+ACCEPT_FROM6 = ::1;
+UNIXPATH = /tmp/gnunet-service-vpn.sock
+UNIX_MATCH_UID = YES
+UNIX_MATCH_GID = YES
+
+IPV6ADDR = 1234::1
+IPV6PREFIX = 32
+IPV4ADDR = 10.11.10.1
+IPV4MASK = 255.255.0.0
+VIRTDNS = 10.11.10.2
+VIRTDNS6 = 1234::17
+IFNAME = vpn-gnunet
+
diff --git a/src/vpn/vpn.h b/src/vpn/vpn.h
new file mode 100644
index 0000000..e937f5e
--- /dev/null
+++ b/src/vpn/vpn.h
@@ -0,0 +1,159 @@
+/*
+ This file is part of GNUnet.
+ (C) 2012 Christian Grothoff
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet 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 GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file vpn/vpn.h
+ * @brief IPC messages between VPN library and VPN service
+ * @author Christian Grothoff
+ */
+#ifndef VPN_H
+#define VPN_H
+
+#include "gnunet_util_lib.h"
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Message send by the VPN client to the VPN service requesting
+ * the setup of a redirection from some IP via an exit node to
+ * some global Internet address.
+ */
+struct RedirectToIpRequestMessage
+{
+ /**
+ * Type is GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_IP
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * GNUNET_YES to notify only after completion of the mesh-level connection,
+ * GNUNET_NO to notify as soon as an address was allocated (in nbo).
+ */
+ int32_t nac GNUNET_PACKED;
+
+ /**
+ * How long should the redirection be maintained at most?
+ */
+ struct GNUNET_TIME_AbsoluteNBO expiration_time;
+
+ /**
+ * Address family desired for the result (AF_INET or AF_INET6 or AF_UNSPEC, in nbo)
+ */
+ int32_t result_af GNUNET_PACKED;
+
+ /**
+ * Address family used for the destination address (AF_INET or AF_INET6, in nbo)
+ */
+ int32_t addr_af GNUNET_PACKED;
+
+ /**
+ * Unique ID to match a future response to this request.
+ * Picked by the client.
+ */
+ uint64_t request_id GNUNET_PACKED;
+
+ /* followed by destination address ('struct in_addr' or 'struct in6_addr') */
+
+};
+
+
+/**
+ * Message send by the VPN client to the VPN service requesting
+ * the setup of a redirection from some IP to a service running
+ * at a particular peer.
+ */
+struct RedirectToServiceRequestMessage
+{
+ /**
+ * Type is GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_IP
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * GNUNET_YES to notify only after completion of the mesh-level connection,
+ * GNUNET_NO to notify as soon as an address was allocated (in nbo).
+ */
+ int32_t nac GNUNET_PACKED;
+
+ /**
+ * How long should the redirection be maintained at most?
+ */
+ struct GNUNET_TIME_AbsoluteNBO expiration_time;
+
+ /**
+ * Desired protocol (IPPROTO_UDP or IPPROTO_TCP)
+ */
+ int32_t protocol GNUNET_PACKED;
+
+ /**
+ * Address family desired for the result (AF_INET or AF_INET6 or AF_UNSPEC, in nbo)
+ */
+ int32_t result_af GNUNET_PACKED;
+
+ /**
+ * Target peer offering the service.
+ */
+ struct GNUNET_PeerIdentity target;
+
+ /**
+ * Service descriptor identifying the service.
+ */
+ GNUNET_HashCode service_descriptor GNUNET_PACKED;
+
+ /**
+ * Unique ID to match a future response to this request.
+ * Picked by the client.
+ */
+ uint64_t request_id GNUNET_PACKED;
+
+};
+
+
+/**
+ * Response from the VPN service to a VPN client informing about
+ * the IP that was assigned for the requested redirection.
+ */
+struct RedirectToIpResponseMessage
+{
+
+ /**
+ * Type is GNUNET_MESSAGE_TYPE_VPN_CLIENT_USE_IP
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * Address family of the allocated address that follows; will match
+ * "result_af" from the request, of be "AF_UNSPEC" on errors.
+ */
+ int32_t result_af GNUNET_PACKED;
+
+ /**
+ * Unique ID to match the response to a request.
+ */
+ uint64_t request_id GNUNET_PACKED;
+
+ /* followed by destination address ('struct in_addr' or 'struct in6_addr') */
+
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+
+#endif
diff --git a/src/vpn/vpn_api.c b/src/vpn/vpn_api.c
new file mode 100644
index 0000000..31d17f8
--- /dev/null
+++ b/src/vpn/vpn_api.c
@@ -0,0 +1,605 @@
+/*
+ This file is part of GNUnet.
+ (C) 2012 Christian Grothoff
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet 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 GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file vpn/vpn_api.c
+ * @brief library to access the VPN service and tell it how to redirect traffic
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_vpn_service.h"
+#include "vpn.h"
+
+
+/**
+ * Opaque VPN handle
+ */
+struct GNUNET_VPN_Handle
+{
+ /**
+ * Configuration we use.
+ */
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ /**
+ * Connection to VPN service.
+ */
+ struct GNUNET_CLIENT_Connection *client;
+
+ /**
+ * Active transmission request.
+ */
+ struct GNUNET_CLIENT_TransmitHandle *th;
+
+ /**
+ * Head of list of active redirection requests.
+ */
+ struct GNUNET_VPN_RedirectionRequest *rr_head;
+
+ /**
+ * Tail of list of active redirection requests.
+ */
+ struct GNUNET_VPN_RedirectionRequest *rr_tail;
+
+ /**
+ * Identifier of a reconnect task.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier rt;
+
+ /**
+ * How long do we wait until we try to reconnect?
+ */
+ struct GNUNET_TIME_Relative backoff;
+
+ /**
+ * ID of the last request that was submitted to the service.
+ */
+ uint64_t request_id_gen;
+
+};
+
+
+/**
+ * Opaque redirection request handle.
+ */
+struct GNUNET_VPN_RedirectionRequest
+{
+ /**
+ * Element in DLL.
+ */
+ struct GNUNET_VPN_RedirectionRequest *next;
+
+ /**
+ * Element in DLL.
+ */
+ struct GNUNET_VPN_RedirectionRequest *prev;
+
+ /**
+ * Pointer to the VPN struct.
+ */
+ struct GNUNET_VPN_Handle *vh;
+
+ /**
+ * Target IP address for the redirection, or NULL for
+ * redirection to service. Allocated after this struct.
+ */
+ const void *addr;
+
+ /**
+ * Function to call with the designated IP address.
+ */
+ GNUNET_VPN_AllocationCallback cb;
+
+ /**
+ * Closure for 'cb'.
+ */
+ void *cb_cls;
+
+ /**
+ * For service redirection, identity of the peer offering the service.
+ */
+ struct GNUNET_PeerIdentity peer;
+
+ /**
+ * For service redirection, service descriptor.
+ */
+ GNUNET_HashCode serv;
+
+ /**
+ * At what time should the created service mapping expire?
+ */
+ struct GNUNET_TIME_Absolute expiration_time;
+
+ /**
+ * non-zero if this request has been sent to the service.
+ */
+ uint64_t request_id;
+
+ /**
+ * Desired address family for the result.
+ */
+ int result_af;
+
+ /**
+ * Address family of 'addr'. AF_INET or AF_INET6.
+ */
+ int addr_af;
+
+ /**
+ * GNUNET_YES if we are to call the callback only after successful
+ * mesh tunnel creation.
+ */
+ int nac;
+
+ /**
+ * For service redirection, IPPROT_UDP or IPPROTO_TCP.
+ */
+ uint8_t protocol;
+
+};
+
+
+/**
+ * Disconnect from the service (communication error) and reconnect later.
+ *
+ * @param vh handle to reconnect.
+ */
+static void
+reconnect (struct GNUNET_VPN_Handle *vh);
+
+
+/**
+ * Function called when we receive a message from the VPN service.
+ *
+ * @param cls the 'struct GNUNET_VPN_Handle'
+ * @param msg message received, NULL on timeout or fatal error
+ */
+static void
+receive_response (void *cls,
+ const struct GNUNET_MessageHeader* msg)
+{
+ struct GNUNET_VPN_Handle *vh = cls;
+ const struct RedirectToIpResponseMessage *rm;
+ struct GNUNET_VPN_RedirectionRequest *rr;
+ size_t msize;
+ size_t alen;
+ int af;
+
+ if (NULL == msg)
+ {
+ reconnect (vh);
+ return;
+ }
+ if ( (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_VPN_CLIENT_USE_IP) ||
+ (sizeof (struct RedirectToIpResponseMessage) > (msize = ntohs (msg->size))) )
+ {
+ GNUNET_break (0);
+ reconnect (vh);
+ return;
+ }
+ rm = (const struct RedirectToIpResponseMessage *) msg;
+ af = (int) ntohl (rm->result_af);
+ switch (af)
+ {
+ case AF_UNSPEC:
+ alen = 0;
+ break;
+ case AF_INET:
+ alen = sizeof (struct in_addr);
+ break;
+ case AF_INET6:
+ alen = sizeof (struct in6_addr);
+ break;
+ default:
+ GNUNET_break (0);
+ reconnect (vh);
+ return;
+ }
+ if ( (msize != alen + sizeof (struct RedirectToIpResponseMessage)) ||
+ (0 == rm->request_id) )
+ {
+ GNUNET_break (0);
+ reconnect (vh);
+ return;
+ }
+ GNUNET_CLIENT_receive (vh->client,
+ &receive_response, vh,
+ GNUNET_TIME_UNIT_FOREVER_REL);
+ for (rr = vh->rr_head; NULL != rr; rr = rr->next)
+ {
+ if (rr->request_id == rm->request_id)
+ {
+ GNUNET_CONTAINER_DLL_remove (vh->rr_head,
+ vh->rr_tail,
+ rr);
+ rr->cb (rr->cb_cls,
+ af,
+ (af == AF_UNSPEC) ? NULL : &rm[1]);
+ GNUNET_free (rr);
+ break;
+ }
+ }
+}
+
+
+/**
+ * We're ready to transmit a request to the VPN service. Do it.
+ *
+ * @param cls the 'struct GNUNET_VPN_Handle*'
+ * @param size number of bytes available in buf
+ * @param buf where to copy the request
+ * @return number of bytes copied to 'buf'
+ */
+static size_t
+transmit_request (void *cls,
+ size_t size,
+ void *buf)
+{
+ struct GNUNET_VPN_Handle *vh = cls;
+ struct GNUNET_VPN_RedirectionRequest *rr;
+ struct RedirectToIpRequestMessage rip;
+ struct RedirectToServiceRequestMessage rs;
+ char *cbuf;
+ size_t alen;
+ size_t ret;
+
+ vh->th = NULL;
+ /* find a pending request */
+ rr = vh->rr_head;
+ while ( (NULL != rr) &&
+ (0 != rr->request_id) )
+ rr = rr->next;
+ if (NULL == rr)
+ return 0;
+ if (0 == size)
+ {
+ reconnect (vh);
+ return 0;
+ }
+
+ /* if first request, start receive loop */
+ if (0 == vh->request_id_gen)
+ GNUNET_CLIENT_receive (vh->client,
+ &receive_response, vh,
+ GNUNET_TIME_UNIT_FOREVER_REL);
+ if (NULL == rr->addr)
+ {
+ ret = sizeof (struct RedirectToServiceRequestMessage);
+ GNUNET_assert (ret <= size);
+ rs.header.size = htons ((uint16_t) ret);
+ rs.header.type = htons (GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_SERVICE);
+ rs.nac = htonl (rr->nac);
+ rs.expiration_time = GNUNET_TIME_absolute_hton (rr->expiration_time);
+ rs.protocol = htonl (rr->protocol);
+ rs.result_af = htonl (rr->result_af);
+ rs.target = rr->peer;
+ rs.service_descriptor = rr->serv;
+ rs.request_id = rr->request_id = ++vh->request_id_gen;
+ memcpy (buf, &rs, sizeof (struct RedirectToServiceRequestMessage));
+ }
+ else
+ {
+ switch (rr->addr_af)
+ {
+ case AF_INET:
+ alen = sizeof (struct in_addr);
+ break;
+ case AF_INET6:
+ alen = sizeof (struct in6_addr);
+ break;
+ default:
+ GNUNET_assert (0);
+ return 0;
+ }
+ ret = alen + sizeof (struct RedirectToIpRequestMessage);
+ GNUNET_assert (ret <= size);
+ rip.header.size = htons ((uint16_t) ret);
+ rip.header.type = htons (GNUNET_MESSAGE_TYPE_VPN_CLIENT_REDIRECT_TO_IP);
+ rip.nac = htonl (rr->nac);
+ rip.expiration_time = GNUNET_TIME_absolute_hton (rr->expiration_time);
+ rip.result_af = htonl (rr->result_af);
+ rip.addr_af = htonl (rr->addr_af);
+ rip.request_id = rr->request_id = ++vh->request_id_gen;
+ cbuf = buf;
+ memcpy (cbuf, &rip, sizeof (struct RedirectToIpRequestMessage));
+ memcpy (&cbuf[sizeof (struct RedirectToIpRequestMessage)], rr->addr, alen);
+ }
+ /* test if there are more pending requests */
+ while ( (NULL != rr) &&
+ (0 != rr->request_id) )
+ rr = rr->next;
+ if (NULL != rr)
+ vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client,
+ sizeof (struct RedirectToServiceRequestMessage),
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ GNUNET_NO,
+ &transmit_request,
+ vh);
+ return ret;
+}
+
+
+/**
+ * Add a request to our request queue and transmit it.
+ *
+ * @param rr request to queue and transmit.
+ */
+static void
+queue_request (struct GNUNET_VPN_RedirectionRequest *rr)
+{
+ struct GNUNET_VPN_Handle *vh;
+
+ vh = rr->vh;
+ GNUNET_CONTAINER_DLL_insert_tail (vh->rr_head,
+ vh->rr_tail,
+ rr);
+ if ( (NULL == vh->th) &&
+ (NULL != vh->client) )
+ vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client,
+ sizeof (struct RedirectToServiceRequestMessage),
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ GNUNET_NO,
+ &transmit_request,
+ vh);
+}
+
+
+/**
+ * Connect to the VPN service and start again to transmit our requests.
+ *
+ * @param cls the 'struct GNUNET_VPN_Handle *'
+ * @param tc scheduler context
+ */
+static void
+connect_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_VPN_Handle *vh = cls;
+
+ vh->rt = GNUNET_SCHEDULER_NO_TASK;
+ vh->client = GNUNET_CLIENT_connect ("vpn", vh->cfg);
+ GNUNET_assert (NULL != vh->client);
+ GNUNET_assert (NULL == vh->th);
+ if (NULL != vh->rr_head)
+ vh->th = GNUNET_CLIENT_notify_transmit_ready (vh->client,
+ sizeof (struct RedirectToServiceRequestMessage),
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ GNUNET_NO,
+ &transmit_request,
+ vh);
+}
+
+
+/**
+ * Disconnect from the service (communication error) and reconnect later.
+ *
+ * @param vh handle to reconnect.
+ */
+static void
+reconnect (struct GNUNET_VPN_Handle *vh)
+{
+ struct GNUNET_VPN_RedirectionRequest *rr;
+
+ if (NULL != vh->th)
+ {
+ GNUNET_CLIENT_notify_transmit_ready_cancel (vh->th);
+ vh->th = NULL;
+ }
+ GNUNET_CLIENT_disconnect (vh->client, GNUNET_NO);
+ vh->client = NULL;
+ vh->request_id_gen = 0;
+ for (rr = vh->rr_head; NULL != rr; rr = rr->next)
+ rr->request_id = 0;
+ vh->backoff = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MILLISECONDS,
+ GNUNET_TIME_relative_min (GNUNET_TIME_relative_multiply (vh->backoff, 2),
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)));
+ vh->rt = GNUNET_SCHEDULER_add_delayed (vh->backoff,
+ &connect_task,
+ vh);
+}
+
+
+/**
+ * Cancel redirection request with the service.
+ *
+ * @param rr request to cancel
+ */
+void
+GNUNET_VPN_cancel_request (struct GNUNET_VPN_RedirectionRequest *rr)
+{
+ struct GNUNET_VPN_Handle *vh;
+
+ vh = rr->vh;
+ GNUNET_CONTAINER_DLL_remove (vh->rr_head,
+ vh->rr_tail,
+ rr);
+ GNUNET_free (rr);
+}
+
+
+/**
+ * Tell the VPN that a forwarding to a particular peer offering a
+ * particular service is requested. The VPN is to reserve a
+ * particular IP for the redirection and return it. The VPN will
+ * begin the redirection as soon as possible and maintain it as long
+ * as it is actively used and keeping it is feasible. Given resource
+ * limitations, the longest inactive mappings will be destroyed.
+ *
+ * @param vh VPN handle
+ * @param result_af desired address family for the returned allocation
+ * can also be AF_UNSPEC
+ * @param protocol protocol, IPPROTO_UDP or IPPROTO_TCP
+ * @param peer target peer for the redirection
+ * @param serv service descriptor to give to the peer
+ * @param nac GNUNET_YES to notify via callback only after completion of
+ * the MESH-level connection,
+ * GNUNET_NO to notify as soon as the IP has been reserved
+ * @param expiration_time at what time should the redirection expire?
+ * (this should not impact connections that are active at that time)
+ * @param cb function to call with the IP
+ * @param cb_cls closure for cb
+ * @return handle to cancel the request (means the callback won't be
+ * invoked anymore; the mapping may or may not be established
+ * anyway)
+ */
+struct GNUNET_VPN_RedirectionRequest *
+GNUNET_VPN_redirect_to_peer (struct GNUNET_VPN_Handle *vh,
+ int result_af,
+ uint8_t protocol,
+ const struct GNUNET_PeerIdentity *peer,
+ const GNUNET_HashCode *serv,
+ int nac,
+ struct GNUNET_TIME_Absolute expiration_time,
+ GNUNET_VPN_AllocationCallback cb,
+ void *cb_cls)
+{
+ struct GNUNET_VPN_RedirectionRequest *rr;
+
+ rr = GNUNET_malloc (sizeof (struct GNUNET_VPN_RedirectionRequest));
+ rr->vh = vh;
+ rr->cb = cb;
+ rr->cb_cls = cb_cls;
+ rr->peer = *peer;
+ rr->serv = *serv;
+ rr->expiration_time = expiration_time;
+ rr->result_af = result_af;
+ rr->nac = nac;
+ rr->protocol = protocol;
+ queue_request (rr);
+ return rr;
+}
+
+
+/**
+ * Tell the VPN that forwarding to the Internet via some exit node is
+ * requested. Note that both UDP and TCP traffic will be forwarded,
+ * but possibly to different exit nodes. The VPN is to reserve a
+ * particular IP for the redirection and return it. The VPN will
+ * begin the redirection as soon as possible and maintain it as long
+ * as it is actively used and keeping it is feasible. Given resource
+ * limitations, the longest inactive mappings will be destroyed.
+ *
+ * @param vh VPN handle
+ * @param result_af desired address family for the returned allocation
+ * @param addr_af address family for 'addr', AF_INET or AF_INET6
+ * @param addr destination IP address on the Internet; destination
+ * port is to be taken from the VPN packet itself
+ * @param nac GNUNET_YES to notify via callback only after completion of
+ * the MESH-level connection,
+ * GNUNET_NO to notify as soon as the IP has been reserved
+ * @param expiration_time at what time should the redirection expire?
+ * (this should not impact connections that are active at that time)
+ * @param cb function to call with the IP
+ * @param cb_cls closure for cb
+ * @return handle to cancel the request (means the callback won't be
+ * invoked anymore; the mapping may or may not be established
+ * anyway)
+ */
+struct GNUNET_VPN_RedirectionRequest *
+GNUNET_VPN_redirect_to_ip (struct GNUNET_VPN_Handle *vh,
+ int result_af,
+ int addr_af,
+ const void *addr,
+ int nac,
+ struct GNUNET_TIME_Absolute expiration_time,
+ GNUNET_VPN_AllocationCallback cb,
+ void *cb_cls)
+{
+ struct GNUNET_VPN_RedirectionRequest *rr;
+ size_t alen;
+
+ switch (addr_af)
+ {
+ case AF_INET:
+ alen = sizeof (struct in_addr);
+ break;
+ case AF_INET6:
+ alen = sizeof (struct in6_addr);
+ break;
+ default:
+ GNUNET_break (0);
+ return NULL;
+ }
+ rr = GNUNET_malloc (sizeof (struct GNUNET_VPN_RedirectionRequest) + alen);
+ rr->vh = vh;
+ rr->addr = &rr[1];
+ rr->cb = cb;
+ rr->cb_cls = cb_cls;
+ rr->expiration_time = expiration_time;
+ rr->result_af = result_af;
+ rr->addr_af = addr_af;
+ rr->nac = nac;
+ memcpy (&rr[1], addr, alen);
+ queue_request (rr);
+ return rr;
+}
+
+
+/**
+ * Connect to the VPN service
+ *
+ * @param cfg configuration to use
+ * @return VPN handle
+ */
+struct GNUNET_VPN_Handle *
+GNUNET_VPN_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ struct GNUNET_VPN_Handle *vh;
+
+ vh = GNUNET_malloc (sizeof (struct GNUNET_VPN_Handle));
+ vh->cfg = cfg;
+ vh->client = GNUNET_CLIENT_connect ("vpn", cfg);
+ if (NULL == vh->client)
+ {
+ GNUNET_free (vh);
+ return NULL;
+ }
+ return vh;
+}
+
+
+/**
+ * Disconnect from the VPN service.
+ *
+ * @param vh VPN handle
+ */
+void
+GNUNET_VPN_disconnect (struct GNUNET_VPN_Handle *vh)
+{
+ GNUNET_assert (NULL == vh->rr_head);
+ if (NULL != vh->th)
+ {
+ GNUNET_CLIENT_notify_transmit_ready_cancel (vh->th);
+ vh->th = NULL;
+ }
+ if (NULL != vh->client)
+ {
+ GNUNET_CLIENT_disconnect (vh->client, GNUNET_NO);
+ vh->client = NULL;
+ }
+ if (GNUNET_SCHEDULER_NO_TASK != vh->rt)
+ {
+ GNUNET_SCHEDULER_cancel (vh->rt);
+ vh->rt = GNUNET_SCHEDULER_NO_TASK;
+ }
+ GNUNET_free (vh);
+}
+
+/* end of vpn_api.c */