diff options
author | Bertrand Marc <beberking@gmail.com> | 2012-05-02 21:43:37 +0200 |
---|---|---|
committer | Bertrand Marc <beberking@gmail.com> | 2012-05-02 21:43:37 +0200 |
commit | 2b81464a43485fcc8ce079fafdee7b7a171835f4 (patch) | |
tree | 394774c0f735199b57d51a2d3840356317853fe1 /src/vpn |
Imported Upstream version 0.9.2upstream/0.9.2
Diffstat (limited to 'src/vpn')
-rw-r--r-- | src/vpn/Makefile.am | 110 | ||||
-rw-r--r-- | src/vpn/Makefile.in | 1018 | ||||
-rw-r--r-- | src/vpn/gnunet-helper-vpn.c | 616 | ||||
-rw-r--r-- | src/vpn/gnunet-service-vpn.c | 3202 | ||||
-rw-r--r-- | src/vpn/gnunet-vpn.c | 334 | ||||
-rw-r--r-- | src/vpn/test_gnunet_vpn.c | 605 | ||||
-rw-r--r-- | src/vpn/test_gnunet_vpn.conf | 37 | ||||
-rw-r--r-- | src/vpn/vpn.conf.in | 21 | ||||
-rw-r--r-- | src/vpn/vpn.h | 159 | ||||
-rw-r--r-- | src/vpn/vpn_api.c | 605 |
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, ©_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 */ |