diff options
author | David Barksdale <amatus.amongus@gmail.com> | 2013-07-22 08:26:16 -0500 |
---|---|---|
committer | David Barksdale <amatus.amongus@gmail.com> | 2013-08-01 08:12:45 -0500 |
commit | 000ad0090a0b4eb9889f4c45d35d4d7af2dbbe27 (patch) | |
tree | dda00bef19b649c154ba6a2b37f0f73fa1697c30 /src/mesh | |
parent | 0f8d8bc0c5c09246d647d233767cf3437c6e8bcd (diff) |
Imported Upstream version 0.9.5a
Diffstat (limited to 'src/mesh')
27 files changed, 17909 insertions, 2458 deletions
diff --git a/src/mesh/Makefile.am b/src/mesh/Makefile.am index ac38b0d..7d9a4da 100644 --- a/src/mesh/Makefile.am +++ b/src/mesh/Makefile.am @@ -11,53 +11,141 @@ endif pkgcfgdir= $(pkgdatadir)/config.d/ +libexecdir= $(pkglibdir)/libexec/ + pkgcfg_DATA = \ mesh.conf +plugindir = $(libdir)/gnunet + AM_CLFAGS = -g +libexec_PROGRAMS = \ + gnunet-service-mesh gnunet-service-mesh-new + bin_PROGRAMS = \ - gnunet-service-mesh + gnunet-mesh lib_LTLIBRARIES = \ libgnunetmesh.la +plugin_LTLIBRARIES = \ + libgnunet_plugin_block_mesh.la + +libgnunet_plugin_block_mesh_la_SOURCES = \ + plugin_block_mesh.c +libgnunet_plugin_block_mesh_la_LIBADD = \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/util/libgnunetutil.la +libgnunet_plugin_block_mesh_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) +libgnunet_plugin_block_mesh_la_DEPENDENCIES = \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/util/libgnunetutil.la + +libgnunetmesh_la_SOURCES = \ + mesh_api.c mesh_common.c +libgnunetmesh_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(XLIB) \ + $(LTLIBINTL) +libgnunetmesh_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 2:1:1 + gnunet_service_mesh_SOURCES = \ gnunet-service-mesh.c \ - mesh_tunnel_tree.c mesh_tunnel_tree.h + mesh_tunnel_tree.c mesh_tunnel_tree.h \ + mesh_common.c +gnunet_service_mesh_CFLAGS = $(AM_CFLAGS) gnunet_service_mesh_LDADD = \ - $(top_builddir)/src/core/libgnunetcore.la\ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/core/libgnunetcore.la \ $(top_builddir)/src/dht/libgnunetdht.la \ - $(top_builddir)/src/util/libgnunetutil.la - gnunet_service_mesh_DEPENDENCIES = \ - $(top_builddir)/src/core/libgnunetcore.la\ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/regex/libgnunetregex.la +gnunet_service_mesh_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/core/libgnunetcore.la \ $(top_builddir)/src/dht/libgnunetdht.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/regex/libgnunetregex.la +if LINUX +gnunet_service_mesh_LDFLAGS = -lrt +endif + +gnunet_mesh_SOURCES = \ + gnunet-mesh.c +gnunet_mesh_LDADD = \ + $(top_builddir)/src/mesh/libgnunetmesh.la \ $(top_builddir)/src/util/libgnunetutil.la +gnunet_mesh_DEPENDENCIES = \ + libgnunetmesh.la -libgnunetmesh_la_SOURCES = \ - mesh_api.c mesh.h mesh_protocol.h -libgnunetmesh_la_LIBADD = \ +gnunet_service_mesh_new_SOURCES = \ + gnunet-service-mesh-new.c \ + mesh_tunnel_tree.c \ + mesh_common.c +gnunet_service_mesh_new_CFLAGS = $(AM_CFLAGS) +gnunet_service_mesh_new_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ - $(XLIB) -libgnunetmesh_la_LDFLAGS = \ - $(GN_LIB_LDFLAGS) $(WINFLAGS) \ - -version-info 1:0:0 + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/dht/libgnunetdht.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/regex/libgnunetregex.la +gnunet_service_mesh_new_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/dht/libgnunetdht.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/regex/libgnunetregex.la +if LINUX +gnunet_service_mesh_new_LDFLAGS = -lrt +endif + + +noinst_LIBRARIES = libgnunetmeshtest.a + +libgnunetmeshtest_a_SOURCES = \ + mesh_test_lib.c mesh_test_lib.h +libgnunetmeshtest_a_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/mesh/libgnunetmesh.la +libgnunetmeshtest_a_DEPENDENCIES = \ + libgnunetmesh.la + check_PROGRAMS = \ test_mesh_api \ test_mesh_tree_api \ test_mesh_local_1 \ test_mesh_local_2 \ + test_mesh_local_traffic_fwd \ + test_mesh_local_traffic_bck \ + test_mesh_local_traffic_both \ test_mesh_2dtorus \ + test_mesh_regex \ test_mesh_small_unicast \ test_mesh_small_multicast \ + test_mesh_small_signal \ test_mesh_small_speed \ + test_mesh_small_speed_nobuf \ + test_mesh_small_speed_min \ + test_mesh_small_speed_backwards \ + test_mesh_small_speed_nobuf_backwards \ + test_mesh_small_speed_min_backwards \ test_mesh_small_speed_ack test_mesh_api_SOURCES = \ test_mesh_api.c test_mesh_api_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ $(top_builddir)/src/mesh/libgnunetmesh.la test_mesh_api_DEPENDENCIES = \ libgnunetmesh.la \ @@ -67,7 +155,7 @@ test_mesh_tree_api_SOURCES = \ test_mesh_tree_api.c test_mesh_tree_api_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/dht/libgnunetdht.la + $(top_builddir)/src/dht/libgnunetdht.la test_mesh_tree_api_DEPENDENCIES = \ libgnunetmesh.la \ $(top_builddir)/src/dht/libgnunetdht.la @@ -76,6 +164,7 @@ test_mesh_local_1_SOURCES = \ test_mesh_local_1.c test_mesh_local_1_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ $(top_builddir)/src/mesh/libgnunetmesh.la test_mesh_local_1_DEPENDENCIES = \ libgnunetmesh.la @@ -84,59 +173,129 @@ test_mesh_local_2_SOURCES = \ test_mesh_local_2.c test_mesh_local_2_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ $(top_builddir)/src/mesh/libgnunetmesh.la test_mesh_local_2_DEPENDENCIES = \ libgnunetmesh.la +test_mesh_local_traffic_fwd_SOURCES = \ + test_mesh_local_traffic.c +test_mesh_local_traffic_fwd_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/mesh/libgnunetmesh.la +test_mesh_local_traffic_fwd_DEPENDENCIES = \ + libgnunetmesh.la + +test_mesh_local_traffic_bck_SOURCES = \ + test_mesh_local_traffic.c +test_mesh_local_traffic_bck_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/mesh/libgnunetmesh.la +test_mesh_local_traffic_bck_DEPENDENCIES = \ + libgnunetmesh.la + +test_mesh_local_traffic_both_SOURCES = \ + test_mesh_local_traffic.c +test_mesh_local_traffic_both_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/mesh/libgnunetmesh.la +test_mesh_local_traffic_both_DEPENDENCIES = \ + libgnunetmesh.la + + +ld_mesh_test_lib = \ + $(top_builddir)/src/mesh/libgnunetmeshtest.a \ + $(top_builddir)/src/mesh/libgnunetmesh.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/util/libgnunetutil.la + +dep_mesh_test_lib = \ + libgnunetmeshtest.a \ + libgnunetmesh.la + test_mesh_2dtorus_SOURCES = \ test_mesh_2dtorus.c -test_mesh_2dtorus_LDADD = \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testing/libgnunettesting.la +test_mesh_2dtorus_LDADD = $(ld_mesh_test_lib) +test_mesh_2dtorus_DEPENDENCIES = $(dep_mesh_test_lib) + +test_mesh_regex_SOURCES = \ + test_mesh_regex.c +test_mesh_regex_LDADD = $(ld_mesh_test_lib) +test_mesh_regex_DEPENDENCIES = $(dep_mesh_test_lib) test_mesh_small_unicast_SOURCES = \ test_mesh_small.c -test_mesh_small_unicast_LDADD = \ - $(top_builddir)/src/mesh/libgnunetmesh.la \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testing/libgnunettesting.la -test_mesh_small_unicast_DEPENDENCIES = \ - libgnunetmesh.la +test_mesh_small_unicast_LDADD = $(ld_mesh_test_lib) +test_mesh_small_unicast_DEPENDENCIES = $(dep_mesh_test_lib) test_mesh_small_multicast_SOURCES = \ test_mesh_small.c -test_mesh_small_multicast_LDADD = \ - $(top_builddir)/src/mesh/libgnunetmesh.la \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testing/libgnunettesting.la -test_mesh_small_multicast_DEPENDENCIES = \ - libgnunetmesh.la +test_mesh_small_multicast_LDADD = $(ld_mesh_test_lib) +test_mesh_small_multicast_DEPENDENCIES = $(dep_mesh_test_lib) -test_mesh_small_speed_SOURCES = \ +test_mesh_small_signal_SOURCES = \ test_mesh_small.c -test_mesh_small_speed_LDADD = \ - $(top_builddir)/src/mesh/libgnunetmesh.la \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testing/libgnunettesting.la -test_mesh_small_speed_DEPENDENCIES = \ - libgnunetmesh.la +test_mesh_small_signal_LDADD = $(ld_mesh_test_lib) +test_mesh_small_signal_DEPENDENCIES = $(dep_mesh_test_lib) test_mesh_small_speed_ack_SOURCES = \ test_mesh_small.c -test_mesh_small_speed_ack_LDADD = \ - $(top_builddir)/src/mesh/libgnunetmesh.la \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testing/libgnunettesting.la -test_mesh_small_speed_ack_DEPENDENCIES = \ - libgnunetmesh.la +test_mesh_small_speed_ack_LDADD = $(ld_mesh_test_lib) +test_mesh_small_speed_ack_DEPENDENCIES = $(dep_mesh_test_lib) + +test_mesh_small_speed_SOURCES = \ + test_mesh_small.c +test_mesh_small_speed_LDADD = $(ld_mesh_test_lib) +test_mesh_small_speed_DEPENDENCIES = $(dep_mesh_test_lib) + +test_mesh_small_speed_min_SOURCES = \ + test_mesh_small.c +test_mesh_small_speed_min_LDADD = $(ld_mesh_test_lib) +test_mesh_small_speed_min_DEPENDENCIES = $(dep_mesh_test_lib) + +test_mesh_small_speed_nobuf_SOURCES = \ + test_mesh_small.c +test_mesh_small_speed_nobuf_LDADD = $(ld_mesh_test_lib) +test_mesh_small_speed_nobuf_DEPENDENCIES = $(dep_mesh_test_lib) + +test_mesh_small_speed_backwards_SOURCES = \ + test_mesh_small.c +test_mesh_small_speed_backwards_LDADD = $(ld_mesh_test_lib) +test_mesh_small_speed_backwards_DEPENDENCIES = $(dep_mesh_test_lib) + +test_mesh_small_speed_min_backwards_SOURCES = \ + test_mesh_small.c +test_mesh_small_speed_min_backwards_LDADD = $(ld_mesh_test_lib) +test_mesh_small_speed_min_backwards_DEPENDENCIES = $(dep_mesh_test_lib) + +test_mesh_small_speed_nobuf_backwards_SOURCES = \ + test_mesh_small.c +test_mesh_small_speed_nobuf_backwards_LDADD = $(ld_mesh_test_lib) +test_mesh_small_speed_nobuf_backwards_DEPENDENCIES = $(dep_mesh_test_lib) + if ENABLE_TEST_RUN -TESTS = test_mesh_api test_mesh_tree_api test_mesh_local_1 test_mesh_local_2 \ - test_mesh_2dtorus test_mesh_small_unicast test_mesh_small_multicast +TESTS = test_mesh_api \ + test_mesh_tree_api \ + test_mesh_local_1 test_mesh_local_2 \ + test_mesh_local_traffic_fwd \ + test_mesh_local_traffic_bck \ + test_mesh_local_traffic_both \ + test_mesh_2dtorus test_mesh_regex \ + test_mesh_small_unicast test_mesh_small_multicast \ + test_mesh_small_signal \ + test_mesh_small_speed \ + test_mesh_small_speed_min \ + test_mesh_small_speed_nobuf \ + test_mesh_small_speed_backwards \ + test_mesh_small_speed_min_backwards endif EXTRA_DIST = \ + mesh.h mesh_protocol.h \ test_mesh.conf \ test_mesh_2dtorus.conf \ - test_mesh_small.conf \ - test_mesh_path.conf + test_mesh_small.conf diff --git a/src/mesh/Makefile.in b/src/mesh/Makefile.in index 1504c4b..4c55099 100644 --- a/src/mesh/Makefile.in +++ b/src/mesh/Makefile.in @@ -1,9 +1,9 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.11.6 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. +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 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. @@ -17,7 +17,25 @@ + VPATH = @srcdir@ +am__make_dryrun = \ + { \ + am__dry=no; \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \ + | grep '^AM OK$$' >/dev/null || am__dry=yes;; \ + *) \ + for am__flg in $$MAKEFLAGS; do \ + case $$am__flg in \ + *=*|--*) ;; \ + *n*) am__dry=yes; break;; \ + esac; \ + done;; \ + esac; \ + test $$am__dry = yes; \ + } pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -37,34 +55,56 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ -bin_PROGRAMS = gnunet-service-mesh$(EXEEXT) +libexec_PROGRAMS = gnunet-service-mesh$(EXEEXT) \ + gnunet-service-mesh-new$(EXEEXT) +bin_PROGRAMS = gnunet-mesh$(EXEEXT) check_PROGRAMS = test_mesh_api$(EXEEXT) test_mesh_tree_api$(EXEEXT) \ test_mesh_local_1$(EXEEXT) test_mesh_local_2$(EXEEXT) \ - test_mesh_2dtorus$(EXEEXT) test_mesh_small_unicast$(EXEEXT) \ + test_mesh_local_traffic_fwd$(EXEEXT) \ + test_mesh_local_traffic_bck$(EXEEXT) \ + test_mesh_local_traffic_both$(EXEEXT) \ + test_mesh_2dtorus$(EXEEXT) test_mesh_regex$(EXEEXT) \ + test_mesh_small_unicast$(EXEEXT) \ test_mesh_small_multicast$(EXEEXT) \ - test_mesh_small_speed$(EXEEXT) \ + test_mesh_small_signal$(EXEEXT) test_mesh_small_speed$(EXEEXT) \ + test_mesh_small_speed_nobuf$(EXEEXT) \ + test_mesh_small_speed_min$(EXEEXT) \ + test_mesh_small_speed_backwards$(EXEEXT) \ + test_mesh_small_speed_nobuf_backwards$(EXEEXT) \ + test_mesh_small_speed_min_backwards$(EXEEXT) \ test_mesh_small_speed_ack$(EXEEXT) @ENABLE_TEST_RUN_TRUE@TESTS = test_mesh_api$(EXEEXT) \ @ENABLE_TEST_RUN_TRUE@ test_mesh_tree_api$(EXEEXT) \ @ENABLE_TEST_RUN_TRUE@ test_mesh_local_1$(EXEEXT) \ @ENABLE_TEST_RUN_TRUE@ test_mesh_local_2$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_mesh_local_traffic_fwd$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_mesh_local_traffic_bck$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_mesh_local_traffic_both$(EXEEXT) \ @ENABLE_TEST_RUN_TRUE@ test_mesh_2dtorus$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_mesh_regex$(EXEEXT) \ @ENABLE_TEST_RUN_TRUE@ test_mesh_small_unicast$(EXEEXT) \ -@ENABLE_TEST_RUN_TRUE@ test_mesh_small_multicast$(EXEEXT) +@ENABLE_TEST_RUN_TRUE@ test_mesh_small_multicast$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_mesh_small_signal$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_mesh_small_speed$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_mesh_small_speed_min$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_mesh_small_speed_nobuf$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_mesh_small_speed_backwards$(EXEEXT) \ +@ENABLE_TEST_RUN_TRUE@ test_mesh_small_speed_min_backwards$(EXEEXT) subdir = src/mesh DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ $(srcdir)/mesh.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/gettext.m4 $(top_srcdir)/m4/glib-2.0.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/pkg.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) \ @@ -73,6 +113,17 @@ mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/gnunet_config.h CONFIG_CLEAN_FILES = mesh.conf CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +libgnunetmeshtest_a_AR = $(AR) $(ARFLAGS) +am_libgnunetmeshtest_a_OBJECTS = mesh_test_lib.$(OBJEXT) +libgnunetmeshtest_a_OBJECTS = $(am_libgnunetmeshtest_a_OBJECTS) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -94,45 +145,108 @@ am__nobase_list = $(am__nobase_strip_setup); \ 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)" \ +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(plugindir)" \ + "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)" \ "$(DESTDIR)$(pkgcfgdir)" -LTLIBRARIES = $(lib_LTLIBRARIES) +LTLIBRARIES = $(lib_LTLIBRARIES) $(plugin_LTLIBRARIES) +am_libgnunet_plugin_block_mesh_la_OBJECTS = plugin_block_mesh.lo +libgnunet_plugin_block_mesh_la_OBJECTS = \ + $(am_libgnunet_plugin_block_mesh_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +libgnunet_plugin_block_mesh_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) \ + $(libgnunet_plugin_block_mesh_la_LDFLAGS) $(LDFLAGS) -o $@ am__DEPENDENCIES_1 = libgnunetmesh_la_DEPENDENCIES = \ $(top_builddir)/src/util/libgnunetutil.la \ - $(am__DEPENDENCIES_1) -am_libgnunetmesh_la_OBJECTS = mesh_api.lo + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libgnunetmesh_la_OBJECTS = mesh_api.lo mesh_common.lo libgnunetmesh_la_OBJECTS = $(am_libgnunetmesh_la_OBJECTS) -AM_V_lt = $(am__v_lt_$(V)) -am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) -am__v_lt_0 = --silent libgnunetmesh_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libgnunetmesh_la_LDFLAGS) $(LDFLAGS) \ -o $@ -PROGRAMS = $(bin_PROGRAMS) -am_gnunet_service_mesh_OBJECTS = gnunet-service-mesh.$(OBJEXT) \ - mesh_tunnel_tree.$(OBJEXT) +PROGRAMS = $(bin_PROGRAMS) $(libexec_PROGRAMS) +am_gnunet_mesh_OBJECTS = gnunet-mesh.$(OBJEXT) +gnunet_mesh_OBJECTS = $(am_gnunet_mesh_OBJECTS) +am_gnunet_service_mesh_OBJECTS = \ + gnunet_service_mesh-gnunet-service-mesh.$(OBJEXT) \ + gnunet_service_mesh-mesh_tunnel_tree.$(OBJEXT) \ + gnunet_service_mesh-mesh_common.$(OBJEXT) gnunet_service_mesh_OBJECTS = $(am_gnunet_service_mesh_OBJECTS) +gnunet_service_mesh_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(gnunet_service_mesh_CFLAGS) $(CFLAGS) \ + $(gnunet_service_mesh_LDFLAGS) $(LDFLAGS) -o $@ +am_gnunet_service_mesh_new_OBJECTS = \ + gnunet_service_mesh_new-gnunet-service-mesh-new.$(OBJEXT) \ + gnunet_service_mesh_new-mesh_tunnel_tree.$(OBJEXT) \ + gnunet_service_mesh_new-mesh_common.$(OBJEXT) +gnunet_service_mesh_new_OBJECTS = \ + $(am_gnunet_service_mesh_new_OBJECTS) +gnunet_service_mesh_new_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(gnunet_service_mesh_new_CFLAGS) $(CFLAGS) \ + $(gnunet_service_mesh_new_LDFLAGS) $(LDFLAGS) -o $@ am_test_mesh_2dtorus_OBJECTS = test_mesh_2dtorus.$(OBJEXT) test_mesh_2dtorus_OBJECTS = $(am_test_mesh_2dtorus_OBJECTS) -test_mesh_2dtorus_DEPENDENCIES = \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testing/libgnunettesting.la am_test_mesh_api_OBJECTS = test_mesh_api.$(OBJEXT) test_mesh_api_OBJECTS = $(am_test_mesh_api_OBJECTS) am_test_mesh_local_1_OBJECTS = test_mesh_local_1.$(OBJEXT) test_mesh_local_1_OBJECTS = $(am_test_mesh_local_1_OBJECTS) am_test_mesh_local_2_OBJECTS = test_mesh_local_2.$(OBJEXT) test_mesh_local_2_OBJECTS = $(am_test_mesh_local_2_OBJECTS) +am_test_mesh_local_traffic_bck_OBJECTS = \ + test_mesh_local_traffic.$(OBJEXT) +test_mesh_local_traffic_bck_OBJECTS = \ + $(am_test_mesh_local_traffic_bck_OBJECTS) +am_test_mesh_local_traffic_both_OBJECTS = \ + test_mesh_local_traffic.$(OBJEXT) +test_mesh_local_traffic_both_OBJECTS = \ + $(am_test_mesh_local_traffic_both_OBJECTS) +am_test_mesh_local_traffic_fwd_OBJECTS = \ + test_mesh_local_traffic.$(OBJEXT) +test_mesh_local_traffic_fwd_OBJECTS = \ + $(am_test_mesh_local_traffic_fwd_OBJECTS) +am_test_mesh_regex_OBJECTS = test_mesh_regex.$(OBJEXT) +test_mesh_regex_OBJECTS = $(am_test_mesh_regex_OBJECTS) am_test_mesh_small_multicast_OBJECTS = test_mesh_small.$(OBJEXT) test_mesh_small_multicast_OBJECTS = \ $(am_test_mesh_small_multicast_OBJECTS) +am_test_mesh_small_signal_OBJECTS = test_mesh_small.$(OBJEXT) +test_mesh_small_signal_OBJECTS = $(am_test_mesh_small_signal_OBJECTS) am_test_mesh_small_speed_OBJECTS = test_mesh_small.$(OBJEXT) test_mesh_small_speed_OBJECTS = $(am_test_mesh_small_speed_OBJECTS) am_test_mesh_small_speed_ack_OBJECTS = test_mesh_small.$(OBJEXT) test_mesh_small_speed_ack_OBJECTS = \ $(am_test_mesh_small_speed_ack_OBJECTS) +am_test_mesh_small_speed_backwards_OBJECTS = \ + test_mesh_small.$(OBJEXT) +test_mesh_small_speed_backwards_OBJECTS = \ + $(am_test_mesh_small_speed_backwards_OBJECTS) +am_test_mesh_small_speed_min_OBJECTS = test_mesh_small.$(OBJEXT) +test_mesh_small_speed_min_OBJECTS = \ + $(am_test_mesh_small_speed_min_OBJECTS) +am_test_mesh_small_speed_min_backwards_OBJECTS = \ + test_mesh_small.$(OBJEXT) +test_mesh_small_speed_min_backwards_OBJECTS = \ + $(am_test_mesh_small_speed_min_backwards_OBJECTS) +am_test_mesh_small_speed_nobuf_OBJECTS = test_mesh_small.$(OBJEXT) +test_mesh_small_speed_nobuf_OBJECTS = \ + $(am_test_mesh_small_speed_nobuf_OBJECTS) +am_test_mesh_small_speed_nobuf_backwards_OBJECTS = \ + test_mesh_small.$(OBJEXT) +test_mesh_small_speed_nobuf_backwards_OBJECTS = \ + $(am_test_mesh_small_speed_nobuf_backwards_OBJECTS) am_test_mesh_small_unicast_OBJECTS = test_mesh_small.$(OBJEXT) test_mesh_small_unicast_OBJECTS = \ $(am_test_mesh_small_unicast_OBJECTS) @@ -148,39 +262,68 @@ 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 = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) 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 = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) 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 = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; -SOURCES = $(libgnunetmesh_la_SOURCES) $(gnunet_service_mesh_SOURCES) \ +SOURCES = $(libgnunetmeshtest_a_SOURCES) \ + $(libgnunet_plugin_block_mesh_la_SOURCES) \ + $(libgnunetmesh_la_SOURCES) $(gnunet_mesh_SOURCES) \ + $(gnunet_service_mesh_SOURCES) \ + $(gnunet_service_mesh_new_SOURCES) \ $(test_mesh_2dtorus_SOURCES) $(test_mesh_api_SOURCES) \ $(test_mesh_local_1_SOURCES) $(test_mesh_local_2_SOURCES) \ + $(test_mesh_local_traffic_bck_SOURCES) \ + $(test_mesh_local_traffic_both_SOURCES) \ + $(test_mesh_local_traffic_fwd_SOURCES) \ + $(test_mesh_regex_SOURCES) \ $(test_mesh_small_multicast_SOURCES) \ + $(test_mesh_small_signal_SOURCES) \ $(test_mesh_small_speed_SOURCES) \ $(test_mesh_small_speed_ack_SOURCES) \ + $(test_mesh_small_speed_backwards_SOURCES) \ + $(test_mesh_small_speed_min_SOURCES) \ + $(test_mesh_small_speed_min_backwards_SOURCES) \ + $(test_mesh_small_speed_nobuf_SOURCES) \ + $(test_mesh_small_speed_nobuf_backwards_SOURCES) \ $(test_mesh_small_unicast_SOURCES) \ $(test_mesh_tree_api_SOURCES) -DIST_SOURCES = $(libgnunetmesh_la_SOURCES) \ - $(gnunet_service_mesh_SOURCES) $(test_mesh_2dtorus_SOURCES) \ - $(test_mesh_api_SOURCES) $(test_mesh_local_1_SOURCES) \ - $(test_mesh_local_2_SOURCES) \ +DIST_SOURCES = $(libgnunetmeshtest_a_SOURCES) \ + $(libgnunet_plugin_block_mesh_la_SOURCES) \ + $(libgnunetmesh_la_SOURCES) $(gnunet_mesh_SOURCES) \ + $(gnunet_service_mesh_SOURCES) \ + $(gnunet_service_mesh_new_SOURCES) \ + $(test_mesh_2dtorus_SOURCES) $(test_mesh_api_SOURCES) \ + $(test_mesh_local_1_SOURCES) $(test_mesh_local_2_SOURCES) \ + $(test_mesh_local_traffic_bck_SOURCES) \ + $(test_mesh_local_traffic_both_SOURCES) \ + $(test_mesh_local_traffic_fwd_SOURCES) \ + $(test_mesh_regex_SOURCES) \ $(test_mesh_small_multicast_SOURCES) \ + $(test_mesh_small_signal_SOURCES) \ $(test_mesh_small_speed_SOURCES) \ $(test_mesh_small_speed_ack_SOURCES) \ + $(test_mesh_small_speed_backwards_SOURCES) \ + $(test_mesh_small_speed_min_SOURCES) \ + $(test_mesh_small_speed_min_backwards_SOURCES) \ + $(test_mesh_small_speed_nobuf_SOURCES) \ + $(test_mesh_small_speed_nobuf_backwards_SOURCES) \ $(test_mesh_small_unicast_SOURCES) \ $(test_mesh_tree_api_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac DATA = $(pkgcfg_DATA) ETAGS = etags CTAGS = ctags @@ -222,6 +365,10 @@ EXEEXT = @EXEEXT@ EXT_LIBS = @EXT_LIBS@ EXT_LIB_PATH = @EXT_LIB_PATH@ FGREP = @FGREP@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ GMSGFMT = @GMSGFMT@ GMSGFMT_015 = @GMSGFMT_015@ GNUNETDNS_GROUP = @GNUNETDNS_GROUP@ @@ -232,6 +379,7 @@ GN_LIBINTL = @GN_LIBINTL@ GN_LIB_LDFLAGS = @GN_LIB_LDFLAGS@ GN_PLUGIN_LDFLAGS = @GN_PLUGIN_LDFLAGS@ GN_USER_HOME_DIR = @GN_USER_HOME_DIR@ +GOBJECT_QUERY = @GOBJECT_QUERY@ GREP = @GREP@ HAVE_LIBUNISTRING = @HAVE_LIBUNISTRING@ INCLTDL = @INCLTDL@ @@ -254,6 +402,8 @@ LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@ LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBGTOP_CFLAGS = @LIBGTOP_CFLAGS@ +LIBGTOP_LIBS = @LIBGTOP_LIBS@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBLTDL = @LIBLTDL@ @@ -275,6 +425,7 @@ LT_CONFIG_H = @LT_CONFIG_H@ LT_DLLOADERS = @LT_DLLOADERS@ LT_DLPREOPEN = @LT_DLPREOPEN@ MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ MONKEYPREFIX = @MONKEYPREFIX@ MSGFMT = @MSGFMT@ @@ -284,6 +435,7 @@ MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ MYSQL_LDFLAGS = @MYSQL_LDFLAGS@ NM = @NM@ NMEDIT = @NMEDIT@ +NSS_DIR = @NSS_DIR@ OBJC = @OBJC@ OBJCDEPMODE = @OBJCDEPMODE@ OBJCFLAGS = @OBJCFLAGS@ @@ -299,6 +451,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ POSTGRES_CPPFLAGS = @POSTGRES_CPPFLAGS@ POSTGRES_LDFLAGS = @POSTGRES_LDFLAGS@ POSUB = @POSUB@ @@ -330,6 +483,7 @@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ @@ -352,6 +506,7 @@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ +gitcommand = @gitcommand@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ @@ -362,10 +517,9 @@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ -libexecdir = @libexecdir@ +libexecdir = $(pkglibdir)/libexec/ localedir = @localedir@ localstatedir = @localstatedir@ -lt_ECHO = @lt_ECHO@ ltdl_LIBOBJS = @ltdl_LIBOBJS@ ltdl_LTLIBOBJS = @ltdl_LTLIBOBJS@ mandir = @mandir@ @@ -383,6 +537,7 @@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ subdirs = @subdirs@ +svnversioncommand = @svnversioncommand@ sys_symbol_underscore = @sys_symbol_underscore@ sysconfdir = @sysconfdir@ target = @target@ @@ -401,40 +556,114 @@ pkgcfgdir = $(pkgdatadir)/config.d/ pkgcfg_DATA = \ mesh.conf +plugindir = $(libdir)/gnunet AM_CLFAGS = -g lib_LTLIBRARIES = \ libgnunetmesh.la +plugin_LTLIBRARIES = \ + libgnunet_plugin_block_mesh.la + +libgnunet_plugin_block_mesh_la_SOURCES = \ + plugin_block_mesh.c + +libgnunet_plugin_block_mesh_la_LIBADD = \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/util/libgnunetutil.la + +libgnunet_plugin_block_mesh_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + +libgnunet_plugin_block_mesh_la_DEPENDENCIES = \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/util/libgnunetutil.la + +libgnunetmesh_la_SOURCES = \ + mesh_api.c mesh_common.c + +libgnunetmesh_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(XLIB) \ + $(LTLIBINTL) + +libgnunetmesh_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 2:1:1 + gnunet_service_mesh_SOURCES = \ gnunet-service-mesh.c \ - mesh_tunnel_tree.c mesh_tunnel_tree.h + mesh_tunnel_tree.c mesh_tunnel_tree.h \ + mesh_common.c +gnunet_service_mesh_CFLAGS = $(AM_CFLAGS) gnunet_service_mesh_LDADD = \ - $(top_builddir)/src/core/libgnunetcore.la\ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/core/libgnunetcore.la \ $(top_builddir)/src/dht/libgnunetdht.la \ - $(top_builddir)/src/util/libgnunetutil.la + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/regex/libgnunetregex.la gnunet_service_mesh_DEPENDENCIES = \ - $(top_builddir)/src/core/libgnunetcore.la\ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/core/libgnunetcore.la \ $(top_builddir)/src/dht/libgnunetdht.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/regex/libgnunetregex.la + +@LINUX_TRUE@gnunet_service_mesh_LDFLAGS = -lrt +gnunet_mesh_SOURCES = \ + gnunet-mesh.c + +gnunet_mesh_LDADD = \ + $(top_builddir)/src/mesh/libgnunetmesh.la \ $(top_builddir)/src/util/libgnunetutil.la -libgnunetmesh_la_SOURCES = \ - mesh_api.c mesh.h mesh_protocol.h +gnunet_mesh_DEPENDENCIES = \ + libgnunetmesh.la -libgnunetmesh_la_LIBADD = \ +gnunet_service_mesh_new_SOURCES = \ + gnunet-service-mesh-new.c \ + mesh_tunnel_tree.c \ + mesh_common.c + +gnunet_service_mesh_new_CFLAGS = $(AM_CFLAGS) +gnunet_service_mesh_new_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ - $(XLIB) + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/dht/libgnunetdht.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/regex/libgnunetregex.la -libgnunetmesh_la_LDFLAGS = \ - $(GN_LIB_LDFLAGS) $(WINFLAGS) \ - -version-info 1:0:0 +gnunet_service_mesh_new_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/dht/libgnunetdht.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/regex/libgnunetregex.la + +@LINUX_TRUE@gnunet_service_mesh_new_LDFLAGS = -lrt +noinst_LIBRARIES = libgnunetmeshtest.a +libgnunetmeshtest_a_SOURCES = \ + mesh_test_lib.c mesh_test_lib.h + +libgnunetmeshtest_a_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/mesh/libgnunetmesh.la + +libgnunetmeshtest_a_DEPENDENCIES = \ + libgnunetmesh.la test_mesh_api_SOURCES = \ test_mesh_api.c test_mesh_api_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ $(top_builddir)/src/mesh/libgnunetmesh.la test_mesh_api_DEPENDENCIES = \ @@ -446,7 +675,7 @@ test_mesh_tree_api_SOURCES = \ test_mesh_tree_api_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/dht/libgnunetdht.la + $(top_builddir)/src/dht/libgnunetdht.la test_mesh_tree_api_DEPENDENCIES = \ libgnunetmesh.la \ @@ -457,6 +686,7 @@ test_mesh_local_1_SOURCES = \ test_mesh_local_1_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ $(top_builddir)/src/mesh/libgnunetmesh.la test_mesh_local_1_DEPENDENCIES = \ @@ -467,67 +697,120 @@ test_mesh_local_2_SOURCES = \ test_mesh_local_2_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ $(top_builddir)/src/mesh/libgnunetmesh.la test_mesh_local_2_DEPENDENCIES = \ libgnunetmesh.la -test_mesh_2dtorus_SOURCES = \ - test_mesh_2dtorus.c +test_mesh_local_traffic_fwd_SOURCES = \ + test_mesh_local_traffic.c -test_mesh_2dtorus_LDADD = \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testing/libgnunettesting.la +test_mesh_local_traffic_fwd_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/mesh/libgnunetmesh.la -test_mesh_small_unicast_SOURCES = \ - test_mesh_small.c +test_mesh_local_traffic_fwd_DEPENDENCIES = \ + libgnunetmesh.la -test_mesh_small_unicast_LDADD = \ +test_mesh_local_traffic_bck_SOURCES = \ + test_mesh_local_traffic.c + +test_mesh_local_traffic_bck_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/mesh/libgnunetmesh.la + +test_mesh_local_traffic_bck_DEPENDENCIES = \ + libgnunetmesh.la + +test_mesh_local_traffic_both_SOURCES = \ + test_mesh_local_traffic.c + +test_mesh_local_traffic_both_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/mesh/libgnunetmesh.la + +test_mesh_local_traffic_both_DEPENDENCIES = \ + libgnunetmesh.la + +ld_mesh_test_lib = \ + $(top_builddir)/src/mesh/libgnunetmeshtest.a \ $(top_builddir)/src/mesh/libgnunetmesh.la \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testing/libgnunettesting.la + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/util/libgnunetutil.la -test_mesh_small_unicast_DEPENDENCIES = \ +dep_mesh_test_lib = \ + libgnunetmeshtest.a \ libgnunetmesh.la +test_mesh_2dtorus_SOURCES = \ + test_mesh_2dtorus.c + +test_mesh_2dtorus_LDADD = $(ld_mesh_test_lib) +test_mesh_2dtorus_DEPENDENCIES = $(dep_mesh_test_lib) +test_mesh_regex_SOURCES = \ + test_mesh_regex.c + +test_mesh_regex_LDADD = $(ld_mesh_test_lib) +test_mesh_regex_DEPENDENCIES = $(dep_mesh_test_lib) +test_mesh_small_unicast_SOURCES = \ + test_mesh_small.c + +test_mesh_small_unicast_LDADD = $(ld_mesh_test_lib) +test_mesh_small_unicast_DEPENDENCIES = $(dep_mesh_test_lib) test_mesh_small_multicast_SOURCES = \ test_mesh_small.c -test_mesh_small_multicast_LDADD = \ - $(top_builddir)/src/mesh/libgnunetmesh.la \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testing/libgnunettesting.la +test_mesh_small_multicast_LDADD = $(ld_mesh_test_lib) +test_mesh_small_multicast_DEPENDENCIES = $(dep_mesh_test_lib) +test_mesh_small_signal_SOURCES = \ + test_mesh_small.c -test_mesh_small_multicast_DEPENDENCIES = \ - libgnunetmesh.la +test_mesh_small_signal_LDADD = $(ld_mesh_test_lib) +test_mesh_small_signal_DEPENDENCIES = $(dep_mesh_test_lib) +test_mesh_small_speed_ack_SOURCES = \ + test_mesh_small.c +test_mesh_small_speed_ack_LDADD = $(ld_mesh_test_lib) +test_mesh_small_speed_ack_DEPENDENCIES = $(dep_mesh_test_lib) test_mesh_small_speed_SOURCES = \ test_mesh_small.c -test_mesh_small_speed_LDADD = \ - $(top_builddir)/src/mesh/libgnunetmesh.la \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testing/libgnunettesting.la +test_mesh_small_speed_LDADD = $(ld_mesh_test_lib) +test_mesh_small_speed_DEPENDENCIES = $(dep_mesh_test_lib) +test_mesh_small_speed_min_SOURCES = \ + test_mesh_small.c -test_mesh_small_speed_DEPENDENCIES = \ - libgnunetmesh.la +test_mesh_small_speed_min_LDADD = $(ld_mesh_test_lib) +test_mesh_small_speed_min_DEPENDENCIES = $(dep_mesh_test_lib) +test_mesh_small_speed_nobuf_SOURCES = \ + test_mesh_small.c -test_mesh_small_speed_ack_SOURCES = \ +test_mesh_small_speed_nobuf_LDADD = $(ld_mesh_test_lib) +test_mesh_small_speed_nobuf_DEPENDENCIES = $(dep_mesh_test_lib) +test_mesh_small_speed_backwards_SOURCES = \ test_mesh_small.c -test_mesh_small_speed_ack_LDADD = \ - $(top_builddir)/src/mesh/libgnunetmesh.la \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testing/libgnunettesting.la +test_mesh_small_speed_backwards_LDADD = $(ld_mesh_test_lib) +test_mesh_small_speed_backwards_DEPENDENCIES = $(dep_mesh_test_lib) +test_mesh_small_speed_min_backwards_SOURCES = \ + test_mesh_small.c -test_mesh_small_speed_ack_DEPENDENCIES = \ - libgnunetmesh.la +test_mesh_small_speed_min_backwards_LDADD = $(ld_mesh_test_lib) +test_mesh_small_speed_min_backwards_DEPENDENCIES = $(dep_mesh_test_lib) +test_mesh_small_speed_nobuf_backwards_SOURCES = \ + test_mesh_small.c +test_mesh_small_speed_nobuf_backwards_LDADD = $(ld_mesh_test_lib) +test_mesh_small_speed_nobuf_backwards_DEPENDENCIES = $(dep_mesh_test_lib) EXTRA_DIST = \ + mesh.h mesh_protocol.h \ test_mesh.conf \ test_mesh_2dtorus.conf \ - test_mesh_small.conf \ - test_mesh_path.conf + test_mesh_small.conf all: all-am @@ -565,9 +848,15 @@ $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): mesh.conf: $(top_builddir)/config.status $(srcdir)/mesh.conf.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +libgnunetmeshtest.a: $(libgnunetmeshtest_a_OBJECTS) $(libgnunetmeshtest_a_DEPENDENCIES) $(EXTRA_libgnunetmeshtest_a_DEPENDENCIES) + $(AM_V_at)-rm -f libgnunetmeshtest.a + $(AM_V_AR)$(libgnunetmeshtest_a_AR) libgnunetmeshtest.a $(libgnunetmeshtest_a_OBJECTS) $(libgnunetmeshtest_a_LIBADD) + $(AM_V_at)$(RANLIB) libgnunetmeshtest.a 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 \ @@ -575,6 +864,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES) else :; fi; \ done; \ test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ 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)"; \ } @@ -596,12 +887,49 @@ clean-libLTLIBRARIES: echo "rm -f \"$${dir}/so_locations\""; \ rm -f "$${dir}/so_locations"; \ done -libgnunetmesh.la: $(libgnunetmesh_la_OBJECTS) $(libgnunetmesh_la_DEPENDENCIES) +install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(plugindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(plugindir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \ + } + +uninstall-pluginLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \ + done + +clean-pluginLTLIBRARIES: + -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES) + @list='$(plugin_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 +libgnunet_plugin_block_mesh.la: $(libgnunet_plugin_block_mesh_la_OBJECTS) $(libgnunet_plugin_block_mesh_la_DEPENDENCIES) $(EXTRA_libgnunet_plugin_block_mesh_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgnunet_plugin_block_mesh_la_LINK) -rpath $(plugindir) $(libgnunet_plugin_block_mesh_la_OBJECTS) $(libgnunet_plugin_block_mesh_la_LIBADD) $(LIBS) +libgnunetmesh.la: $(libgnunetmesh_la_OBJECTS) $(libgnunetmesh_la_DEPENDENCIES) $(EXTRA_libgnunetmesh_la_DEPENDENCIES) $(AM_V_CCLD)$(libgnunetmesh_la_LINK) -rpath $(libdir) $(libgnunetmesh_la_OBJECTS) $(libgnunetmesh_la_LIBADD) $(LIBS) install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) - test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p || test -f $$p1; \ @@ -650,34 +978,116 @@ clean-checkPROGRAMS: list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list -gnunet-service-mesh$(EXEEXT): $(gnunet_service_mesh_OBJECTS) $(gnunet_service_mesh_DEPENDENCIES) +install-libexecPROGRAMS: $(libexec_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \ + fi; \ + 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)$(libexecdir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-libexecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || 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)$(libexecdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(libexecdir)" && rm -f $$files + +clean-libexecPROGRAMS: + @list='$(libexec_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-mesh$(EXEEXT): $(gnunet_mesh_OBJECTS) $(gnunet_mesh_DEPENDENCIES) $(EXTRA_gnunet_mesh_DEPENDENCIES) + @rm -f gnunet-mesh$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gnunet_mesh_OBJECTS) $(gnunet_mesh_LDADD) $(LIBS) +gnunet-service-mesh$(EXEEXT): $(gnunet_service_mesh_OBJECTS) $(gnunet_service_mesh_DEPENDENCIES) $(EXTRA_gnunet_service_mesh_DEPENDENCIES) @rm -f gnunet-service-mesh$(EXEEXT) - $(AM_V_CCLD)$(LINK) $(gnunet_service_mesh_OBJECTS) $(gnunet_service_mesh_LDADD) $(LIBS) -test_mesh_2dtorus$(EXEEXT): $(test_mesh_2dtorus_OBJECTS) $(test_mesh_2dtorus_DEPENDENCIES) + $(AM_V_CCLD)$(gnunet_service_mesh_LINK) $(gnunet_service_mesh_OBJECTS) $(gnunet_service_mesh_LDADD) $(LIBS) +gnunet-service-mesh-new$(EXEEXT): $(gnunet_service_mesh_new_OBJECTS) $(gnunet_service_mesh_new_DEPENDENCIES) $(EXTRA_gnunet_service_mesh_new_DEPENDENCIES) + @rm -f gnunet-service-mesh-new$(EXEEXT) + $(AM_V_CCLD)$(gnunet_service_mesh_new_LINK) $(gnunet_service_mesh_new_OBJECTS) $(gnunet_service_mesh_new_LDADD) $(LIBS) +test_mesh_2dtorus$(EXEEXT): $(test_mesh_2dtorus_OBJECTS) $(test_mesh_2dtorus_DEPENDENCIES) $(EXTRA_test_mesh_2dtorus_DEPENDENCIES) @rm -f test_mesh_2dtorus$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mesh_2dtorus_OBJECTS) $(test_mesh_2dtorus_LDADD) $(LIBS) -test_mesh_api$(EXEEXT): $(test_mesh_api_OBJECTS) $(test_mesh_api_DEPENDENCIES) +test_mesh_api$(EXEEXT): $(test_mesh_api_OBJECTS) $(test_mesh_api_DEPENDENCIES) $(EXTRA_test_mesh_api_DEPENDENCIES) @rm -f test_mesh_api$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mesh_api_OBJECTS) $(test_mesh_api_LDADD) $(LIBS) -test_mesh_local_1$(EXEEXT): $(test_mesh_local_1_OBJECTS) $(test_mesh_local_1_DEPENDENCIES) +test_mesh_local_1$(EXEEXT): $(test_mesh_local_1_OBJECTS) $(test_mesh_local_1_DEPENDENCIES) $(EXTRA_test_mesh_local_1_DEPENDENCIES) @rm -f test_mesh_local_1$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mesh_local_1_OBJECTS) $(test_mesh_local_1_LDADD) $(LIBS) -test_mesh_local_2$(EXEEXT): $(test_mesh_local_2_OBJECTS) $(test_mesh_local_2_DEPENDENCIES) +test_mesh_local_2$(EXEEXT): $(test_mesh_local_2_OBJECTS) $(test_mesh_local_2_DEPENDENCIES) $(EXTRA_test_mesh_local_2_DEPENDENCIES) @rm -f test_mesh_local_2$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mesh_local_2_OBJECTS) $(test_mesh_local_2_LDADD) $(LIBS) -test_mesh_small_multicast$(EXEEXT): $(test_mesh_small_multicast_OBJECTS) $(test_mesh_small_multicast_DEPENDENCIES) +test_mesh_local_traffic_bck$(EXEEXT): $(test_mesh_local_traffic_bck_OBJECTS) $(test_mesh_local_traffic_bck_DEPENDENCIES) $(EXTRA_test_mesh_local_traffic_bck_DEPENDENCIES) + @rm -f test_mesh_local_traffic_bck$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_mesh_local_traffic_bck_OBJECTS) $(test_mesh_local_traffic_bck_LDADD) $(LIBS) +test_mesh_local_traffic_both$(EXEEXT): $(test_mesh_local_traffic_both_OBJECTS) $(test_mesh_local_traffic_both_DEPENDENCIES) $(EXTRA_test_mesh_local_traffic_both_DEPENDENCIES) + @rm -f test_mesh_local_traffic_both$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_mesh_local_traffic_both_OBJECTS) $(test_mesh_local_traffic_both_LDADD) $(LIBS) +test_mesh_local_traffic_fwd$(EXEEXT): $(test_mesh_local_traffic_fwd_OBJECTS) $(test_mesh_local_traffic_fwd_DEPENDENCIES) $(EXTRA_test_mesh_local_traffic_fwd_DEPENDENCIES) + @rm -f test_mesh_local_traffic_fwd$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_mesh_local_traffic_fwd_OBJECTS) $(test_mesh_local_traffic_fwd_LDADD) $(LIBS) +test_mesh_regex$(EXEEXT): $(test_mesh_regex_OBJECTS) $(test_mesh_regex_DEPENDENCIES) $(EXTRA_test_mesh_regex_DEPENDENCIES) + @rm -f test_mesh_regex$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_mesh_regex_OBJECTS) $(test_mesh_regex_LDADD) $(LIBS) +test_mesh_small_multicast$(EXEEXT): $(test_mesh_small_multicast_OBJECTS) $(test_mesh_small_multicast_DEPENDENCIES) $(EXTRA_test_mesh_small_multicast_DEPENDENCIES) @rm -f test_mesh_small_multicast$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mesh_small_multicast_OBJECTS) $(test_mesh_small_multicast_LDADD) $(LIBS) -test_mesh_small_speed$(EXEEXT): $(test_mesh_small_speed_OBJECTS) $(test_mesh_small_speed_DEPENDENCIES) +test_mesh_small_signal$(EXEEXT): $(test_mesh_small_signal_OBJECTS) $(test_mesh_small_signal_DEPENDENCIES) $(EXTRA_test_mesh_small_signal_DEPENDENCIES) + @rm -f test_mesh_small_signal$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_mesh_small_signal_OBJECTS) $(test_mesh_small_signal_LDADD) $(LIBS) +test_mesh_small_speed$(EXEEXT): $(test_mesh_small_speed_OBJECTS) $(test_mesh_small_speed_DEPENDENCIES) $(EXTRA_test_mesh_small_speed_DEPENDENCIES) @rm -f test_mesh_small_speed$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mesh_small_speed_OBJECTS) $(test_mesh_small_speed_LDADD) $(LIBS) -test_mesh_small_speed_ack$(EXEEXT): $(test_mesh_small_speed_ack_OBJECTS) $(test_mesh_small_speed_ack_DEPENDENCIES) +test_mesh_small_speed_ack$(EXEEXT): $(test_mesh_small_speed_ack_OBJECTS) $(test_mesh_small_speed_ack_DEPENDENCIES) $(EXTRA_test_mesh_small_speed_ack_DEPENDENCIES) @rm -f test_mesh_small_speed_ack$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mesh_small_speed_ack_OBJECTS) $(test_mesh_small_speed_ack_LDADD) $(LIBS) -test_mesh_small_unicast$(EXEEXT): $(test_mesh_small_unicast_OBJECTS) $(test_mesh_small_unicast_DEPENDENCIES) +test_mesh_small_speed_backwards$(EXEEXT): $(test_mesh_small_speed_backwards_OBJECTS) $(test_mesh_small_speed_backwards_DEPENDENCIES) $(EXTRA_test_mesh_small_speed_backwards_DEPENDENCIES) + @rm -f test_mesh_small_speed_backwards$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_mesh_small_speed_backwards_OBJECTS) $(test_mesh_small_speed_backwards_LDADD) $(LIBS) +test_mesh_small_speed_min$(EXEEXT): $(test_mesh_small_speed_min_OBJECTS) $(test_mesh_small_speed_min_DEPENDENCIES) $(EXTRA_test_mesh_small_speed_min_DEPENDENCIES) + @rm -f test_mesh_small_speed_min$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_mesh_small_speed_min_OBJECTS) $(test_mesh_small_speed_min_LDADD) $(LIBS) +test_mesh_small_speed_min_backwards$(EXEEXT): $(test_mesh_small_speed_min_backwards_OBJECTS) $(test_mesh_small_speed_min_backwards_DEPENDENCIES) $(EXTRA_test_mesh_small_speed_min_backwards_DEPENDENCIES) + @rm -f test_mesh_small_speed_min_backwards$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_mesh_small_speed_min_backwards_OBJECTS) $(test_mesh_small_speed_min_backwards_LDADD) $(LIBS) +test_mesh_small_speed_nobuf$(EXEEXT): $(test_mesh_small_speed_nobuf_OBJECTS) $(test_mesh_small_speed_nobuf_DEPENDENCIES) $(EXTRA_test_mesh_small_speed_nobuf_DEPENDENCIES) + @rm -f test_mesh_small_speed_nobuf$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_mesh_small_speed_nobuf_OBJECTS) $(test_mesh_small_speed_nobuf_LDADD) $(LIBS) +test_mesh_small_speed_nobuf_backwards$(EXEEXT): $(test_mesh_small_speed_nobuf_backwards_OBJECTS) $(test_mesh_small_speed_nobuf_backwards_DEPENDENCIES) $(EXTRA_test_mesh_small_speed_nobuf_backwards_DEPENDENCIES) + @rm -f test_mesh_small_speed_nobuf_backwards$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_mesh_small_speed_nobuf_backwards_OBJECTS) $(test_mesh_small_speed_nobuf_backwards_LDADD) $(LIBS) +test_mesh_small_unicast$(EXEEXT): $(test_mesh_small_unicast_OBJECTS) $(test_mesh_small_unicast_DEPENDENCIES) $(EXTRA_test_mesh_small_unicast_DEPENDENCIES) @rm -f test_mesh_small_unicast$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mesh_small_unicast_OBJECTS) $(test_mesh_small_unicast_LDADD) $(LIBS) -test_mesh_tree_api$(EXEEXT): $(test_mesh_tree_api_OBJECTS) $(test_mesh_tree_api_DEPENDENCIES) +test_mesh_tree_api$(EXEEXT): $(test_mesh_tree_api_OBJECTS) $(test_mesh_tree_api_DEPENDENCIES) $(EXTRA_test_mesh_tree_api_DEPENDENCIES) @rm -f test_mesh_tree_api$(EXEEXT) $(AM_V_CCLD)$(LINK) $(test_mesh_tree_api_OBJECTS) $(test_mesh_tree_api_LDADD) $(LIBS) @@ -687,39 +1097,130 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-mesh.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-mesh.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet_service_mesh-gnunet-service-mesh.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet_service_mesh-mesh_common.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet_service_mesh-mesh_tunnel_tree.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet_service_mesh_new-gnunet-service-mesh-new.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet_service_mesh_new-mesh_common.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet_service_mesh_new-mesh_tunnel_tree.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mesh_api.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mesh_tunnel_tree.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mesh_common.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mesh_test_lib.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin_block_mesh.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mesh_2dtorus.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mesh_api.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mesh_local_1.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mesh_local_2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mesh_local_traffic.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mesh_regex.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mesh_small.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mesh_tree_api.Po@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@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(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@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(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@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +gnunet_service_mesh-gnunet-service-mesh.o: gnunet-service-mesh.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_CFLAGS) $(CFLAGS) -MT gnunet_service_mesh-gnunet-service-mesh.o -MD -MP -MF $(DEPDIR)/gnunet_service_mesh-gnunet-service-mesh.Tpo -c -o gnunet_service_mesh-gnunet-service-mesh.o `test -f 'gnunet-service-mesh.c' || echo '$(srcdir)/'`gnunet-service-mesh.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gnunet_service_mesh-gnunet-service-mesh.Tpo $(DEPDIR)/gnunet_service_mesh-gnunet-service-mesh.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gnunet-service-mesh.c' object='gnunet_service_mesh-gnunet-service-mesh.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_CFLAGS) $(CFLAGS) -c -o gnunet_service_mesh-gnunet-service-mesh.o `test -f 'gnunet-service-mesh.c' || echo '$(srcdir)/'`gnunet-service-mesh.c + +gnunet_service_mesh-gnunet-service-mesh.obj: gnunet-service-mesh.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_CFLAGS) $(CFLAGS) -MT gnunet_service_mesh-gnunet-service-mesh.obj -MD -MP -MF $(DEPDIR)/gnunet_service_mesh-gnunet-service-mesh.Tpo -c -o gnunet_service_mesh-gnunet-service-mesh.obj `if test -f 'gnunet-service-mesh.c'; then $(CYGPATH_W) 'gnunet-service-mesh.c'; else $(CYGPATH_W) '$(srcdir)/gnunet-service-mesh.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gnunet_service_mesh-gnunet-service-mesh.Tpo $(DEPDIR)/gnunet_service_mesh-gnunet-service-mesh.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gnunet-service-mesh.c' object='gnunet_service_mesh-gnunet-service-mesh.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_CFLAGS) $(CFLAGS) -c -o gnunet_service_mesh-gnunet-service-mesh.obj `if test -f 'gnunet-service-mesh.c'; then $(CYGPATH_W) 'gnunet-service-mesh.c'; else $(CYGPATH_W) '$(srcdir)/gnunet-service-mesh.c'; fi` + +gnunet_service_mesh-mesh_tunnel_tree.o: mesh_tunnel_tree.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_CFLAGS) $(CFLAGS) -MT gnunet_service_mesh-mesh_tunnel_tree.o -MD -MP -MF $(DEPDIR)/gnunet_service_mesh-mesh_tunnel_tree.Tpo -c -o gnunet_service_mesh-mesh_tunnel_tree.o `test -f 'mesh_tunnel_tree.c' || echo '$(srcdir)/'`mesh_tunnel_tree.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gnunet_service_mesh-mesh_tunnel_tree.Tpo $(DEPDIR)/gnunet_service_mesh-mesh_tunnel_tree.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mesh_tunnel_tree.c' object='gnunet_service_mesh-mesh_tunnel_tree.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_CFLAGS) $(CFLAGS) -c -o gnunet_service_mesh-mesh_tunnel_tree.o `test -f 'mesh_tunnel_tree.c' || echo '$(srcdir)/'`mesh_tunnel_tree.c + +gnunet_service_mesh-mesh_tunnel_tree.obj: mesh_tunnel_tree.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_CFLAGS) $(CFLAGS) -MT gnunet_service_mesh-mesh_tunnel_tree.obj -MD -MP -MF $(DEPDIR)/gnunet_service_mesh-mesh_tunnel_tree.Tpo -c -o gnunet_service_mesh-mesh_tunnel_tree.obj `if test -f 'mesh_tunnel_tree.c'; then $(CYGPATH_W) 'mesh_tunnel_tree.c'; else $(CYGPATH_W) '$(srcdir)/mesh_tunnel_tree.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gnunet_service_mesh-mesh_tunnel_tree.Tpo $(DEPDIR)/gnunet_service_mesh-mesh_tunnel_tree.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mesh_tunnel_tree.c' object='gnunet_service_mesh-mesh_tunnel_tree.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_CFLAGS) $(CFLAGS) -c -o gnunet_service_mesh-mesh_tunnel_tree.obj `if test -f 'mesh_tunnel_tree.c'; then $(CYGPATH_W) 'mesh_tunnel_tree.c'; else $(CYGPATH_W) '$(srcdir)/mesh_tunnel_tree.c'; fi` + +gnunet_service_mesh-mesh_common.o: mesh_common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_CFLAGS) $(CFLAGS) -MT gnunet_service_mesh-mesh_common.o -MD -MP -MF $(DEPDIR)/gnunet_service_mesh-mesh_common.Tpo -c -o gnunet_service_mesh-mesh_common.o `test -f 'mesh_common.c' || echo '$(srcdir)/'`mesh_common.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gnunet_service_mesh-mesh_common.Tpo $(DEPDIR)/gnunet_service_mesh-mesh_common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mesh_common.c' object='gnunet_service_mesh-mesh_common.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_CFLAGS) $(CFLAGS) -c -o gnunet_service_mesh-mesh_common.o `test -f 'mesh_common.c' || echo '$(srcdir)/'`mesh_common.c + +gnunet_service_mesh-mesh_common.obj: mesh_common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_CFLAGS) $(CFLAGS) -MT gnunet_service_mesh-mesh_common.obj -MD -MP -MF $(DEPDIR)/gnunet_service_mesh-mesh_common.Tpo -c -o gnunet_service_mesh-mesh_common.obj `if test -f 'mesh_common.c'; then $(CYGPATH_W) 'mesh_common.c'; else $(CYGPATH_W) '$(srcdir)/mesh_common.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gnunet_service_mesh-mesh_common.Tpo $(DEPDIR)/gnunet_service_mesh-mesh_common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mesh_common.c' object='gnunet_service_mesh-mesh_common.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_CFLAGS) $(CFLAGS) -c -o gnunet_service_mesh-mesh_common.obj `if test -f 'mesh_common.c'; then $(CYGPATH_W) 'mesh_common.c'; else $(CYGPATH_W) '$(srcdir)/mesh_common.c'; fi` + +gnunet_service_mesh_new-gnunet-service-mesh-new.o: gnunet-service-mesh-new.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_new_CFLAGS) $(CFLAGS) -MT gnunet_service_mesh_new-gnunet-service-mesh-new.o -MD -MP -MF $(DEPDIR)/gnunet_service_mesh_new-gnunet-service-mesh-new.Tpo -c -o gnunet_service_mesh_new-gnunet-service-mesh-new.o `test -f 'gnunet-service-mesh-new.c' || echo '$(srcdir)/'`gnunet-service-mesh-new.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gnunet_service_mesh_new-gnunet-service-mesh-new.Tpo $(DEPDIR)/gnunet_service_mesh_new-gnunet-service-mesh-new.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gnunet-service-mesh-new.c' object='gnunet_service_mesh_new-gnunet-service-mesh-new.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_new_CFLAGS) $(CFLAGS) -c -o gnunet_service_mesh_new-gnunet-service-mesh-new.o `test -f 'gnunet-service-mesh-new.c' || echo '$(srcdir)/'`gnunet-service-mesh-new.c + +gnunet_service_mesh_new-gnunet-service-mesh-new.obj: gnunet-service-mesh-new.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_new_CFLAGS) $(CFLAGS) -MT gnunet_service_mesh_new-gnunet-service-mesh-new.obj -MD -MP -MF $(DEPDIR)/gnunet_service_mesh_new-gnunet-service-mesh-new.Tpo -c -o gnunet_service_mesh_new-gnunet-service-mesh-new.obj `if test -f 'gnunet-service-mesh-new.c'; then $(CYGPATH_W) 'gnunet-service-mesh-new.c'; else $(CYGPATH_W) '$(srcdir)/gnunet-service-mesh-new.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gnunet_service_mesh_new-gnunet-service-mesh-new.Tpo $(DEPDIR)/gnunet_service_mesh_new-gnunet-service-mesh-new.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gnunet-service-mesh-new.c' object='gnunet_service_mesh_new-gnunet-service-mesh-new.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_new_CFLAGS) $(CFLAGS) -c -o gnunet_service_mesh_new-gnunet-service-mesh-new.obj `if test -f 'gnunet-service-mesh-new.c'; then $(CYGPATH_W) 'gnunet-service-mesh-new.c'; else $(CYGPATH_W) '$(srcdir)/gnunet-service-mesh-new.c'; fi` + +gnunet_service_mesh_new-mesh_tunnel_tree.o: mesh_tunnel_tree.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_new_CFLAGS) $(CFLAGS) -MT gnunet_service_mesh_new-mesh_tunnel_tree.o -MD -MP -MF $(DEPDIR)/gnunet_service_mesh_new-mesh_tunnel_tree.Tpo -c -o gnunet_service_mesh_new-mesh_tunnel_tree.o `test -f 'mesh_tunnel_tree.c' || echo '$(srcdir)/'`mesh_tunnel_tree.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gnunet_service_mesh_new-mesh_tunnel_tree.Tpo $(DEPDIR)/gnunet_service_mesh_new-mesh_tunnel_tree.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mesh_tunnel_tree.c' object='gnunet_service_mesh_new-mesh_tunnel_tree.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_new_CFLAGS) $(CFLAGS) -c -o gnunet_service_mesh_new-mesh_tunnel_tree.o `test -f 'mesh_tunnel_tree.c' || echo '$(srcdir)/'`mesh_tunnel_tree.c + +gnunet_service_mesh_new-mesh_tunnel_tree.obj: mesh_tunnel_tree.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_new_CFLAGS) $(CFLAGS) -MT gnunet_service_mesh_new-mesh_tunnel_tree.obj -MD -MP -MF $(DEPDIR)/gnunet_service_mesh_new-mesh_tunnel_tree.Tpo -c -o gnunet_service_mesh_new-mesh_tunnel_tree.obj `if test -f 'mesh_tunnel_tree.c'; then $(CYGPATH_W) 'mesh_tunnel_tree.c'; else $(CYGPATH_W) '$(srcdir)/mesh_tunnel_tree.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gnunet_service_mesh_new-mesh_tunnel_tree.Tpo $(DEPDIR)/gnunet_service_mesh_new-mesh_tunnel_tree.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mesh_tunnel_tree.c' object='gnunet_service_mesh_new-mesh_tunnel_tree.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_new_CFLAGS) $(CFLAGS) -c -o gnunet_service_mesh_new-mesh_tunnel_tree.obj `if test -f 'mesh_tunnel_tree.c'; then $(CYGPATH_W) 'mesh_tunnel_tree.c'; else $(CYGPATH_W) '$(srcdir)/mesh_tunnel_tree.c'; fi` + +gnunet_service_mesh_new-mesh_common.o: mesh_common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_new_CFLAGS) $(CFLAGS) -MT gnunet_service_mesh_new-mesh_common.o -MD -MP -MF $(DEPDIR)/gnunet_service_mesh_new-mesh_common.Tpo -c -o gnunet_service_mesh_new-mesh_common.o `test -f 'mesh_common.c' || echo '$(srcdir)/'`mesh_common.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gnunet_service_mesh_new-mesh_common.Tpo $(DEPDIR)/gnunet_service_mesh_new-mesh_common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mesh_common.c' object='gnunet_service_mesh_new-mesh_common.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_new_CFLAGS) $(CFLAGS) -c -o gnunet_service_mesh_new-mesh_common.o `test -f 'mesh_common.c' || echo '$(srcdir)/'`mesh_common.c + +gnunet_service_mesh_new-mesh_common.obj: mesh_common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_new_CFLAGS) $(CFLAGS) -MT gnunet_service_mesh_new-mesh_common.obj -MD -MP -MF $(DEPDIR)/gnunet_service_mesh_new-mesh_common.Tpo -c -o gnunet_service_mesh_new-mesh_common.obj `if test -f 'mesh_common.c'; then $(CYGPATH_W) 'mesh_common.c'; else $(CYGPATH_W) '$(srcdir)/mesh_common.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/gnunet_service_mesh_new-mesh_common.Tpo $(DEPDIR)/gnunet_service_mesh_new-mesh_common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mesh_common.c' object='gnunet_service_mesh_new-mesh_common.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(gnunet_service_mesh_new_CFLAGS) $(CFLAGS) -c -o gnunet_service_mesh_new-mesh_common.obj `if test -f 'mesh_common.c'; then $(CYGPATH_W) 'mesh_common.c'; else $(CYGPATH_W) '$(srcdir)/mesh_common.c'; fi` mostlyclean-libtool: -rm -f *.lo @@ -728,8 +1229,11 @@ 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=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgcfgdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgcfgdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -743,9 +1247,7 @@ 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 + dir='$(DESTDIR)$(pkgcfgdir)'; $(am__uninstall_files_from_dir) ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ @@ -880,14 +1382,15 @@ check-TESTS: $(TESTS) fi; \ dashes=`echo "$$dashes" | sed s/./=/g`; \ if test "$$failed" -eq 0; then \ - echo "$$grn$$dashes"; \ + col="$$grn"; \ else \ - echo "$$red$$dashes"; \ + col="$$red"; \ fi; \ - echo "$$banner"; \ - test -z "$$skipped" || echo "$$skipped"; \ - test -z "$$report" || echo "$$report"; \ - echo "$$dashes$$std"; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ test "$$failed" -eq 0; \ else :; fi @@ -925,11 +1428,11 @@ check-am: all-am $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: check-am -all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(DATA) +all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) $(PROGRAMS) $(DATA) install-binPROGRAMS: install-libLTLIBRARIES installdirs: - for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgcfgdir)"; do \ + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(plugindir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(pkgcfgdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am @@ -942,10 +1445,15 @@ install-am: all-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 + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: @@ -960,7 +1468,8 @@ maintainer-clean-generic: clean: clean-am clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \ - clean-libLTLIBRARIES clean-libtool mostlyclean-am + clean-libLTLIBRARIES clean-libexecPROGRAMS clean-libtool \ + clean-noinstLIBRARIES clean-pluginLTLIBRARIES mostlyclean-am distclean: distclean-am -rm -rf ./$(DEPDIR) @@ -980,13 +1489,14 @@ info: info-am info-am: -install-data-am: install-pkgcfgDATA +install-data-am: install-pkgcfgDATA install-pluginLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: -install-exec-am: install-binPROGRAMS install-libLTLIBRARIES +install-exec-am: install-binPROGRAMS install-libLTLIBRARIES \ + install-libexecPROGRAMS install-html: install-html-am @@ -1027,26 +1537,30 @@ ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-libLTLIBRARIES \ - uninstall-pkgcfgDATA + uninstall-libexecPROGRAMS uninstall-pkgcfgDATA \ + uninstall-pluginLTLIBRARIES .MAKE: check-am install-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 \ + clean-libLTLIBRARIES clean-libexecPROGRAMS clean-libtool \ + clean-noinstLIBRARIES clean-pluginLTLIBRARIES 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-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-info-am install-libLTLIBRARIES install-libexecPROGRAMS \ + install-man install-pdf install-pdf-am install-pkgcfgDATA \ + install-pluginLTLIBRARIES 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 + uninstall-libexecPROGRAMS uninstall-pkgcfgDATA \ + uninstall-pluginLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/src/mesh/gnunet-mesh.c b/src/mesh/gnunet-mesh.c new file mode 100644 index 0000000..21a3f7d --- /dev/null +++ b/src/mesh/gnunet-mesh.c @@ -0,0 +1,235 @@ +/* + 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 mesh/gnunet-mesh.c + * @brief Print information about mesh tunnels and peers. + * @author Bartlomiej Polot + */ +#include "platform.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_mesh_service.h" +#include "gnunet_program_lib.h" + + +/** + * Option -m. + */ +static int monitor_connections; + +/** + * Option -t + */ +static char *tunnel_id; + +/** + * Mesh handle. + */ +static struct GNUNET_MESH_Handle *mh; + +/** + * Shutdown task handle. + */ +GNUNET_SCHEDULER_TaskIdentifier sd; + +/** + * Task run in monitor mode when the user presses CTRL-C to abort. + * Stops monitoring activity. + * + * @param cls Closure (unused). + * @param tc scheduler context + */ +static void +shutdown_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (NULL != mh) + { + GNUNET_MESH_disconnect (mh); + mh = NULL; + } +} + + +/** + * Method called to retrieve information about each tunnel the mesh peer + * is aware of. + * + * @param cls Closure (unused). + * @param initiator Peer that started the tunnel (owner). + * @param tunnel_number Tunnel number. + * @param peers Array of peer identities that participate in the tunnel. + * @param npeers Number of peers in peers. + */ +static void +tunnels_callback (void *cls, + const struct GNUNET_PeerIdentity *initiator, + unsigned int tunnel_number, + const struct GNUNET_PeerIdentity *peers, + unsigned int npeers) +{ + unsigned int i; + + fprintf (stdout, "Tunnel %s [%u]: %u peers\n", + GNUNET_i2s_full (initiator), tunnel_number, npeers); + for (i = 0; i < npeers; i++) + fprintf (stdout, " * %s\n", GNUNET_i2s_full (&peers[i])); + fprintf (stdout, "\n"); +} + + +/** + * Method called to retrieve information about each tunnel the mesh peer + * is aware of. + * + * @param cls Closure. + * @param peer Peer in the tunnel's tree. + * @param parent Parent of the current peer. All 0 when peer is root. + * + */ +static void +tunnel_callback (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_PeerIdentity *parent) +{ +} + + +/** + * Call MESH's monitor API, get all tunnels known to peer. + * + * @param cls Closure (unused). + * @param tc TaskContext + */ +static void +get_tunnels (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + { + return; + } + GNUNET_MESH_get_tunnels (mh, &tunnels_callback, NULL); + if (GNUNET_YES != monitor_connections) + { + GNUNET_SCHEDULER_shutdown(); + } +} + +/** + * Call MESH's monitor API, get info of one tunnel. + * + * @param cls Closure (unused). + * @param tc TaskContext + */ +static void +show_tunnel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_PeerIdentity pid; + + if (GNUNET_OK != + GNUNET_CRYPTO_hash_from_string (tunnel_id, &pid.hashPubKey)) + { + GNUNET_SCHEDULER_shutdown(); + return; + } + GNUNET_MESH_show_tunnel (mh, &pid, 0, tunnel_callback, NULL); +} + + +/** + * 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) +{ + static const struct GNUNET_MESH_MessageHandler handlers[] = { + {NULL, 0, 0} /* FIXME add option to monitor msg types */ + }; + GNUNET_MESH_ApplicationType apps = 0; /* FIXME add option to monitor apps */ + + if (args[0] != NULL) + { + FPRINTF (stderr, _("Invalid command line argument `%s'\n"), args[0]); + return; + } + mh = GNUNET_MESH_connect (cfg, + NULL, /* cls */ + NULL, /* nt */ + NULL, /* cleaner */ + handlers, + &apps); + if (NULL == mh) + GNUNET_SCHEDULER_add_now (shutdown_task, NULL); + else + sd = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, + shutdown_task, NULL); + + if (NULL != tunnel_id) + GNUNET_SCHEDULER_add_now (&show_tunnel, NULL); + else + GNUNET_SCHEDULER_add_now (&get_tunnels, NULL); +} + + +/** + * The main function to obtain peer information. + * + * @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) +{ + int res; + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + {'m', "monitor", NULL, + gettext_noop ("provide information about all tunnels (continuously) NOT IMPLEMENTED"), /* FIXME */ + GNUNET_NO, &GNUNET_GETOPT_set_one, &monitor_connections}, + {'t', "tunnel", "OWNER_ID:TUNNEL_ID", + gettext_noop ("provide information about a particular tunnel"), + GNUNET_YES, &GNUNET_GETOPT_set_string, &tunnel_id}, + GNUNET_GETOPT_OPTION_END + }; + + if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) + return 2; + + res = GNUNET_PROGRAM_run (argc, argv, "gnunet-mesh", + gettext_noop + ("Print information about mesh tunnels and peers."), + options, &run, NULL); + + GNUNET_free ((void *) argv); + + if (GNUNET_OK == res) + return 0; + else + return 1; +} + +/* end of gnunet-mesh.c */ diff --git a/src/mesh/gnunet-service-mesh-new.c b/src/mesh/gnunet-service-mesh-new.c new file mode 100644 index 0000000..82bfc5e --- /dev/null +++ b/src/mesh/gnunet-service-mesh-new.c @@ -0,0 +1,8483 @@ +/* + This file is part of GNUnet. + (C) 2001-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 mesh/gnunet-service-mesh.c + * @brief GNUnet MESH service + * @author Bartlomiej Polot + * + * STRUCTURE: + * - DATA STRUCTURES + * - GLOBAL VARIABLES + * - GENERAL HELPERS + * - PERIODIC FUNCTIONS + * - MESH NETWORK HANDLER HELPERS + * - MESH NETWORK HANDLES + * - MESH LOCAL HANDLER HELPERS + * - MESH LOCAL HANDLES + * - MAIN FUNCTIONS (main & run) + * + * TODO: + * - error reporting (CREATE/CHANGE/ADD/DEL?) -- new message! + * - partial disconnect reporting -- same as error reporting? + * - add ping message + * - relay corking down to core + * - set ttl relative to tree depth + * - Add data ACK count in path ACK + * - Make common GNUNET_MESH_Data header for unicast, to_orig, multicast + * TODO END + */ + +#include "platform.h" +#include "mesh.h" +#include "mesh_protocol.h" +#include "mesh_tunnel_tree.h" +#include "block_mesh.h" +#include "gnunet_dht_service.h" +#include "gnunet_statistics_service.h" +#include "gnunet_regex_lib.h" + +#define MESH_BLOOM_SIZE 128 + +#define MESH_DEBUG_DHT GNUNET_NO +#define MESH_DEBUG_CONNECTION GNUNET_NO +#define MESH_DEBUG_TIMING __LINUX__ && GNUNET_NO + +#define MESH_MAX_POLL_TIME GNUNET_TIME_relative_multiply (\ + GNUNET_TIME_UNIT_MINUTES,\ + 10) + +#if MESH_DEBUG_CONNECTION +#define DEBUG_CONN(...) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__) +#else +#define DEBUG_CONN(...) +#endif + +#if MESH_DEBUG_DHT +#define DEBUG_DHT(...) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__) +#else +#define DEBUG_DHT(...) +#endif + +#if MESH_DEBUG_TIMING +#include <time.h> +double __sum; +uint64_t __count; +struct timespec __mesh_start; +struct timespec __mesh_end; +#define INTERVAL_START clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &(__mesh_start)) +#define INTERVAL_END \ +do {\ + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &(__mesh_end));\ + double __diff = __mesh_end.tv_nsec - __mesh_start.tv_nsec;\ + if (__diff < 0) __diff += 1000000000;\ + __sum += __diff;\ + __count++;\ +} while (0) +#define INTERVAL_SHOW \ +if (0 < __count)\ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "AVG process time: %f ns\n", __sum/__count) +#else +#define INTERVAL_START +#define INTERVAL_END +#define INTERVAL_SHOW +#endif + +/******************************************************************************/ +/************************ DATA STRUCTURES ****************************/ +/******************************************************************************/ + +/** FWD declaration */ +struct MeshPeerInfo; +struct MeshClient; + + +/** + * Struct representing a piece of data being sent to other peers + */ +struct MeshData +{ + /** Tunnel it belongs to. */ + struct MeshTunnel *t; + + /** How many remaining neighbors still hav't got it. */ + unsigned int reference_counter; + + /** How many remaining neighbors we need to send this to. */ + unsigned int total_out; + + /** Size of the data. */ + size_t data_len; + + /** Data itself */ + void *data; +}; + + +/** + * Struct containing info about a queued transmission to this peer + */ +struct MeshPeerQueue +{ + /** + * DLL next + */ + struct MeshPeerQueue *next; + + /** + * DLL previous + */ + struct MeshPeerQueue *prev; + + /** + * Peer this transmission is directed to. + */ + struct MeshPeerInfo *peer; + + /** + * Tunnel this message belongs to. + */ + struct MeshTunnel *tunnel; + + /** + * Pointer to info stucture used as cls. + */ + void *cls; + + /** + * Type of message + */ + uint16_t type; + + /** + * Size of the message + */ + size_t size; +}; + + +/** + * Struct to store regex information announced by clients. + */ +struct MeshRegexDescriptor +{ + /** + * Regular expression itself. + */ + char *regex; + + /** + * How many characters per edge can we squeeze? + */ + uint16_t compression; + + /** + * Handle to announce the regex. + */ + struct GNUNET_REGEX_announce_handle *h; +}; + + +/** + * Struct to keep information of searches of services described by a regex + * using a user-provided string service description. + */ +struct MeshRegexSearchInfo +{ + /** + * Which tunnel is this for + */ + struct MeshTunnel *t; + + /** + * User provided description of the searched service. + */ + char *description; + + /** + * Regex search handle. + */ + struct GNUNET_REGEX_search_handle *search_handle; + + /** + * Peer that is connecting via connect_by_string. When connected, free ctx. + */ + GNUNET_PEER_Id peer; + + /** + * Other peers that are found but not yet being connected to. + */ + GNUNET_PEER_Id *peers; + + /** + * Number of elements in peers. + */ + unsigned int n_peers; + + /** + * Next peer to try to connect to. + */ + unsigned int i_peer; + + /** + * Timeout for a connect attempt. + * When reached, try to connect to a different peer, if any. If not, + * try the same peer again. + */ + GNUNET_SCHEDULER_TaskIdentifier timeout; + +}; + + +/** + * Struct containing all info possibly needed to build a package when called + * back by core. + */ +struct MeshTransmissionDescriptor +{ + /** ID of the tunnel this packet travels in */ + struct MESH_TunnelID *origin; + + /** Who was this message being sent to */ + struct MeshPeerInfo *peer; + + /** Ultimate destination of the packet */ + GNUNET_PEER_Id destination; + + /** Data descriptor */ + struct MeshData* mesh_data; +}; + + +/** + * Struct containing all information regarding a given peer + */ +struct MeshPeerInfo +{ + /** + * ID of the peer + */ + GNUNET_PEER_Id id; + + /** + * Last time we heard from this peer + */ + struct GNUNET_TIME_Absolute last_contact; + + /** + * Task handler for delayed connect task; + */ + GNUNET_SCHEDULER_TaskIdentifier connect_task; + + /** + * Number of attempts to reconnect so far + */ + int n_reconnect_attempts; + + /** + * Paths to reach the peer, ordered by ascending hop count + */ + struct MeshPeerPath *path_head; + + /** + * Paths to reach the peer, ordered by ascending hop count + */ + struct MeshPeerPath *path_tail; + + /** + * Handle to stop the DHT search for a path to this peer + */ + struct GNUNET_DHT_GetHandle *dhtget; + + /** + * Closure given to the DHT GET + */ + struct MeshPathInfo *dhtgetcls; + + /** + * Array of tunnels this peer participates in + * (most probably a small amount, therefore not a hashmap) + * When the path to the peer changes, notify these tunnels to let them + * re-adjust their path trees. + */ + struct MeshTunnel **tunnels; + + /** + * Number of tunnels this peers participates in + */ + unsigned int ntunnels; + + /** + * Transmission queue to core DLL head + */ + struct MeshPeerQueue *queue_head; + + /** + * Transmission queue to core DLL tail + */ + struct MeshPeerQueue *queue_tail; + + /** + * How many messages are in the queue to this peer. + */ + unsigned int queue_n; + + /** + * Handle to for queued transmissions + */ + struct GNUNET_CORE_TransmitHandle *core_transmit; +}; + + +/** + * Globally unique tunnel identification (owner + number) + * DO NOT USE OVER THE NETWORK + */ +struct MESH_TunnelID +{ + /** + * Node that owns the tunnel + */ + GNUNET_PEER_Id oid; + + /** + * Tunnel number to differentiate all the tunnels owned by the node oid + * ( tid < GNUNET_MESH_LOCAL_TUNNEL_ID_CLI ) + */ + MESH_TunnelNumber tid; +}; + + +/** + * Struct containing all information regarding a tunnel + * For an intermediate node the improtant info used will be: + * - id Tunnel unique identification + * - paths[0] To know where to send it next + * - metainfo: ready, speeds, accounting + */ +struct MeshTunnel +{ + /** + * Tunnel ID + */ + struct MESH_TunnelID id; + + /** + * Local tunnel number ( >= GNUNET_MESH_LOCAL_TUNNEL_ID_CLI or 0 ) + */ + MESH_TunnelNumber local_tid; + + /** + * Local tunnel number for local destination clients (incoming number) + * ( >= GNUNET_MESH_LOCAL_TUNNEL_ID_SERV or 0). All clients share the same + * number. + */ + MESH_TunnelNumber local_tid_dest; + + /** + * Is the speed on the tunnel limited to the slowest peer? + */ + int speed_min; + + /** + * Is the tunnel bufferless (minimum latency)? + */ + int nobuffer; + + /** + * Packet ID of the last fwd packet seen (sent/retransmitted/received). + */ + uint32_t fwd_pid; + + /** + * Packet ID of the last bck packet sent (unique counter per hop). + */ + uint32_t bck_pid; + + /** + * SKIP value for this tunnel. + */ + uint32_t skip; + + /** + * Force sending ACK? Flag to allow duplicate ACK on POLL. + */ + int force_ack; + + /** + * MeshTunnelChildInfo of all children, indexed by GNUNET_PEER_Id. + * Contains the Flow Control info: FWD ACK value received, + * last BCK ACK sent, PID and SKIP values. + */ + struct GNUNET_CONTAINER_MultiHashMap *children_fc; + + /** + * Last ACK sent towards the origin (for traffic towards leaf node). + */ + uint32_t last_fwd_ack; + + /** + * BCK ACK value received from the hop towards the owner of the tunnel, + * (previous node / owner): up to what message PID can we sent back to him. + */ + uint32_t bck_ack; + + /** + * How many messages are in the forward queue (towards leaves). + */ + unsigned int fwd_queue_n; + + /** + * How many messages do we accept in the forward queue. + */ + unsigned int fwd_queue_max; + + /** + * How many messages are in the backward queue (towards origin). + */ + unsigned int bck_queue_n; + + /** + * How many messages do we accept in the backward queue. + */ + unsigned int bck_queue_max; + + /** + * Task to poll peer in case of a stall. + */ + GNUNET_SCHEDULER_TaskIdentifier fc_poll_bck; + + /** + * Last time the tunnel was used + */ + struct GNUNET_TIME_Absolute timestamp; + + /** + * Peers in the tunnel, indexed by PeerIdentity -> (MeshPeerInfo) + * containing peers added by id or by type, not intermediate peers. + */ + struct GNUNET_CONTAINER_MultiHashMap *peers; + + /** + * Number of peers that are connected and potentially ready to receive data + */ + unsigned int peers_ready; + + /** + * Number of peers that have been added to the tunnel + */ + unsigned int peers_total; + + /** + * Client owner of the tunnel, if any + */ + struct MeshClient *owner; + + /** + * Clients that have been informed about and want to stay in the tunnel. + */ + struct MeshClient **clients; + + /** + * Flow control info for each client. + */ + struct MeshTunnelClientInfo *clients_fc; + + /** + * Number of elements in clients/clients_fc + */ + unsigned int nclients; + + /** + * Clients that have been informed but requested to leave the tunnel. + */ + struct MeshClient **ignore; + + /** + * Number of elements in clients + */ + unsigned int nignore; + + /** + * Blacklisted peers + */ + GNUNET_PEER_Id *blacklisted; + + /** + * Number of elements in blacklisted + */ + unsigned int nblacklisted; + + /** + * Bloomfilter (for peer identities) to stop circular routes + */ + char bloomfilter[MESH_BLOOM_SIZE]; + + /** + * Tunnel paths + */ + struct MeshTunnelTree *tree; + + /** + * Application type we are looking for in this tunnel + */ + GNUNET_MESH_ApplicationType type; + + /** + * Used to search peers offering a service + */ + struct GNUNET_DHT_GetHandle *dht_get_type; + + /** + * Handle for the regex search for a connect_by_string + */ + struct MeshRegexSearchInfo *regex_search; + + /** + * Task to keep the used paths alive + */ + GNUNET_SCHEDULER_TaskIdentifier path_refresh_task; + + /** + * Task to destroy the tunnel after timeout + * + * FIXME: merge the two? a tunnel will have either + * a path refresh OR a timeout, never both! + */ + GNUNET_SCHEDULER_TaskIdentifier timeout_task; + + /** + * Flag to signal the destruction of the tunnel. + * If this is set GNUNET_YES the tunnel will be destroyed + * when the queue is empty. + */ + int destroy; + + /** + * Total messages pending for this tunnels, payload or not. + */ + unsigned int pending_messages; + + /** + * If the tunnel is empty, destoy it. + */ + GNUNET_SCHEDULER_TaskIdentifier delayed_destroy; +}; + + +/** + * Info about a child node in a tunnel, needed to perform flow control. + */ +struct MeshTunnelChildInfo +{ + /** + * ID of the child node. + */ + GNUNET_PEER_Id id; + + /** + * SKIP value. + */ + uint32_t skip; + + /** + * Last sent PID. + */ + uint32_t fwd_pid; + + /** + * Last received PID. + */ + uint32_t bck_pid; + + /** + * Maximum PID allowed (FWD ACK received). + */ + uint32_t fwd_ack; + + /** + * Last ACK sent to that child (BCK ACK). + */ + uint32_t bck_ack; + + /** + * Circular buffer pointing to MeshPeerQueue elements for all + * payload traffic going to this child. + * Size determined by the tunnel queue size (@c t->fwd_queue_max). + */ + struct MeshPeerQueue **send_buffer; + + /** + * Index of the oldest element in the send_buffer. + */ + unsigned int send_buffer_start; + + /** + * How many elements are already in the buffer. + */ + unsigned int send_buffer_n; + + /** + * Tunnel this info is about + */ + struct MeshTunnel *t; + + /** + * Task to poll peer in case of a stall. + */ + GNUNET_SCHEDULER_TaskIdentifier fc_poll; + + /** + * Time to use for next polling call. + */ + struct GNUNET_TIME_Relative fc_poll_time; +}; + + +/** + * Info about a leaf client of a tunnel, needed to perform flow control. + */ +struct MeshTunnelClientInfo +{ + /** + * PID of the last packet sent to the client (FWD). + */ + uint32_t fwd_pid; + + /** + * PID of the last packet received from the client (BCK). + */ + uint32_t bck_pid; + + /** + * Maximum PID allowed (FWD ACK received). + */ + uint32_t fwd_ack; + + /** + * Last ACK sent to that child (BCK ACK). + */ + uint32_t bck_ack; +}; + + + +/** + * Info collected during iteration of child nodes in order to get the ACK value + * for a tunnel. + */ +struct MeshTunnelChildIteratorContext +{ + /** + * Tunnel whose info is being collected. + */ + struct MeshTunnel *t; + + /** + * Is this context initialized? Is the value in max_child_ack valid? + */ + int init; + + /** + * Maximum child ACK so far. + */ + uint32_t max_child_ack; + + /** + * Number of children nodes + */ + unsigned int nchildren; +}; + + +/** + * Info needed to work with tunnel paths and peers + */ +struct MeshPathInfo +{ + /** + * Tunnel + */ + struct MeshTunnel *t; + + /** + * Neighbouring peer to whom we send the packet to + */ + struct MeshPeerInfo *peer; + + /** + * Path itself + */ + struct MeshPeerPath *path; +}; + + +/** + * Struct containing information about a client of the service + */ +struct MeshClient +{ + /** + * Linked list next + */ + struct MeshClient *next; + + /** + * Linked list prev + */ + struct MeshClient *prev; + + /** + * Tunnels that belong to this client, indexed by local id + */ + struct GNUNET_CONTAINER_MultiHashMap *own_tunnels; + + /** + * Tunnels this client has accepted, indexed by incoming local id + */ + struct GNUNET_CONTAINER_MultiHashMap *incoming_tunnels; + + /** + * Tunnels this client has rejected, indexed by incoming local id + */ + struct GNUNET_CONTAINER_MultiHashMap *ignore_tunnels; + /** + * Handle to communicate with the client + */ + struct GNUNET_SERVER_Client *handle; + + /** + * Applications that this client has claimed to provide + */ + struct GNUNET_CONTAINER_MultiHashMap *apps; + + /** + * Messages that this client has declared interest in + */ + struct GNUNET_CONTAINER_MultiHashMap *types; + + /** + * Whether the client is active or shutting down (don't send confirmations + * to a client that is shutting down. + */ + int shutting_down; + + /** + * ID of the client, mainly for debug messages + */ + unsigned int id; + + /** + * Regular expressions describing the services offered by this client. + */ + struct MeshRegexDescriptor *regexes; // FIXME regex add timeout? API to remove a regex? + + /** + * Number of regular expressions in regexes. + */ + unsigned int n_regex; + + /** + * Task to refresh all regular expresions in the DHT. + */ + GNUNET_SCHEDULER_TaskIdentifier regex_announce_task; + + /** + * Tmp store for partially retrieved regex. + */ + char *partial_regex; + +}; + + +/******************************************************************************/ +/************************ DEBUG FUNCTIONS ****************************/ +/******************************************************************************/ + +#if MESH_DEBUG +/** + * GNUNET_SCHEDULER_Task for printing a message after some operation is done + * @param cls string to print + * @param success GNUNET_OK if the PUT was transmitted, + * GNUNET_NO on timeout, + * GNUNET_SYSERR on disconnect from service + * after the PUT message was transmitted + * (so we don't know if it was received or not) + */ + +#if 0 +static void +mesh_debug (void *cls, int success) +{ + char *s = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s (%d)\n", s, success); +} +#endif + +unsigned int debug_fwd_ack; +unsigned int debug_bck_ack; + +#endif + +/******************************************************************************/ +/*********************** GLOBAL VARIABLES ****************************/ +/******************************************************************************/ + +/************************** Configuration parameters **************************/ + +/** + * How often to send tunnel keepalives. Tunnels timeout after 4 missed. + */ +static struct GNUNET_TIME_Relative refresh_path_time; + +/** + * How often to PUT local application numbers in the DHT. + */ +static struct GNUNET_TIME_Relative app_announce_time; + +/** + * How often to PUT own ID in the DHT. + */ +static struct GNUNET_TIME_Relative id_announce_time; + +/** + * Maximum time allowed to connect to a peer found by string. + */ +static struct GNUNET_TIME_Relative connect_timeout; + +/** + * Default TTL for payload packets. + */ +static unsigned long long default_ttl; + +/** + * DHT replication level, see DHT API: GNUNET_DHT_get_start, GNUNET_DHT_put. + */ +static unsigned long long dht_replication_level; + +/** + * How many tunnels are we willing to maintain. + * Local tunnels are always allowed, even if there are more tunnels than max. + */ +static unsigned long long max_tunnels; + +/** + * How many messages *in total* are we willing to queue, divided by number of + * tunnels to get tunnel queue size. + */ +static unsigned long long max_msgs_queue; + +/** + * How many peers do we want to remember? + */ +static unsigned long long max_peers; + + +/*************************** Static global variables **************************/ + +/** + * Hostkey generation context + */ +static struct GNUNET_CRYPTO_RsaKeyGenerationContext *keygen; + +/** + * DLL with all the clients, head. + */ +static struct MeshClient *clients; + +/** + * DLL with all the clients, tail. + */ +static struct MeshClient *clients_tail; + +/** + * Tunnels known, indexed by MESH_TunnelID (MeshTunnel). + */ +static struct GNUNET_CONTAINER_MultiHashMap *tunnels; + +/** + * Number of tunnels known. + */ +static unsigned long long n_tunnels; + +/** + * Tunnels incoming, indexed by MESH_TunnelNumber + * (which is greater than GNUNET_MESH_LOCAL_TUNNEL_ID_SERV). + */ +static struct GNUNET_CONTAINER_MultiHashMap *incoming_tunnels; + +/** + * Peers known, indexed by PeerIdentity (MeshPeerInfo). + */ +static struct GNUNET_CONTAINER_MultiHashMap *peers; + +/* + * Handle to communicate with transport + */ +// static struct GNUNET_TRANSPORT_Handle *transport_handle; + +/** + * Handle to communicate with core. + */ +static struct GNUNET_CORE_Handle *core_handle; + +/** + * Handle to use DHT. + */ +static struct GNUNET_DHT_Handle *dht_handle; + +/** + * Handle to server. + */ +static struct GNUNET_SERVER_Handle *server_handle; + +/** + * Handle to the statistics service. + */ +static struct GNUNET_STATISTICS_Handle *stats; + +/** + * Notification context, to send messages to local clients. + */ +static struct GNUNET_SERVER_NotificationContext *nc; + +/** + * Local peer own ID (memory efficient handle). + */ +static GNUNET_PEER_Id myid; + +/** + * Local peer own ID (full value). + */ +static struct GNUNET_PeerIdentity my_full_id; + +/** + * Own private key. + */ +static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; + +/** + * Own public key. + */ +static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key; + +/** + * Tunnel ID for the next created tunnel (global tunnel number). + */ +static MESH_TunnelNumber next_tid; + +/** + * Tunnel ID for the next incoming tunnel (local tunnel number). + */ +static MESH_TunnelNumber next_local_tid; + +/** + * All application types provided by this peer. + */ +static struct GNUNET_CONTAINER_MultiHashMap *applications; + +/** + * All message types clients of this peer are interested in. + */ +static struct GNUNET_CONTAINER_MultiHashMap *types; + +/** + * Task to periodically announce provided applications. + */ +GNUNET_SCHEDULER_TaskIdentifier announce_applications_task; + +/** + * Task to periodically announce itself in the network. + */ +GNUNET_SCHEDULER_TaskIdentifier announce_id_task; + +/** + * Next ID to assign to a client. + */ +unsigned int next_client_id; + + +/******************************************************************************/ +/*********************** DECLARATIONS **************************/ +/******************************************************************************/ + +/** + * Function to process paths received for a new peer addition. The recorded + * paths form the initial tunnel, which can be optimized later. + * Called on each result obtained for the DHT search. + * + * @param cls closure + * @param exp when will this value expire + * @param key key of the result + * @param type type of the result + * @param size number of bytes in data + * @param data pointer to the result data + */ +static void +dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp, + const struct GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_length, enum GNUNET_BLOCK_Type type, + size_t size, const void *data); + + +/** + * Retrieve the MeshPeerInfo stucture associated with the peer, create one + * and insert it in the appropiate structures if the peer is not known yet. + * + * @param peer Full identity of the peer. + * + * @return Existing or newly created peer info. + */ +static struct MeshPeerInfo * +peer_info_get (const struct GNUNET_PeerIdentity *peer); + + +/** + * Retrieve the MeshPeerInfo stucture associated with the peer, create one + * and insert it in the appropiate structures if the peer is not known yet. + * + * @param peer Short identity of the peer. + * + * @return Existing or newly created peer info. + */ +static struct MeshPeerInfo * +peer_info_get_short (const GNUNET_PEER_Id peer); + + +/** + * Try to establish a new connection to this peer. + * Use the best path for the given tunnel. + * If the peer doesn't have any path to it yet, try to get one. + * If the peer already has some path, send a CREATE PATH towards it. + * + * @param peer PeerInfo of the peer. + * @param t Tunnel for which to create the path, if possible. + */ +static void +peer_info_connect (struct MeshPeerInfo *peer, struct MeshTunnel *t); + + +/** + * Build a PeerPath from the paths returned from the DHT, reversing the paths + * to obtain a local peer -> destination path and interning the peer ids. + * + * @return Newly allocated and created path + */ +static struct MeshPeerPath * +path_build_from_dht (const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_length); + + +/** + * Adds a path to the peer_infos of all the peers in the path + * + * @param p Path to process. + * @param confirmed Whether we know if the path works or not. + */ +static void +path_add_to_peers (struct MeshPeerPath *p, int confirmed); + + +/** + * Add a peer to a tunnel, accomodating paths accordingly and initializing all + * needed rescources. + * If peer already exists, reevaluate shortest path and change if different. + * + * @param t Tunnel we want to add a new peer to + * @param peer PeerInfo of the peer being added + * + */ +static void +tunnel_add_peer (struct MeshTunnel *t, struct MeshPeerInfo *peer); + + +/** + * Removes an explicit path from a tunnel, freeing all intermediate nodes + * that are no longer needed, as well as nodes of no longer reachable peers. + * The tunnel itself is also destoyed if results in a remote empty tunnel. + * + * @param t Tunnel from which to remove the path. + * @param peer Short id of the peer which should be removed. + */ +static void +tunnel_delete_peer (struct MeshTunnel *t, GNUNET_PEER_Id peer); + + +/** + * Search for a tunnel by global ID using full PeerIdentities. + * + * @param oid owner of the tunnel. + * @param tid global tunnel number. + * + * @return tunnel handler, NULL if doesn't exist. + */ +static struct MeshTunnel * +tunnel_get (const struct GNUNET_PeerIdentity *oid, MESH_TunnelNumber tid); + + +/** + * Delete an active client from the tunnel. + * + * @param t Tunnel. + * @param c Client. + */ +static void +tunnel_delete_active_client (struct MeshTunnel *t, const struct MeshClient *c); + +/** + * Notify a tunnel that a connection has broken that affects at least + * some of its peers. + * + * @param t Tunnel affected. + * @param p1 Peer that got disconnected from p2. + * @param p2 Peer that got disconnected from p1. + * + * @return Short ID of the peer disconnected (either p1 or p2). + * 0 if the tunnel remained unaffected. + */ +static GNUNET_PEER_Id +tunnel_notify_connection_broken (struct MeshTunnel *t, GNUNET_PEER_Id p1, + GNUNET_PEER_Id p2); + + +/** + * Get the current ack value for a tunnel, for data going from root to leaves, + * taking in account the tunnel mode and the status of all children and clients. + * + * @param t Tunnel. + * + * @return Maximum PID allowed. + */ +static uint32_t +tunnel_get_fwd_ack (struct MeshTunnel *t); + + +/** + * Add a client to a tunnel, initializing all needed data structures. + * + * @param t Tunnel to which add the client. + * @param c Client which to add to the tunnel. + */ +static void +tunnel_add_client (struct MeshTunnel *t, struct MeshClient *c); + + +/** + * @brief Queue and pass message to core when possible. + * + * If type is payload (UNICAST, TO_ORIGIN, MULTICAST) checks for queue status + * and accounts for it. In case the queue is full, the message is dropped and + * a break issued. + * + * Otherwise, message is treated as internal and allowed to go regardless of + * queue status. + * + * @param cls Closure (@c type dependant). It will be used by queue_send to + * build the message to be sent if not already prebuilt. + * @param type Type of the message, 0 for a raw message. + * @param size Size of the message. + * @param dst Neighbor to send message to. + * @param t Tunnel this message belongs to. + */ +static void +queue_add (void *cls, uint16_t type, size_t size, + struct MeshPeerInfo *dst, struct MeshTunnel *t); + + +/** + * Free a transmission that was already queued with all resources + * associated to the request. + * + * @param queue Queue handler to cancel. + * @param clear_cls Is it necessary to free associated cls? + */ +static void +queue_destroy (struct MeshPeerQueue *queue, int clear_cls); + + +/** + * @brief Get the next transmittable message from the queue. + * + * This will be the head, except in the case of being a data packet + * not allowed by the destination peer. + * + * @param peer Destination peer. + * + * @return The next viable MeshPeerQueue element to send to that peer. + * NULL when there are no transmittable messages. + */ +struct MeshPeerQueue * +queue_get_next (const struct MeshPeerInfo *peer); + + +/** + * Core callback to write a queued packet to core buffer + * + * @param cls Closure (peer info). + * @param size Number of bytes available in buf. + * @param buf Where the to write the message. + * + * @return number of bytes written to buf + */ +static size_t +queue_send (void *cls, size_t size, void *buf); + +/******************************************************************************/ +/************************ REGEX INTEGRATION ****************************/ +/******************************************************************************/ + +/** + * Cancel a mesh regex search and free resources. + */ +static void +regex_cancel_search (struct MeshRegexSearchInfo *regex_search) +{ + GNUNET_REGEX_search_cancel (regex_search->search_handle); + if (0 < regex_search->n_peers) + GNUNET_free (regex_search->peers); + if (GNUNET_SCHEDULER_NO_TASK != regex_search->timeout) + { + GNUNET_SCHEDULER_cancel(regex_search->timeout); + } + GNUNET_free (regex_search); +} + + +/** + * Function called if the connect attempt to a peer found via + * connect_by_string times out. Try to connect to another peer, if any. + * Otherwise try to reconnect to the same peer. + * + * @param cls Closure (info about regex search). + * @param tc TaskContext. + */ +static void +regex_connect_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct MeshRegexSearchInfo *info = cls; + struct MeshPeerInfo *peer_info; + GNUNET_PEER_Id id; + GNUNET_PEER_Id old; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Regex connect timeout\n"); + info->timeout = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " due to shutdown\n"); + return; + } + + old = info->peer; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " timed out: %u\n", old); + + if (0 < info->n_peers) + { + // Select next peer, put current in that spot. + id = info->peers[info->i_peer]; + info->peers[info->i_peer] = info->peer; + info->i_peer = (info->i_peer + 1) % info->n_peers; + } + else + { + // Try to connect to same peer again. + id = info->peer; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " trying: %u\n", id); + + peer_info = peer_info_get_short(id); + tunnel_add_peer (info->t, peer_info); + if (old != id) + tunnel_delete_peer (info->t, old); + peer_info_connect (peer_info, info->t); + info->timeout = GNUNET_SCHEDULER_add_delayed (connect_timeout, + ®ex_connect_timeout, + info); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Regex connect timeout END\n"); +} + + +/** + * Function to process DHT string to regex matching. + * Called on each result obtained for the DHT search. + * + * @param cls Closure provided in GNUNET_REGEX_search. + * @param id Peer providing a regex that matches the string. + * @param get_path Path of the get request. + * @param get_path_length Lenght of get_path. + * @param put_path Path of the put request. + * @param put_path_length Length of the put_path. + */ +static void +regex_found_handler (void *cls, + const struct GNUNET_PeerIdentity *id, + const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_length) +{ + struct MeshRegexSearchInfo *info = cls; + struct MeshPeerPath *p; + struct MeshPeerInfo *peer_info; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got regex results from DHT!\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " for %s\n", info->description); + + peer_info = peer_info_get (id); + p = path_build_from_dht (get_path, get_path_length, + put_path, put_path_length); + path_add_to_peers (p, GNUNET_NO); + path_destroy(p); + + tunnel_add_peer (info->t, peer_info); + peer_info_connect (peer_info, info->t); + if (0 == info->peer) + { + info->peer = peer_info->id; + } + else + { + GNUNET_array_append (info->peers, info->n_peers, peer_info->id); + } + + if (GNUNET_SCHEDULER_NO_TASK != info->timeout) + return; + + info->timeout = GNUNET_SCHEDULER_add_delayed (connect_timeout, + ®ex_connect_timeout, + info); + + return; +} + + +/** + * Store the regular expression describing a local service into the DHT. + * + * @param regex The regular expresion. + */ +static void +regex_put (struct MeshRegexDescriptor *regex) +{ + DEBUG_DHT (" regex_put (%s) start\n", regex->regex); + if (NULL == regex->h) + { + DEBUG_DHT (" first put, creating DFA\n"); + regex->h = GNUNET_REGEX_announce (dht_handle, + &my_full_id, + regex->regex, + regex->compression, + stats); + } + else + { + DEBUG_DHT (" not first put, using cached data\n"); + GNUNET_REGEX_reannounce (regex->h); + } + DEBUG_DHT (" regex_put (%s) end\n", regex->regex); +} + + +/** + * Periodically announce what applications are provided by local clients + * (by regex) + * + * @param cls closure + * @param tc task context + */ +static void +regex_announce (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct MeshClient *c = cls; + unsigned int i; + + c->regex_announce_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + DEBUG_DHT ("Starting PUT for regex\n"); + for (i = 0; i < c->n_regex; i++) + regex_put (&c->regexes[i]); + c->regex_announce_task = GNUNET_SCHEDULER_add_delayed (app_announce_time, + ®ex_announce, + cls); + DEBUG_DHT ("Finished PUT for regex\n"); +} + + +/******************************************************************************/ +/************************ PERIODIC FUNCTIONS ****************************/ +/******************************************************************************/ + +/** + * Announce iterator over for each application provided by the peer + * + * @param cls closure + * @param key current key code + * @param value value in the hash map + * @return GNUNET_YES if we should continue to + * iterate, + * GNUNET_NO if not. + */ +static int +announce_application (void *cls, const struct GNUNET_HashCode * key, void *value) +{ + struct PBlock block; + struct MeshClient *c; + + block.id = my_full_id; + c = GNUNET_CONTAINER_multihashmap_get (applications, key); + GNUNET_assert(NULL != c); + block.type = (long) GNUNET_CONTAINER_multihashmap_get (c->apps, key); + if (0 == block.type) + { + GNUNET_break(0); + return GNUNET_YES; + } + block.type = htonl (block.type); + + GNUNET_break (NULL != + GNUNET_DHT_put (dht_handle, key, + dht_replication_level, + GNUNET_DHT_RO_RECORD_ROUTE | + GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, + GNUNET_BLOCK_TYPE_MESH_PEER_BY_TYPE, + sizeof (block), + (const char *) &block, + GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS), /* FIXME: this should be an option */ + app_announce_time, NULL, NULL)); + return GNUNET_OK; +} + + +/** + * Periodically announce what applications are provided by local clients + * (by type) + * + * @param cls closure + * @param tc task context + */ +static void +announce_applications (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + { + announce_applications_task = GNUNET_SCHEDULER_NO_TASK; + return; + } + + DEBUG_DHT ("Starting PUT for apps\n"); + + GNUNET_CONTAINER_multihashmap_iterate (applications, &announce_application, + NULL); + announce_applications_task = + GNUNET_SCHEDULER_add_delayed (app_announce_time, &announce_applications, + cls); + DEBUG_DHT ("Finished PUT for apps\n"); +} + + +/** + * Periodically announce self id in the DHT + * + * @param cls closure + * @param tc task context + */ +static void +announce_id (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct PBlock block; + + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + { + announce_id_task = GNUNET_SCHEDULER_NO_TASK; + return; + } + /* TODO + * - Set data expiration in function of X + * - Adapt X to churn + */ + DEBUG_DHT ("DHT_put for ID %s started.\n", GNUNET_i2s (&my_full_id)); + + block.id = my_full_id; + block.type = htonl (0); + GNUNET_DHT_put (dht_handle, /* DHT handle */ + &my_full_id.hashPubKey, /* Key to use */ + dht_replication_level, /* Replication level */ + GNUNET_DHT_RO_RECORD_ROUTE | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, /* DHT options */ + GNUNET_BLOCK_TYPE_MESH_PEER, /* Block type */ + sizeof (block), /* Size of the data */ + (const char *) &block, /* Data itself */ + GNUNET_TIME_UNIT_FOREVER_ABS, /* Data expiration */ + GNUNET_TIME_UNIT_FOREVER_REL, /* Retry time */ + NULL, /* Continuation */ + NULL); /* Continuation closure */ + announce_id_task = + GNUNET_SCHEDULER_add_delayed (id_announce_time, &announce_id, cls); +} + + +/******************************************************************************/ +/****************** GENERAL HELPER FUNCTIONS ************************/ +/******************************************************************************/ + +/** + * Decrements the reference counter and frees all resources if needed + * + * @param mesh_data Data Descriptor used in a multicast message. + * Freed no longer needed (last message). + */ +static void +data_descriptor_decrement_rc (struct MeshData *mesh_data) +{ + if (0 == --(mesh_data->reference_counter)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Last copy!\n"); + GNUNET_free (mesh_data->data); + GNUNET_free (mesh_data); + } +} + + +/** + * Check if client has registered with the service and has not disconnected + * + * @param client the client to check + * + * @return non-NULL if client exists in the global DLL + */ +static struct MeshClient * +client_get (struct GNUNET_SERVER_Client *client) +{ + struct MeshClient *c; + + c = clients; + while (NULL != c) + { + if (c->handle == client) + return c; + c = c->next; + } + return NULL; +} + + +/** + * Checks if a given client has subscribed to certain message type + * + * @param message_type Type of message to check + * @param c Client to check + * + * @return GNUNET_YES or GNUNET_NO, depending on subscription status + * + * FIXME: use of crypto_hash slows it down + * The hash function alone takes 8-10us out of the ~55us for the whole + * process of retransmitting the message from one local client to another. + * Find faster implementation! + */ +static int +client_is_subscribed (uint16_t message_type, struct MeshClient *c) +{ + struct GNUNET_HashCode hc; + + if (NULL == c->types) + return GNUNET_NO; + + GNUNET_CRYPTO_hash (&message_type, sizeof (uint16_t), &hc); + return GNUNET_CONTAINER_multihashmap_contains (c->types, &hc); +} + + +/** + * Check whether client wants traffic from a tunnel. + * + * @param c Client to check. + * @param t Tunnel to be found. + * + * @return GNUNET_YES if client knows tunnel. + * + * TODO look in client hashmap + */ +static int +client_wants_tunnel (struct MeshClient *c, struct MeshTunnel *t) +{ + unsigned int i; + + for (i = 0; i < t->nclients; i++) + if (t->clients[i] == c) + return GNUNET_YES; + return GNUNET_NO; +} + + +/** + * Check whether client has been informed about a tunnel. + * + * @param c Client to check. + * @param t Tunnel to be found. + * + * @return GNUNET_YES if client knows tunnel. + * + * TODO look in client hashmap + */ +static int +client_knows_tunnel (struct MeshClient *c, struct MeshTunnel *t) +{ + unsigned int i; + + for (i = 0; i < t->nignore; i++) + if (t->ignore[i] == c) + return GNUNET_YES; + return client_wants_tunnel(c, t); +} + + +/** + * Marks a client as uninterested in traffic from the tunnel, updating both + * client and tunnel to reflect this. + * + * @param c Client that doesn't want traffic anymore. + * @param t Tunnel which should be ignored. + * + * FIXME when to delete an incoming tunnel? + */ +static void +client_ignore_tunnel (struct MeshClient *c, struct MeshTunnel *t) +{ + struct GNUNET_HashCode hash; + + GNUNET_CRYPTO_hash (&t->local_tid_dest, sizeof (MESH_TunnelNumber), &hash); + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (c->incoming_tunnels, + &hash, t)); + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_put (c->ignore_tunnels, &hash, t, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); + tunnel_delete_active_client (t, c); + GNUNET_array_append (t->ignore, t->nignore, c); +} + + +/** + * Deletes a tunnel from a client (either owner or destination). To be used on + * tunnel destroy, otherwise, use client_ignore_tunnel. + * + * @param c Client whose tunnel to delete. + * @param t Tunnel which should be deleted. + */ +static void +client_delete_tunnel (struct MeshClient *c, struct MeshTunnel *t) +{ + struct GNUNET_HashCode hash; + + if (c == t->owner) + { + GNUNET_CRYPTO_hash(&t->local_tid, sizeof (MESH_TunnelNumber), &hash); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (c->own_tunnels, + &hash, + t)); + } + else + { + GNUNET_CRYPTO_hash(&t->local_tid_dest, sizeof (MESH_TunnelNumber), &hash); + // FIXME XOR? + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (c->incoming_tunnels, + &hash, + t) || + GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (c->ignore_tunnels, + &hash, + t)); + } +} + + +/** + * Notify the owner of a tunnel that a peer has disconnected. + * + * @param c Client (owner of tunnel). + * @param t Tunnel this message is about. + * @param peer_id Short ID of the disconnected peer. + */ +void +client_notify_peer_disconnected (struct MeshClient *c, + struct MeshTunnel *t, + GNUNET_PEER_Id peer_id) +{ + struct GNUNET_MESH_PeerControl msg; + + if (NULL == t->owner || NULL == nc) + return; + + msg.header.size = htons (sizeof (msg)); + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_DEL); + msg.tunnel_id = htonl (t->local_tid); + GNUNET_PEER_resolve (peer_id, &msg.peer); + GNUNET_SERVER_notification_context_unicast (nc, t->owner->handle, + &msg.header, GNUNET_NO); +} + + +/** + * Send the message to all clients that have subscribed to its type + * + * @param msg Pointer to the message itself + * @param payload Pointer to the payload of the message. + * @param t The tunnel to whose clients this message goes. + * + * @return number of clients this message was sent to + */ +static unsigned int +send_subscribed_clients (const struct GNUNET_MessageHeader *msg, + const struct GNUNET_MessageHeader *payload, + struct MeshTunnel *t) +{ + struct MeshClient *c; + MESH_TunnelNumber *tid; + unsigned int count; + uint16_t type; + char cbuf[htons (msg->size)]; + + type = ntohs (payload->type); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending to clients...\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "message of type %s\n", + GNUNET_MESH_DEBUG_M2S (type)); + + memcpy (cbuf, msg, sizeof (cbuf)); + switch (htons (msg->type)) + { + struct GNUNET_MESH_Unicast *uc; + struct GNUNET_MESH_Multicast *mc; + struct GNUNET_MESH_ToOrigin *to; + + case GNUNET_MESSAGE_TYPE_MESH_UNICAST: + uc = (struct GNUNET_MESH_Unicast *) cbuf; + tid = &uc->tid; + break; + case GNUNET_MESSAGE_TYPE_MESH_MULTICAST: + mc = (struct GNUNET_MESH_Multicast *) cbuf; + tid = &mc->tid; + break; + case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: + to = (struct GNUNET_MESH_ToOrigin *) cbuf; + tid = &to->tid; + break; + default: + GNUNET_break (0); + return 0; + } + + for (count = 0, c = clients; c != NULL; c = c->next) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " client %u\n", c->id); + if (client_is_subscribed (type, c)) + { + if (htons (msg->type) == GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN) + { + if (c != t->owner) + continue; + *tid = htonl (t->local_tid); + } + else + { + if (GNUNET_NO == client_knows_tunnel (c, t)) + { + /* This client doesn't know the tunnel */ + struct GNUNET_MESH_TunnelNotification tmsg; + struct GNUNET_HashCode hash; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " sending tunnel create\n"); + tmsg.header.size = htons (sizeof (tmsg)); + tmsg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_CREATE); + GNUNET_PEER_resolve (t->id.oid, &tmsg.peer); + tmsg.tunnel_id = htonl (t->local_tid_dest); + tmsg.opt = 0; + if (GNUNET_YES == t->speed_min) + tmsg.opt |= MESH_TUNNEL_OPT_SPEED_MIN; + if (GNUNET_YES == t->nobuffer) + tmsg.opt |= MESH_TUNNEL_OPT_NOBUFFER; + GNUNET_SERVER_notification_context_unicast (nc, c->handle, + &tmsg.header, GNUNET_NO); + tunnel_add_client (t, c); + GNUNET_CRYPTO_hash (&t->local_tid_dest, sizeof (MESH_TunnelNumber), + &hash); + GNUNET_break (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put ( + c->incoming_tunnels, &hash, t, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); + } + *tid = htonl (t->local_tid_dest); + } + + /* Check if the client wants to get traffic from the tunnel */ + if (GNUNET_NO == client_wants_tunnel(c, t)) + continue; + count++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " sending\n"); + GNUNET_SERVER_notification_context_unicast (nc, c->handle, + (struct GNUNET_MessageHeader + *) cbuf, GNUNET_NO); + } + } + + return count; +} + + +/** + * Notify the client that owns the tunnel that a peer has connected to it + * (the requested path to it has been confirmed). + * + * @param t Tunnel whose owner to notify + * @param id Short id of the peer that has connected + */ +static void +send_client_peer_connected (const struct MeshTunnel *t, const GNUNET_PEER_Id id) +{ + struct GNUNET_MESH_PeerControl pc; + + if (NULL == t->owner || GNUNET_YES == t->destroy) + return; + + pc.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_ADD); + pc.header.size = htons (sizeof (struct GNUNET_MESH_PeerControl)); + pc.tunnel_id = htonl (t->local_tid); + GNUNET_PEER_resolve (id, &pc.peer); + GNUNET_SERVER_notification_context_unicast (nc, t->owner->handle, &pc.header, + GNUNET_NO); +} + + +/** + * Notify all clients (not depending on registration status) that the incoming + * tunnel is no longer valid. + * + * @param t Tunnel that was destroyed. + */ +static void +send_clients_tunnel_destroy (struct MeshTunnel *t) +{ + struct GNUNET_MESH_TunnelMessage msg; + + msg.header.size = htons (sizeof (msg)); + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_DESTROY); + msg.tunnel_id = htonl (t->local_tid_dest); + GNUNET_SERVER_notification_context_broadcast (nc, &msg.header, GNUNET_NO); +} + + +/** + * Notify clients of tunnel disconnections, if needed. + * In case the origin disconnects, the destination clients get a tunnel destroy + * notification. If the last destination disconnects (only one remaining client + * in tunnel), the origin gets a (local ID) peer disconnected. + * Note that the function must be called BEFORE removing the client from + * the tunnel. + * + * @param t Tunnel that was destroyed. + * @param c Client that disconnected. + */ +static void +send_client_tunnel_disconnect (struct MeshTunnel *t, struct MeshClient *c) +{ + unsigned int i; + + if (c == t->owner) + { + struct GNUNET_MESH_TunnelMessage msg; + + msg.header.size = htons (sizeof (msg)); + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_DESTROY); + msg.tunnel_id = htonl (t->local_tid_dest); + for (i = 0; i < t->nclients; i++) + GNUNET_SERVER_notification_context_unicast (nc, t->clients[i]->handle, + &msg.header, GNUNET_NO); + } + // FIXME when to disconnect an incoming tunnel? + else if (1 == t->nclients && NULL != t->owner) + { + struct GNUNET_MESH_PeerControl msg; + + msg.header.size = htons (sizeof (msg)); + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_DEL); + msg.tunnel_id = htonl (t->local_tid); + msg.peer = my_full_id; + GNUNET_SERVER_notification_context_unicast (nc, t->owner->handle, + &msg.header, GNUNET_NO); + } +} + + +/** + * Iterator over all the peers to remove the oldest not-used entry. + * + * @param cls Closure (unsued). + * @param key ID of the peer. + * @param value Peer_Info of the peer. + * + * FIXME implement + */ +static int +peer_info_timeout (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + return GNUNET_YES; +} + +/** + * Retrieve the MeshPeerInfo stucture associated with the peer, create one + * and insert it in the appropiate structures if the peer is not known yet. + * + * @param peer Full identity of the peer. + * + * @return Existing or newly created peer info. + */ +static struct MeshPeerInfo * +peer_info_get (const struct GNUNET_PeerIdentity *peer) +{ + struct MeshPeerInfo *peer_info; + + peer_info = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey); + if (NULL == peer_info) + { + peer_info = + (struct MeshPeerInfo *) GNUNET_malloc (sizeof (struct MeshPeerInfo)); + if (GNUNET_CONTAINER_multihashmap_size (peers) > max_peers) + { + GNUNET_CONTAINER_multihashmap_iterate (peers, + &peer_info_timeout, + NULL); + } + GNUNET_CONTAINER_multihashmap_put (peers, &peer->hashPubKey, peer_info, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + peer_info->id = GNUNET_PEER_intern (peer); + } + peer_info->last_contact = GNUNET_TIME_absolute_get(); + + return peer_info; +} + + +/** + * Retrieve the MeshPeerInfo stucture associated with the peer, create one + * and insert it in the appropiate structures if the peer is not known yet. + * + * @param peer Short identity of the peer. + * + * @return Existing or newly created peer info. + */ +static struct MeshPeerInfo * +peer_info_get_short (const GNUNET_PEER_Id peer) +{ + struct GNUNET_PeerIdentity id; + + GNUNET_PEER_resolve (peer, &id); + return peer_info_get (&id); +} + + +/** + * Iterator to remove the tunnel from the list of tunnels a peer participates + * in. + * + * @param cls Closure (tunnel info) + * @param key GNUNET_PeerIdentity of the peer (unused) + * @param value PeerInfo of the peer + * + * @return always GNUNET_YES, to keep iterating + */ +static int +peer_info_delete_tunnel (void *cls, const struct GNUNET_HashCode * key, void *value) +{ + struct MeshTunnel *t = cls; + struct MeshPeerInfo *peer = value; + unsigned int i; + + for (i = 0; i < peer->ntunnels; i++) + { + if (0 == + memcmp (&peer->tunnels[i]->id, &t->id, sizeof (struct MESH_TunnelID))) + { + peer->ntunnels--; + peer->tunnels[i] = peer->tunnels[peer->ntunnels]; + peer->tunnels = GNUNET_realloc (peer->tunnels, peer->ntunnels); + return GNUNET_YES; + } + } + return GNUNET_YES; +} + + +/** + * Core callback to write a pre-constructed data packet to core buffer + * + * @param cls Closure (MeshTransmissionDescriptor with data in "data" member). + * @param size Number of bytes available in buf. + * @param buf Where the to write the message. + * + * @return number of bytes written to buf + */ +static size_t +send_core_data_raw (void *cls, size_t size, void *buf) +{ + struct MeshTransmissionDescriptor *info = cls; + struct GNUNET_MessageHeader *msg; + size_t total_size; + + GNUNET_assert (NULL != info); + GNUNET_assert (NULL != info->mesh_data); + msg = (struct GNUNET_MessageHeader *) info->mesh_data->data; + total_size = ntohs (msg->size); + + if (total_size > size) + { + GNUNET_break (0); + return 0; + } + memcpy (buf, msg, total_size); + data_descriptor_decrement_rc (info->mesh_data); + GNUNET_free (info); + return total_size; +} + + +/** + * Sends an already built non-multicast message to a peer, + * properly registrating all used resources. + * + * @param message Message to send. Function makes a copy of it. + * @param peer Short ID of the neighbor whom to send the message. + * @param t Tunnel on which this message is transmitted. + */ +static void +send_prebuilt_message (const struct GNUNET_MessageHeader *message, + const struct GNUNET_PeerIdentity *peer, + struct MeshTunnel *t) +{ + struct MeshTransmissionDescriptor *info; + struct MeshPeerInfo *neighbor; + struct MeshPeerPath *p; + size_t size; + uint16_t type; + +// GNUNET_TRANSPORT_try_connect(); FIXME use? + + size = ntohs (message->size); + info = GNUNET_malloc (sizeof (struct MeshTransmissionDescriptor)); + info->mesh_data = GNUNET_malloc (sizeof (struct MeshData)); + info->mesh_data->data = GNUNET_malloc (size); + memcpy (info->mesh_data->data, message, size); + type = ntohs(message->type); + switch (type) + { + struct GNUNET_MESH_Unicast *m; + struct GNUNET_MESH_ToOrigin *to; + + case GNUNET_MESSAGE_TYPE_MESH_UNICAST: + m = (struct GNUNET_MESH_Unicast *) info->mesh_data->data; + m->ttl = htonl (ntohl (m->ttl) - 1); + break; + case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: + to = (struct GNUNET_MESH_ToOrigin *) info->mesh_data->data; + t->bck_pid++; + to->pid = htonl(t->bck_pid); + } + info->mesh_data->data_len = size; + info->mesh_data->reference_counter = 1; + info->mesh_data->total_out = 1; + neighbor = peer_info_get (peer); + for (p = neighbor->path_head; NULL != p; p = p->next) + { + if (2 >= p->length) + { + break; + } + } + if (NULL == p) + { +#if MESH_DEBUG + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " %s IS NOT DIRECTLY CONNECTED\n", + GNUNET_i2s(peer)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " PATHS TO %s:\n", + GNUNET_i2s(peer)); + for (p = neighbor->path_head; NULL != p; p = p->next) + { + struct GNUNET_PeerIdentity debug_id; + unsigned int i; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " path with %u hops through:\n", + p->length); + for (i = 0; i < p->length; i++) + { + GNUNET_PEER_resolve(p->peers[i], &debug_id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " hop %u: %s\n", + i, GNUNET_i2s(&debug_id)); + } + } +#endif + GNUNET_break (0); // FIXME sometimes fails (testing disconnect?) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + " no direct connection to %s\n", + GNUNET_i2s (peer)); + GNUNET_free (info->mesh_data->data); + GNUNET_free (info->mesh_data); + GNUNET_free (info); + return; + } + info->peer = neighbor; + if (GNUNET_MESSAGE_TYPE_MESH_PATH_ACK == type) + type = 0; + queue_add (info, + type, + size, + neighbor, + t); +} + + +/** + * Sends a CREATE PATH message for a path to a peer, properly registrating + * all used resources. + * + * @param peer PeerInfo of the final peer for whom this path is being created. + * @param p Path itself. + * @param t Tunnel for which the path is created. + */ +static void +send_create_path (struct MeshPeerInfo *peer, struct MeshPeerPath *p, + struct MeshTunnel *t) +{ + struct GNUNET_PeerIdentity id; + struct MeshPathInfo *path_info; + struct MeshPeerInfo *neighbor; + + unsigned int i; + + if (NULL == p) + { + p = tree_get_path_to_peer (t->tree, peer->id); + if (NULL == p) + { + GNUNET_break (0); + return; + } + } + for (i = 0; i < p->length; i++) + { + if (p->peers[i] == myid) + break; + } + if (i >= p->length - 1) + { + path_destroy (p); + GNUNET_break (0); + return; + } + GNUNET_PEER_resolve (p->peers[i + 1], &id); + + path_info = GNUNET_malloc (sizeof (struct MeshPathInfo)); + path_info->path = p; + path_info->t = t; + neighbor = peer_info_get (&id); + path_info->peer = neighbor; + queue_add (path_info, + GNUNET_MESSAGE_TYPE_MESH_PATH_CREATE, + sizeof (struct GNUNET_MESH_ManipulatePath) + + (p->length * sizeof (struct GNUNET_PeerIdentity)), + neighbor, + t); +} + + +/** + * Sends a DESTROY PATH message to free resources for a path in a tunnel + * + * @param t Tunnel whose path to destroy. + * @param destination Short ID of the peer to whom the path to destroy. + */ +static void +send_destroy_path (struct MeshTunnel *t, GNUNET_PEER_Id destination) +{ + struct MeshPeerPath *p; + size_t size; + + p = tree_get_path_to_peer (t->tree, destination); + if (NULL == p) + { + GNUNET_break (0); + return; + } + size = sizeof (struct GNUNET_MESH_ManipulatePath); + size += p->length * sizeof (struct GNUNET_PeerIdentity); + { + struct GNUNET_MESH_ManipulatePath *msg; + struct GNUNET_PeerIdentity *pi; + char cbuf[size]; + unsigned int i; + + msg = (struct GNUNET_MESH_ManipulatePath *) cbuf; + msg->header.size = htons (size); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_PATH_DESTROY); + msg->tid = htonl (t->id.tid); + pi = (struct GNUNET_PeerIdentity *) &msg[1]; + for (i = 0; i < p->length; i++) + { + GNUNET_PEER_resolve (p->peers[i], &pi[i]); + } + send_prebuilt_message (&msg->header, tree_get_first_hop (t->tree, destination), t); + } + path_destroy (p); +} + + +/** + * Sends a PATH ACK message in reponse to a received PATH_CREATE directed to us. + * + * @param t Tunnel which to confirm. + */ +static void +send_path_ack (struct MeshTunnel *t) +{ + struct MeshTransmissionDescriptor *info; + struct GNUNET_PeerIdentity id; + GNUNET_PEER_Id peer; + + peer = tree_get_predecessor (t->tree); + GNUNET_PEER_resolve (peer, &id); + info = GNUNET_malloc (sizeof (struct MeshTransmissionDescriptor)); + info->origin = &t->id; + info->peer = GNUNET_CONTAINER_multihashmap_get (peers, &id.hashPubKey); + GNUNET_assert (NULL != info->peer); + + queue_add (info, + GNUNET_MESSAGE_TYPE_MESH_PATH_ACK, + sizeof (struct GNUNET_MESH_PathACK), + info->peer, + t); +} + + +/** + * Try to establish a new connection to this peer. + * Use the best path for the given tunnel. + * If the peer doesn't have any path to it yet, try to get one. + * If the peer already has some path, send a CREATE PATH towards it. + * + * @param peer PeerInfo of the peer. + * @param t Tunnel for which to create the path, if possible. + */ +static void +peer_info_connect (struct MeshPeerInfo *peer, struct MeshTunnel *t) +{ + struct MeshPeerPath *p; + struct MeshPathInfo *path_info; + + if (NULL != peer->path_head) + { + p = tree_get_path_to_peer (t->tree, peer->id); + if (NULL == p) + { + GNUNET_break (0); + return; + } + + // FIXME always send create path to self + if (p->length > 1) + { + send_create_path (peer, p, t); + } + else + { + struct GNUNET_HashCode hash; + + path_destroy (p); + send_client_peer_connected (t, myid); + t->local_tid_dest = next_local_tid++; + GNUNET_CRYPTO_hash (&t->local_tid_dest, sizeof (MESH_TunnelNumber), + &hash); + if (GNUNET_OK != + GNUNET_CONTAINER_multihashmap_put (incoming_tunnels, &hash, t, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + { + GNUNET_break (0); + return; + } + } + } + else if (NULL == peer->dhtget) + { + struct GNUNET_PeerIdentity id; + + GNUNET_PEER_resolve (peer->id, &id); + path_info = GNUNET_malloc (sizeof (struct MeshPathInfo)); + path_info->peer = peer; + path_info->t = t; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " Starting DHT GET for peer %s\n", GNUNET_i2s (&id)); + peer->dhtgetcls = path_info; + peer->dhtget = GNUNET_DHT_get_start (dht_handle, /* handle */ + GNUNET_BLOCK_TYPE_MESH_PEER, /* type */ + &id.hashPubKey, /* key to search */ + dht_replication_level, /* replication level */ + GNUNET_DHT_RO_RECORD_ROUTE | + GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, + NULL, /* xquery */ // FIXME BLOOMFILTER + 0, /* xquery bits */ // FIXME BLOOMFILTER SIZE + &dht_get_id_handler, path_info); + } + /* Otherwise, there is no path but the DHT get is already started. */ +} + + +/** + * Task to delay the connection of a peer + * + * @param cls Closure (path info with tunnel and peer to connect). + * Will be free'd on exection. + * @param tc TaskContext + */ +static void +peer_info_connect_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct MeshPathInfo *path_info = cls; + + path_info->peer->connect_task = GNUNET_SCHEDULER_NO_TASK; + + if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) + { + GNUNET_free (cls); + return; + } + peer_info_connect (path_info->peer, path_info->t); + GNUNET_free (cls); +} + + +/** + * Destroy the peer_info and free any allocated resources linked to it + * + * @param pi The peer_info to destroy. + * + * @return GNUNET_OK on success + */ +static int +peer_info_destroy (struct MeshPeerInfo *pi) +{ + struct GNUNET_PeerIdentity id; + struct MeshPeerPath *p; + struct MeshPeerPath *nextp; + + GNUNET_PEER_resolve (pi->id, &id); + GNUNET_PEER_change_rc (pi->id, -1); + + if (GNUNET_YES != + GNUNET_CONTAINER_multihashmap_remove (peers, &id.hashPubKey, pi)) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "removing peer %s, not in hashmap\n", GNUNET_i2s (&id)); + } + if (NULL != pi->dhtget) + { + GNUNET_DHT_get_stop (pi->dhtget); + GNUNET_free (pi->dhtgetcls); + } + p = pi->path_head; + while (NULL != p) + { + nextp = p->next; + GNUNET_CONTAINER_DLL_remove (pi->path_head, pi->path_tail, p); + path_destroy (p); + p = nextp; + } + if (GNUNET_SCHEDULER_NO_TASK != pi->connect_task) + { + GNUNET_free (GNUNET_SCHEDULER_cancel (pi->connect_task)); + } + GNUNET_free (pi); + return GNUNET_OK; +} + + +/** + * Remove all paths that rely on a direct connection between p1 and p2 + * from the peer itself and notify all tunnels about it. + * + * @param peer PeerInfo of affected peer. + * @param p1 GNUNET_PEER_Id of one peer. + * @param p2 GNUNET_PEER_Id of another peer that was connected to the first and + * no longer is. + * + * TODO: optimize (see below) + */ +static void +peer_info_remove_path (struct MeshPeerInfo *peer, GNUNET_PEER_Id p1, + GNUNET_PEER_Id p2) +{ + struct MeshPeerPath *p; + struct MeshPeerPath *aux; + struct MeshPeerInfo *peer_d; + GNUNET_PEER_Id d; + unsigned int destroyed; + unsigned int best; + unsigned int cost; + unsigned int i; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "peer_info_remove_path\n"); + destroyed = 0; + p = peer->path_head; + while (NULL != p) + { + aux = p->next; + for (i = 0; i < (p->length - 1); i++) + { + if ((p->peers[i] == p1 && p->peers[i + 1] == p2) || + (p->peers[i] == p2 && p->peers[i + 1] == p1)) + { + GNUNET_CONTAINER_DLL_remove (peer->path_head, peer->path_tail, p); + path_destroy (p); + destroyed++; + break; + } + } + p = aux; + } + if (0 == destroyed) + return; + + for (i = 0; i < peer->ntunnels; i++) + { + d = tunnel_notify_connection_broken (peer->tunnels[i], p1, p2); + if (0 == d) + continue; + /* TODO + * Problem: one or more peers have been deleted from the tunnel tree. + * We don't know who they are to try to add them again. + * We need to try to find a new path for each of the disconnected peers. + * Some of them might already have a path to reach them that does not + * involve p1 and p2. Adding all anew might render in a better tree than + * the trivial immediate fix. + * + * Trivial immiediate fix: try to reconnect to the disconnected node. All + * its children will be reachable trough him. + */ + peer_d = peer_info_get_short (d); + best = UINT_MAX; + aux = NULL; + for (p = peer_d->path_head; NULL != p; p = p->next) + { + if ((cost = tree_get_path_cost (peer->tunnels[i]->tree, p)) < best) + { + best = cost; + aux = p; + } + } + if (NULL != aux) + { + /* No callback, as peer will be already disconnected and a connection + * scheduled by tunnel_notify_connection_broken. + */ + tree_add_path (peer->tunnels[i]->tree, aux, NULL, NULL); + } + else + { + peer_info_connect (peer_d, peer->tunnels[i]); + } + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "peer_info_remove_path END\n"); +} + + +/** + * Add the path to the peer and update the path used to reach it in case this + * is the shortest. + * + * @param peer_info Destination peer to add the path to. + * @param path New path to add. Last peer must be the peer in arg 1. + * Path will be either used of freed if already known. + * @param trusted Do we trust that this path is real? + */ +void +peer_info_add_path (struct MeshPeerInfo *peer_info, struct MeshPeerPath *path, + int trusted) +{ + struct MeshPeerPath *aux; + unsigned int l; + unsigned int l2; + + if ((NULL == peer_info) || (NULL == path)) + { + GNUNET_break (0); + path_destroy (path); + return; + } + if (path->peers[path->length - 1] != peer_info->id) + { + GNUNET_break (0); + path_destroy (path); + return; + } + if (path->length <= 2 && GNUNET_NO == trusted) + { + /* Only allow CORE to tell us about direct paths */ + path_destroy (path); + return; + } + GNUNET_assert (peer_info->id == path->peers[path->length - 1]); + for (l = 1; l < path->length; l++) + { + if (path->peers[l] == myid) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "shortening path by %u\n", l); + for (l2 = 0; l2 < path->length - l; l2++) + { + path->peers[l2] = path->peers[l + l2]; + } + path->length -= l; + l = 1; + path->peers = + GNUNET_realloc (path->peers, path->length * sizeof (GNUNET_PEER_Id)); + } + } +#if MESH_DEBUG + { + struct GNUNET_PeerIdentity id; + + GNUNET_PEER_resolve (peer_info->id, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "adding path [%u] to peer %s\n", + path->length, GNUNET_i2s (&id)); + } +#endif + l = path_get_length (path); + if (0 == l) + { + GNUNET_free (path); + return; + } + + GNUNET_assert (peer_info->id == path->peers[path->length - 1]); + for (aux = peer_info->path_head; aux != NULL; aux = aux->next) + { + l2 = path_get_length (aux); + if (l2 > l) + { + GNUNET_CONTAINER_DLL_insert_before (peer_info->path_head, + peer_info->path_tail, aux, path); + return; + } + else + { + if (l2 == l && memcmp (path->peers, aux->peers, l) == 0) + { + path_destroy (path); + return; + } + } + } + GNUNET_CONTAINER_DLL_insert_tail (peer_info->path_head, peer_info->path_tail, + path); + return; +} + + +/** + * Add the path to the origin peer and update the path used to reach it in case + * this is the shortest. + * The path is given in peer_info -> destination, therefore we turn the path + * upside down first. + * + * @param peer_info Peer to add the path to, being the origin of the path. + * @param path New path to add after being inversed. + * @param trusted Do we trust that this path is real? + */ +static void +peer_info_add_path_to_origin (struct MeshPeerInfo *peer_info, + struct MeshPeerPath *path, int trusted) +{ + path_invert (path); + peer_info_add_path (peer_info, path, trusted); +} + + +/** + * Function called if the connection to the peer has been stalled for a while, + * possibly due to a missed ACK. Poll the peer about its ACK status. + * + * @param cls Closure (cinfo). + * @param tc TaskContext. + */ +static void +tunnel_poll (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct MeshTunnelChildInfo *cinfo = cls; + struct GNUNET_MESH_Poll msg; + struct GNUNET_PeerIdentity id; + struct MeshTunnel *t; + + cinfo->fc_poll = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + { + return; + } + + t = cinfo->t; + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_POLL); + msg.header.size = htons (sizeof (msg)); + msg.tid = htonl (t->id.tid); + GNUNET_PEER_resolve (t->id.oid, &msg.oid); + msg.last_ack = htonl (cinfo->fwd_ack); + + GNUNET_PEER_resolve (cinfo->id, &id); + send_prebuilt_message (&msg.header, &id, cinfo->t); + cinfo->fc_poll_time = GNUNET_TIME_relative_min ( + MESH_MAX_POLL_TIME, + GNUNET_TIME_relative_multiply (cinfo->fc_poll_time, 2)); + cinfo->fc_poll = GNUNET_SCHEDULER_add_delayed (cinfo->fc_poll_time, + &tunnel_poll, cinfo); +} + + +/** + * Build a PeerPath from the paths returned from the DHT, reversing the paths + * to obtain a local peer -> destination path and interning the peer ids. + * + * @return Newly allocated and created path + */ +static struct MeshPeerPath * +path_build_from_dht (const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_length) +{ + struct MeshPeerPath *p; + GNUNET_PEER_Id id; + int i; + + p = path_new (1); + p->peers[0] = myid; + GNUNET_PEER_change_rc (myid, 1); + i = get_path_length; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " GET has %d hops.\n", i); + for (i--; i >= 0; i--) + { + id = GNUNET_PEER_intern (&get_path[i]); + if (p->length > 0 && id == p->peers[p->length - 1]) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Optimizing 1 hop out.\n"); + GNUNET_PEER_change_rc (id, -1); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Adding from GET: %s.\n", + GNUNET_i2s (&get_path[i])); + p->length++; + p->peers = GNUNET_realloc (p->peers, sizeof (GNUNET_PEER_Id) * p->length); + p->peers[p->length - 1] = id; + } + } + i = put_path_length; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " PUT has %d hops.\n", i); + for (i--; i >= 0; i--) + { + id = GNUNET_PEER_intern (&put_path[i]); + if (id == myid) + { + /* PUT path went through us, so discard the path up until now and start + * from here to get a much shorter (and loop-free) path. + */ + path_destroy (p); + p = path_new (0); + } + if (p->length > 0 && id == p->peers[p->length - 1]) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Optimizing 1 hop out.\n"); + GNUNET_PEER_change_rc (id, -1); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Adding from PUT: %s.\n", + GNUNET_i2s (&put_path[i])); + p->length++; + p->peers = GNUNET_realloc (p->peers, sizeof (GNUNET_PEER_Id) * p->length); + p->peers[p->length - 1] = id; + } + } +#if MESH_DEBUG + if (get_path_length > 0) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " (first of GET: %s)\n", + GNUNET_i2s (&get_path[0])); + if (put_path_length > 0) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " (first of PUT: %s)\n", + GNUNET_i2s (&put_path[0])); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " In total: %d hops\n", + p->length); + for (i = 0; i < p->length; i++) + { + struct GNUNET_PeerIdentity peer_id; + + GNUNET_PEER_resolve (p->peers[i], &peer_id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " %u: %s\n", p->peers[i], + GNUNET_i2s (&peer_id)); + } +#endif + return p; +} + + +/** + * Adds a path to the peer_infos of all the peers in the path + * + * @param p Path to process. + * @param confirmed Whether we know if the path works or not. + */ +static void +path_add_to_peers (struct MeshPeerPath *p, int confirmed) +{ + unsigned int i; + + /* TODO: invert and add */ + for (i = 0; i < p->length && p->peers[i] != myid; i++) /* skip'em */ ; + for (i++; i < p->length; i++) + { + struct MeshPeerInfo *aux; + struct MeshPeerPath *copy; + + aux = peer_info_get_short (p->peers[i]); + copy = path_duplicate (p); + copy->length = i + 1; + peer_info_add_path (aux, copy, GNUNET_NO); + } +} + + +/** + * Send keepalive packets for a peer + * + * @param cls Closure (tunnel for which to send the keepalive). + * @param tc Notification context. + * + * TODO: implement explicit multicast keepalive? + */ +static void +path_refresh (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Search for a tunnel among the incoming tunnels + * + * @param tid the local id of the tunnel + * + * @return tunnel handler, NULL if doesn't exist + */ +static struct MeshTunnel * +tunnel_get_incoming (MESH_TunnelNumber tid) +{ + struct GNUNET_HashCode hash; + + GNUNET_assert (tid >= GNUNET_MESH_LOCAL_TUNNEL_ID_SERV); + GNUNET_CRYPTO_hash (&tid, sizeof (MESH_TunnelNumber), &hash); + return GNUNET_CONTAINER_multihashmap_get (incoming_tunnels, &hash); +} + + +/** + * Search for a tunnel among the tunnels for a client + * + * @param c the client whose tunnels to search in + * @param tid the local id of the tunnel + * + * @return tunnel handler, NULL if doesn't exist + */ +static struct MeshTunnel * +tunnel_get_by_local_id (struct MeshClient *c, MESH_TunnelNumber tid) +{ + if (tid >= GNUNET_MESH_LOCAL_TUNNEL_ID_SERV) + { + return tunnel_get_incoming (tid); + } + else + { + struct GNUNET_HashCode hash; + + GNUNET_CRYPTO_hash (&tid, sizeof (MESH_TunnelNumber), &hash); + return GNUNET_CONTAINER_multihashmap_get (c->own_tunnels, &hash); + } +} + + +/** + * Search for a tunnel by global ID using PEER_ID + * + * @param pi owner of the tunnel + * @param tid global tunnel number + * + * @return tunnel handler, NULL if doesn't exist + */ +static struct MeshTunnel * +tunnel_get_by_pi (GNUNET_PEER_Id pi, MESH_TunnelNumber tid) +{ + struct MESH_TunnelID id; + struct GNUNET_HashCode hash; + + id.oid = pi; + id.tid = tid; + + GNUNET_CRYPTO_hash (&id, sizeof (struct MESH_TunnelID), &hash); + return GNUNET_CONTAINER_multihashmap_get (tunnels, &hash); +} + + +/** + * Search for a tunnel by global ID using full PeerIdentities + * + * @param oid owner of the tunnel + * @param tid global tunnel number + * + * @return tunnel handler, NULL if doesn't exist + */ +static struct MeshTunnel * +tunnel_get (const struct GNUNET_PeerIdentity *oid, MESH_TunnelNumber tid) +{ + return tunnel_get_by_pi (GNUNET_PEER_search (oid), tid); +} + + +/** + * Delete an active client from the tunnel. + * + * @param t Tunnel. + * @param c Client. + */ +static void +tunnel_delete_active_client (struct MeshTunnel *t, const struct MeshClient *c) +{ + unsigned int i; + + for (i = 0; i < t->nclients; i++) + { + if (t->clients[i] == c) + { + t->clients[i] = t->clients[t->nclients - 1]; + t->clients_fc[i] = t->clients_fc[t->nclients - 1]; + GNUNET_array_grow (t->clients, t->nclients, t->nclients - 1); + t->nclients++; + GNUNET_array_grow (t->clients_fc, t->nclients, t->nclients - 1); + break; + } + } +} + + +/** + * Delete an ignored client from the tunnel. + * + * @param t Tunnel. + * @param c Client. + */ +static void +tunnel_delete_ignored_client (struct MeshTunnel *t, const struct MeshClient *c) +{ + unsigned int i; + + for (i = 0; i < t->nignore; i++) + { + if (t->ignore[i] == c) + { + t->ignore[i] = t->ignore[t->nignore - 1]; + GNUNET_array_grow (t->ignore, t->nignore, t->nignore - 1); + break; + } + } +} + + +/** + * Delete a client from the tunnel. It should be only done on + * client disconnection, otherwise use client_ignore_tunnel. + * + * @param t Tunnel. + * @param c Client. + */ +static void +tunnel_delete_client (struct MeshTunnel *t, const struct MeshClient *c) +{ + tunnel_delete_ignored_client (t, c); + tunnel_delete_active_client (t, c); +} + + +/** + * @brief Iterator to destroy MeshTunnelChildInfo of tunnel children. + * + * Destroys queue elements of all waiting transmissions and frees all memory + * used by the struct and its elements. + * + * @param cls Closure (tunnel info). + * @param key Hash of GNUNET_PEER_Id (unused). + * @param value MeshTunnelChildInfo of the child. + * + * @return always GNUNET_YES, to keep iterating + */ +static int +tunnel_destroy_child (void *cls, + const struct GNUNET_HashCode * key, + void *value) +{ + struct MeshTunnelChildInfo *cinfo = value; + struct MeshTunnel *t = cls; + struct MeshPeerQueue *q; + unsigned int c; + unsigned int i; + + for (c = 0; c < cinfo->send_buffer_n; c++) + { + i = (cinfo->send_buffer_start + c) % t->fwd_queue_max; + q = cinfo->send_buffer[i]; + cinfo->send_buffer[i] = NULL; + if (NULL != q) + queue_destroy (q, GNUNET_YES); + else + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "%u %u\n", c, cinfo->send_buffer_n); + } + GNUNET_free_non_null (cinfo->send_buffer); + if (GNUNET_SCHEDULER_NO_TASK != cinfo->fc_poll) + { + GNUNET_SCHEDULER_cancel (cinfo->fc_poll); + cinfo->fc_poll = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_free (cinfo); + return GNUNET_YES; +} + + +/** + * Callback used to notify a client owner of a tunnel that a peer has + * disconnected, most likely because of a path change. + * + * @param cls Closure (tunnel this notification is about). + * @param peer_id Short ID of disconnected peer. + */ +void +tunnel_notify_client_peer_disconnected (void *cls, GNUNET_PEER_Id peer_id) +{ + struct MeshTunnel *t = cls; + struct MeshPeerInfo *peer; + struct MeshPathInfo *path_info; + + client_notify_peer_disconnected (t->owner, t, peer_id); + + peer = peer_info_get_short (peer_id); + path_info = GNUNET_malloc (sizeof (struct MeshPathInfo)); + path_info->peer = peer; + path_info->t = t; + peer->connect_task = GNUNET_SCHEDULER_add_now (&peer_info_connect_task, + path_info); +} + + +/** + * Add a peer to a tunnel, accomodating paths accordingly and initializing all + * needed rescources. + * If peer already exists, reevaluate shortest path and change if different. + * + * @param t Tunnel we want to add a new peer to + * @param peer PeerInfo of the peer being added + * + */ +static void +tunnel_add_peer (struct MeshTunnel *t, struct MeshPeerInfo *peer) +{ + struct GNUNET_PeerIdentity id; + struct MeshPeerPath *best_p; + struct MeshPeerPath *p; + unsigned int best_cost; + unsigned int cost; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tunnel_add_peer\n"); + GNUNET_PEER_resolve (peer->id, &id); + if (GNUNET_NO == + GNUNET_CONTAINER_multihashmap_contains (t->peers, &id.hashPubKey)) + { + t->peers_total++; + GNUNET_array_append (peer->tunnels, peer->ntunnels, t); + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (t->peers, &id.hashPubKey, + peer, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); + } + + if (NULL != (p = peer->path_head)) + { + best_p = p; + best_cost = tree_get_path_cost (t->tree, p); + while (NULL != p) + { + if ((cost = tree_get_path_cost (t->tree, p)) < best_cost) + { + best_cost = cost; + best_p = p; + } + p = p->next; + } + tree_add_path (t->tree, best_p, &tunnel_notify_client_peer_disconnected, t); + if (GNUNET_SCHEDULER_NO_TASK == t->path_refresh_task) + t->path_refresh_task = + GNUNET_SCHEDULER_add_delayed (refresh_path_time, &path_refresh, t); + } + else + { + /* Start a DHT get */ + peer_info_connect (peer, t); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tunnel_add_peer END\n"); +} + +/** + * Add a path to a tunnel which we don't own, just to remember the next hop. + * If destination node was already in the tunnel, the first hop information + * will be replaced with the new path. + * + * @param t Tunnel we want to add a new peer to + * @param p Path to add + * @param own_pos Position of local node in path. + * + */ +static void +tunnel_add_path (struct MeshTunnel *t, struct MeshPeerPath *p, + unsigned int own_pos) +{ + struct GNUNET_PeerIdentity id; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tunnel_add_path\n"); + GNUNET_assert (0 != own_pos); + tree_add_path (t->tree, p, NULL, NULL); + if (own_pos < p->length - 1) + { + GNUNET_PEER_resolve (p->peers[own_pos + 1], &id); + tree_update_first_hops (t->tree, myid, &id); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tunnel_add_path END\n"); +} + +/** + * Add a client to a tunnel, initializing all needed data structures. + * + * @param t Tunnel to which add the client. + * @param c Client which to add to the tunnel. + */ +static void +tunnel_add_client (struct MeshTunnel *t, struct MeshClient *c) +{ + struct MeshTunnelClientInfo clinfo; + + GNUNET_array_append (t->clients, t->nclients, c); + clinfo.fwd_ack = t->fwd_pid + 1; + clinfo.bck_ack = t->nobuffer ? 1 : INITIAL_WINDOW_SIZE - 1; + clinfo.fwd_pid = t->fwd_pid; + clinfo.bck_pid = (uint32_t) -1; // Expected next: 0 + t->nclients--; + GNUNET_array_append (t->clients_fc, t->nclients, clinfo); +} + + +/** + * Notifies a tunnel that a connection has broken that affects at least + * some of its peers. Sends a notification towards the root of the tree. + * In case the peer is the owner of the tree, notifies the client that owns + * the tunnel and tries to reconnect. + * + * @param t Tunnel affected. + * @param p1 Peer that got disconnected from p2. + * @param p2 Peer that got disconnected from p1. + * + * @return Short ID of the peer disconnected (either p1 or p2). + * 0 if the tunnel remained unaffected. + */ +static GNUNET_PEER_Id +tunnel_notify_connection_broken (struct MeshTunnel *t, GNUNET_PEER_Id p1, + GNUNET_PEER_Id p2) +{ + GNUNET_PEER_Id pid; + + pid = + tree_notify_connection_broken (t->tree, p1, p2, + &tunnel_notify_client_peer_disconnected, + t); + if (myid != p1 && myid != p2) + { + return pid; + } + if (pid != myid) + { + if (tree_get_predecessor (t->tree) != 0) + { + /* We are the peer still connected, notify owner of the disconnection. */ + struct GNUNET_MESH_PathBroken msg; + struct GNUNET_PeerIdentity neighbor; + + msg.header.size = htons (sizeof (msg)); + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_PATH_BROKEN); + GNUNET_PEER_resolve (t->id.oid, &msg.oid); + msg.tid = htonl (t->id.tid); + msg.peer1 = my_full_id; + GNUNET_PEER_resolve (pid, &msg.peer2); + GNUNET_PEER_resolve (tree_get_predecessor (t->tree), &neighbor); + send_prebuilt_message (&msg.header, &neighbor, t); + } + } + return pid; +} + + +/** + * Send a multicast packet to a neighbor. + * + * @param cls Closure (Info about the multicast packet) + * @param neighbor_id Short ID of the neighbor to send the packet to. + */ +static void +tunnel_send_multicast_iterator (void *cls, GNUNET_PEER_Id neighbor_id) +{ + struct MeshData *mdata = cls; + struct MeshTransmissionDescriptor *info; + struct GNUNET_PeerIdentity neighbor; + struct GNUNET_MessageHeader *msg; + + info = GNUNET_malloc (sizeof (struct MeshTransmissionDescriptor)); + + info->mesh_data = mdata; + (mdata->reference_counter) ++; + info->destination = neighbor_id; + GNUNET_PEER_resolve (neighbor_id, &neighbor); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " sending to %s...\n", + GNUNET_i2s (&neighbor)); + info->peer = peer_info_get (&neighbor); + GNUNET_assert (NULL != info->peer); + msg = (struct GNUNET_MessageHeader *) mdata->data; + queue_add(info, + ntohs (msg->type), + info->mesh_data->data_len, + info->peer, + mdata->t); +} + + +/** + * Queue a message in a tunnel in multicast, sending a copy to each child node + * down the local one in the tunnel tree. + * + * @param t Tunnel in which to send the data. + * @param msg Message to be sent. + */ +static void +tunnel_send_multicast (struct MeshTunnel *t, + const struct GNUNET_MessageHeader *msg) +{ + struct MeshData *mdata; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " sending a multicast packet...\n"); + + mdata = GNUNET_malloc (sizeof (struct MeshData)); + mdata->data_len = ntohs (msg->size); + mdata->t = t; + mdata->data = GNUNET_malloc (mdata->data_len); + memcpy (mdata->data, msg, mdata->data_len); + if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_MESH_MULTICAST) + { + struct GNUNET_MESH_Multicast *mcast; + + mcast = (struct GNUNET_MESH_Multicast *) mdata->data; + if (t->fwd_queue_n >= t->fwd_queue_max) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, " queue full!\n"); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + " message from %s!\n", + GNUNET_i2s(&mcast->oid)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + " message at %s!\n", + GNUNET_i2s(&my_full_id)); + GNUNET_free (mdata->data); + GNUNET_free (mdata); + return; + } + t->fwd_queue_n++; + mcast->ttl = htonl (ntohl (mcast->ttl) - 1); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " data packet, ttl: %u\n", + ntohl (mcast->ttl)); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " not a data packet, no ttl\n"); + } + + tree_iterate_children (t->tree, &tunnel_send_multicast_iterator, mdata); + if (mdata->reference_counter == 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " no one to send data to\n"); + GNUNET_free (mdata->data); + GNUNET_free (mdata); + if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_MESH_MULTICAST) + t->fwd_queue_n--; + } + else + { + mdata->total_out = mdata->reference_counter; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " sending a multicast packet done\n"); + return; +} + + +/** + * Increase the SKIP value of all peers that + * have not received a unicast message. + * + * @param cls Closure (ID of the peer that HAS received the message). + * @param key ID of the neighbor. + * @param value Information about the neighbor. + * + * @return GNUNET_YES to keep iterating. + */ +static int +tunnel_add_skip (void *cls, + const struct GNUNET_HashCode * key, + void *value) +{ + struct GNUNET_PeerIdentity *neighbor = cls; + struct MeshTunnelChildInfo *cinfo = value; + + /* TODO compare only pointers? key == neighbor? */ + if (0 == memcmp (&neighbor->hashPubKey, key, sizeof (struct GNUNET_HashCode))) + { + return GNUNET_YES; + } + cinfo->skip++; + return GNUNET_YES; +} + + +/** + * @brief Get neighbor's Flow Control information. + * + * Retrieves the MeshTunnelChildInfo containing Flow Control data about a direct + * descendant of the local node in a certain tunnel. + * If the info is not yet there (recently created path), creates the data struct + * and inserts it into the tunnel info, initialized to the current tunnel ACK + * values. + * + * @param t Tunnel related. + * @param peer Neighbor whose Flow Control info is needed. + * + * @return Neighbor's Flow Control info. + */ +static struct MeshTunnelChildInfo * +tunnel_get_neighbor_fc (struct MeshTunnel *t, + const struct GNUNET_PeerIdentity *peer) +{ + struct MeshTunnelChildInfo *cinfo; + + if (NULL == t->children_fc) + return NULL; + + cinfo = GNUNET_CONTAINER_multihashmap_get (t->children_fc, + &peer->hashPubKey); + if (NULL == cinfo) + { + uint32_t delta; + + cinfo = GNUNET_malloc (sizeof (struct MeshTunnelChildInfo)); + cinfo->id = GNUNET_PEER_intern (peer); + cinfo->skip = t->fwd_pid; + cinfo->t = t; + + delta = t->nobuffer ? 1 : INITIAL_WINDOW_SIZE; + cinfo->fwd_ack = t->fwd_pid + delta; + cinfo->bck_ack = delta; + cinfo->bck_pid = -1; + + cinfo->fc_poll = GNUNET_SCHEDULER_NO_TASK; + cinfo->fc_poll_time = GNUNET_TIME_UNIT_SECONDS; + + cinfo->send_buffer = + GNUNET_malloc (sizeof(struct MeshPeerQueue *) * t->fwd_queue_max); + + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (t->children_fc, + &peer->hashPubKey, + cinfo, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); + } + return cinfo; +} + + +/** + * Get the Flow Control info of a client. + * + * @param t Tunnel on which to look. + * @param c Client whose ACK to get. + * + * @return ACK value. + */ +static struct MeshTunnelClientInfo * +tunnel_get_client_fc (struct MeshTunnel *t, + struct MeshClient *c) +{ + unsigned int i; + + for (i = 0; i < t->nclients; i++) + { + if (t->clients[i] != c) + continue; + return &t->clients_fc[i]; + } + GNUNET_assert (0); + return NULL; // avoid compiler / coverity complaints +} + + +/** + * Iterator to get the appropiate ACK value from all children nodes. + * + * @param cls Closue (tunnel). + * @param id Id of the child node. + */ +static void +tunnel_get_child_fwd_ack (void *cls, + GNUNET_PEER_Id id) +{ + struct GNUNET_PeerIdentity peer_id; + struct MeshTunnelChildInfo *cinfo; + struct MeshTunnelChildIteratorContext *ctx = cls; + struct MeshTunnel *t = ctx->t; + uint32_t ack; + + GNUNET_PEER_resolve (id, &peer_id); + cinfo = tunnel_get_neighbor_fc (t, &peer_id); + ack = cinfo->fwd_ack; + + ctx->nchildren++; + if (GNUNET_NO == ctx->init) + { + ctx->max_child_ack = ack; + ctx->init = GNUNET_YES; + } + + if (GNUNET_YES == t->speed_min) + { + ctx->max_child_ack = ctx->max_child_ack > ack ? ack : ctx->max_child_ack; + } + else + { + ctx->max_child_ack = ctx->max_child_ack > ack ? ctx->max_child_ack : ack; + } + +} + + +/** + * Get the maximum PID allowed to transmit to any + * tunnel child of the local peer, depending on the tunnel + * buffering/speed settings. + * + * @param t Tunnel. + * + * @return Maximum PID allowed (uint32 MAX), -1LL if node has no children. + */ +static int64_t +tunnel_get_children_fwd_ack (struct MeshTunnel *t) +{ + struct MeshTunnelChildIteratorContext ctx; + ctx.t = t; + ctx.max_child_ack = 0; + ctx.nchildren = 0; + ctx.init = GNUNET_NO; + tree_iterate_children (t->tree, tunnel_get_child_fwd_ack, &ctx); + + if (0 == ctx.nchildren) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " tunnel has no children, no FWD ACK\n"); + return -1LL; + } + + if (GNUNET_YES == t->nobuffer && GMC_is_pid_bigger(ctx.max_child_ack, t->fwd_pid)) + ctx.max_child_ack = t->fwd_pid + 1; // Might overflow, it's ok. + + return (int64_t) ctx.max_child_ack; +} + + +/** + * Set the FWD ACK value of a client in a particular tunnel. + * + * @param t Tunnel affected. + * @param c Client whose ACK to set. + * @param ack ACK value. + */ +static void +tunnel_set_client_fwd_ack (struct MeshTunnel *t, + struct MeshClient *c, + uint32_t ack) +{ + unsigned int i; + + for (i = 0; i < t->nclients; i++) + { + if (t->clients[i] != c) + continue; + t->clients_fc[i].fwd_ack = ack; + return; + } + GNUNET_break (0); +} + + +/** + * Get the highest ACK value of all clients in a particular tunnel, + * according to the buffering/speed settings. + * + * @param t Tunnel on which to look. + * + * @return Corresponding ACK value (max uint32_t). + * If no clients are suscribed, -1LL. + */ +static int64_t +tunnel_get_clients_fwd_ack (struct MeshTunnel *t) +{ + unsigned int i; + int64_t ack; + + if (0 == t->nclients) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " tunnel has no clients, no FWD ACK\n"); + return -1LL; + } + + for (ack = -1LL, i = 0; i < t->nclients; i++) + { + if (-1LL == ack || + (GNUNET_YES == t->speed_min && + GNUNET_YES == GMC_is_pid_bigger (ack, t->clients_fc[i].fwd_ack)) || + (GNUNET_NO == t->speed_min && + GNUNET_YES == GMC_is_pid_bigger (t->clients_fc[i].fwd_ack, ack))) + { + ack = t->clients_fc[i].fwd_ack; + } + } + + if (GNUNET_YES == t->nobuffer && GMC_is_pid_bigger(ack, t->fwd_pid)) + ack = (uint32_t) t->fwd_pid + 1; // Might overflow, it's ok. + + return (uint32_t) ack; +} + + +/** + * Get the current fwd ack value for a tunnel, taking in account the tunnel + * mode and the status of all children nodes. + * + * @param t Tunnel. + * + * @return Maximum PID allowed. + */ +static uint32_t +tunnel_get_fwd_ack (struct MeshTunnel *t) +{ + uint32_t ack; + uint32_t count; + uint32_t buffer_free; + int64_t child_ack; + int64_t client_ack; + + count = t->fwd_pid - t->skip; + buffer_free = t->fwd_queue_max - t->fwd_queue_n; + child_ack = tunnel_get_children_fwd_ack (t); + client_ack = tunnel_get_clients_fwd_ack (t); + if (GNUNET_YES == t->nobuffer) + { + ack = count; + if (-1LL == child_ack) + child_ack = client_ack; + if (-1LL == child_ack) + { + GNUNET_break (0); + client_ack = child_ack = ack; + } + } + else + { + ack = count + buffer_free; // Overflow? OK! + } + if (-1LL == child_ack) + { + // Node has no children, child_ack AND core buffer are irrelevant. + if (-1LL == client_ack) // No children AND no clients? Not good! + { + GNUNET_STATISTICS_update (stats, "# mesh acks with no target", + 1, GNUNET_NO); + + } + return (uint32_t) client_ack; + } + if (-1LL == client_ack) + { + client_ack = ack; + } + if (GNUNET_YES == t->speed_min) + { + ack = GMC_min_pid ((uint32_t) child_ack, ack); + ack = GMC_min_pid ((uint32_t) client_ack, ack); + } + else + { + ack = GMC_max_pid ((uint32_t) child_ack, ack); + ack = GMC_max_pid ((uint32_t) client_ack, ack); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "c %u, bf %u, ch %lld, cl %lld, ACK: %u\n", + count, buffer_free, child_ack, client_ack, ack); + return ack; +} + + +/** + * Build a local ACK message and send it to a local client. + * + * @param t Tunnel on which to send the ACK. + * @param c Client to whom send the ACK. + * @param ack Value of the ACK. + */ +static void +send_local_ack (struct MeshTunnel *t, struct MeshClient *c, uint32_t ack) +{ + struct GNUNET_MESH_LocalAck msg; + + msg.header.size = htons (sizeof (msg)); + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK); + msg.tunnel_id = htonl (t->owner == c ? t->local_tid : t->local_tid_dest); + msg.max_pid = htonl (ack); + GNUNET_SERVER_notification_context_unicast(nc, + c->handle, + &msg.header, + GNUNET_NO); +} + +/** + * Build an ACK message and queue it to send to the given peer. + * + * @param t Tunnel on which to send the ACK. + * @param peer Peer to whom send the ACK. + * @param ack Value of the ACK. + */ +static void +send_ack (struct MeshTunnel *t, struct GNUNET_PeerIdentity *peer, uint32_t ack) +{ + struct GNUNET_MESH_ACK msg; + + GNUNET_PEER_resolve (t->id.oid, &msg.oid); + msg.header.size = htons (sizeof (msg)); + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_ACK); + msg.pid = htonl (ack); + msg.tid = htonl (t->id.tid); + + send_prebuilt_message (&msg.header, peer, t); +} + + +/** + * Notify a the owner of a tunnel about how many more + * payload packages will we accept on a given tunnel. + * + * @param t Tunnel on which to send the ACK. + */ +static void +tunnel_send_client_fwd_ack (struct MeshTunnel *t) +{ + uint32_t ack; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending client FWD ACK on tunnel %X\n", + t->local_tid); + + ack = tunnel_get_fwd_ack (t); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " ack %u\n", ack); + if (t->last_fwd_ack == ack) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " same as last, not sending!\n"); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " sending!\n"); + t->last_fwd_ack = ack; + send_local_ack (t, t->owner, ack); +} + + +/** + * Send an ACK informing the predecessor about the available buffer space. + * In case there is no predecessor, inform the owning client. + * If buffering is off, send only on behalf of children or self if endpoint. + * If buffering is on, send when sent to children and buffer space is free. + * Note that although the name is fwd_ack, the FWD mean forward *traffic*, + * the ACK itself goes "back" (towards root). + * + * @param t Tunnel on which to send the ACK. + * @param type Type of message that triggered the ACK transmission. + */ +static void +tunnel_send_fwd_ack (struct MeshTunnel *t, uint16_t type) +{ + struct GNUNET_PeerIdentity id; + uint32_t ack; + + if (NULL != t->owner) + { + tunnel_send_client_fwd_ack (t); + return; + } + /* Is it after unicast / multicast retransmission? */ + switch (type) + { + case GNUNET_MESSAGE_TYPE_MESH_UNICAST: + case GNUNET_MESSAGE_TYPE_MESH_MULTICAST: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "ACK due to FWD DATA retransmission\n"); + if (GNUNET_YES == t->nobuffer) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Not sending ACK, nobuffer\n"); + return; + } + break; + case GNUNET_MESSAGE_TYPE_MESH_ACK: + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK: + break; + case GNUNET_MESSAGE_TYPE_MESH_POLL: + t->force_ack = GNUNET_YES; + break; + default: + GNUNET_break (0); + } + + /* Check if we need to transmit the ACK */ + if (t->fwd_queue_max > t->fwd_queue_n * 4 && + GMC_is_pid_bigger(t->last_fwd_ack, t->fwd_pid) && + GNUNET_NO == t->force_ack) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Not sending ACK, buffer free\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " t->qmax: %u, t->qn: %u\n", + t->fwd_queue_max, t->fwd_queue_n); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " t->pid: %u, t->ack: %u\n", + t->fwd_pid, t->last_fwd_ack); + return; + } + + /* Ok, ACK might be necessary, what PID to ACK? */ + ack = tunnel_get_fwd_ack (t); + + /* If speed_min and not all children have ack'd, dont send yet */ + if (ack == t->last_fwd_ack && GNUNET_NO == t->force_ack) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Not sending FWD ACK, not ready\n"); + return; + } + + t->last_fwd_ack = ack; + GNUNET_PEER_resolve (tree_get_predecessor (t->tree), &id); + send_ack (t, &id, ack); + debug_fwd_ack++; + t->force_ack = GNUNET_NO; +} + + +/** + * Iterator to send a child node a BCK ACK to allow him to send more + * to_origin data. + * + * @param cls Closure (tunnel). + * @param id Id of the child node. + */ +static void +tunnel_send_child_bck_ack (void *cls, + GNUNET_PEER_Id id) +{ + struct MeshTunnel *t = cls; + struct MeshTunnelChildInfo *cinfo; + struct GNUNET_PeerIdentity peer; + uint32_t ack; + + GNUNET_PEER_resolve (id, &peer); + cinfo = tunnel_get_neighbor_fc (t, &peer); + ack = cinfo->bck_pid + t->bck_queue_max - t->bck_queue_n; + + if (cinfo->bck_ack == ack && GNUNET_NO == t->force_ack) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " Not sending ACK, not needed\n"); + return; + } + cinfo->bck_ack = ack; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " Sending BCK ACK %u (last sent: %u)\n", + ack, cinfo->bck_ack); + send_ack (t, &peer, ack); +} + + +/** + * @brief Send BCK ACKs to clients to allow them more to_origin traffic + * + * Iterates over all clients and sends BCK ACKs to the ones that need it. + * + * FIXME fc: what happens if we have 2 clients but q_size is 1? + * - implement a size 1 buffer in each client_fc AND children_fc + * to hold at least 1 message per "child". + * problem: violates no buffer policy + * - ack 0 and make "children" poll for transmission slots + * problem: big overhead, extra latency even in low traffic + * settings + * + * @param t Tunnel on which to send the BCK ACKs. + */ +static void +tunnel_send_clients_bck_ack (struct MeshTunnel *t) +{ + unsigned int i; + unsigned int tunnel_delta; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Sending BCK ACK to clients\n"); + + tunnel_delta = t->bck_queue_max - t->bck_queue_n; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " tunnel delta: %u\n", tunnel_delta); + + /* Find client whom to allow to send to origin (with lowest buffer space) */ + for (i = 0; i < t->nclients; i++) + { + struct MeshTunnelClientInfo *clinfo; + unsigned int delta; + + clinfo = &t->clients_fc[i]; + delta = clinfo->bck_ack - clinfo->bck_pid; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " client %u delta: %u\n", + t->clients[i]->id, delta); + + if ((GNUNET_NO == t->nobuffer && tunnel_delta > delta) || + (GNUNET_YES == t->nobuffer && 0 == delta)) + { + uint32_t ack; + + ack = clinfo->bck_pid; + ack += t->nobuffer ? 1 : tunnel_delta; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " sending ack to client %u: %u\n", + t->clients[i]->id, ack); + send_local_ack (t, t->clients[i], ack); + clinfo->bck_ack = ack; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " not sending ack to client %u (td %u, d %u)\n", + t->clients[i]->id, tunnel_delta, delta); + } + } +} + + +/** + * Send an ACK informing the children nodes and destination clients about + * the available buffer space. + * If buffering is off, send only on behalf of root (can be self). + * If buffering is on, send when sent to predecessor and buffer space is free. + * Note that although the name is bck_ack, the BCK mean backwards *traffic*, + * the ACK itself goes "forward" (towards children/clients). + * + * @param t Tunnel on which to send the ACK. + * @param type Type of message that triggered the ACK transmission. + */ +static void +tunnel_send_bck_ack (struct MeshTunnel *t, uint16_t type) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending BCK ACK on tunnel %u [%u] due to %s\n", + t->id.oid, t->id.tid, GNUNET_MESH_DEBUG_M2S(type)); + /* Is it after data to_origin retransmission? */ + switch (type) + { + case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: + if (GNUNET_YES == t->nobuffer) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " Not sending ACK, nobuffer\n"); + return; + } + break; + case GNUNET_MESSAGE_TYPE_MESH_ACK: + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK: + break; + case GNUNET_MESSAGE_TYPE_MESH_POLL: + t->force_ack = GNUNET_YES; + break; + default: + GNUNET_break (0); + } + + tunnel_send_clients_bck_ack (t); + tree_iterate_children (t->tree, &tunnel_send_child_bck_ack, t); + t->force_ack = GNUNET_NO; +} + + +/** + * @brief Re-initiate traffic to this peer if necessary. + * + * Check if there is traffic queued towards this peer + * and the core transmit handle is NULL (traffic was stalled). + * If so, call core tmt rdy. + * + * @param cls Closure (unused) + * @param peer_id Short ID of peer to which initiate traffic. + */ +static void +peer_unlock_queue(void *cls, GNUNET_PEER_Id peer_id) +{ + struct MeshPeerInfo *peer; + struct GNUNET_PeerIdentity id; + struct MeshPeerQueue *q; + size_t size; + + peer = peer_info_get_short(peer_id); + if (NULL != peer->core_transmit) + return; + + q = queue_get_next(peer); + if (NULL == q) + { + /* Might br multicast traffic already sent to this particular peer but + * not to other children in this tunnel. + * This way t->queue_n would be > 0 but the queue of this particular peer + * would be empty. + */ + return; + } + size = q->size; + GNUNET_PEER_resolve (peer->id, &id); + peer->core_transmit = + GNUNET_CORE_notify_transmit_ready(core_handle, + 0, + 0, + GNUNET_TIME_UNIT_FOREVER_REL, + &id, + size, + &queue_send, + peer); + return; +} + + +/** + * @brief Allow transmission of FWD traffic on this tunnel + * + * Check if there is traffic queued towards any children + * and the core transmit handle is NULL, and if so, call core tmt rdy. + * + * @param t Tunnel on which to unlock FWD traffic. + */ +static void +tunnel_unlock_fwd_queues (struct MeshTunnel *t) +{ + if (0 == t->fwd_queue_n) + return; + + tree_iterate_children (t->tree, &peer_unlock_queue, NULL); +} + + +/** + * @brief Allow transmission of BCK traffic on this tunnel + * + * Check if there is traffic queued towards the root of the tree + * and the core transmit handle is NULL, and if so, call core tmt rdy. + * + * @param t Tunnel on which to unlock BCK traffic. + */ +static void +tunnel_unlock_bck_queue (struct MeshTunnel *t) +{ + if (0 == t->bck_queue_n) + return; + + peer_unlock_queue(NULL, tree_get_predecessor(t->tree)); +} + + +/** + * Send a message to all peers in this tunnel that the tunnel is no longer + * valid. + * + * @param t The tunnel whose peers to notify. + * @param parent ID of the parent, in case the tree is already destroyed. + */ +static void +tunnel_send_destroy (struct MeshTunnel *t, GNUNET_PEER_Id parent) +{ + struct GNUNET_MESH_TunnelDestroy msg; + struct GNUNET_PeerIdentity id; + + msg.header.size = htons (sizeof (msg)); + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_TUNNEL_DESTROY); + GNUNET_PEER_resolve (t->id.oid, &msg.oid); + msg.tid = htonl (t->id.tid); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " sending tunnel destroy for tunnel: %s [%X]\n", + GNUNET_i2s (&msg.oid), t->id.tid); + if (tree_count_children(t->tree) > 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " sending multicast to children\n"); + tunnel_send_multicast (t, &msg.header); + } + if (0 == parent) + parent = tree_get_predecessor (t->tree); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " parent: %u\n", parent); + if (0 == parent) + return; + + GNUNET_PEER_resolve (parent, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " sending back to %s\n", + GNUNET_i2s (&id)); + send_prebuilt_message (&msg.header, &id, t); +} + + +/** + * Cancel all transmissions towards a neighbor that belong to a certain tunnel. + * + * @param cls Closure (Tunnel which to cancel). + * @param neighbor_id Short ID of the neighbor to whom cancel the transmissions. + */ +static void +tunnel_cancel_queues (void *cls, GNUNET_PEER_Id neighbor_id) +{ + struct MeshTunnel *t = cls; + struct MeshPeerInfo *peer_info; + struct MeshPeerQueue *pq; + struct MeshPeerQueue *next; + + peer_info = peer_info_get_short (neighbor_id); + for (pq = peer_info->queue_head; NULL != pq; pq = next) + { + next = pq->next; + if (pq->tunnel == t) + { + if (GNUNET_MESSAGE_TYPE_MESH_MULTICAST == pq->type || + GNUNET_MESSAGE_TYPE_MESH_UNICAST == pq->type || + GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN == pq->type) + { + // Should have been removed on destroy children + GNUNET_break (0); + } + queue_destroy (pq, GNUNET_YES); + } + } + if (NULL == peer_info->queue_head && NULL != peer_info->core_transmit) + { + GNUNET_CORE_notify_transmit_ready_cancel(peer_info->core_transmit); + peer_info->core_transmit = NULL; + } +} + +/** + * Destroy the tunnel and free any allocated resources linked to it. + * + * @param t the tunnel to destroy + * + * @return GNUNET_OK on success + */ +static int +tunnel_destroy (struct MeshTunnel *t) +{ + struct MeshClient *c; + struct GNUNET_HashCode hash; + unsigned int i; + int r; + + if (NULL == t) + return GNUNET_OK; + + r = GNUNET_OK; + c = t->owner; +#if MESH_DEBUG + { + struct GNUNET_PeerIdentity id; + + GNUNET_PEER_resolve (t->id.oid, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "destroying tunnel %s [%x]\n", + GNUNET_i2s (&id), t->id.tid); + if (NULL != c) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + } +#endif + + GNUNET_CRYPTO_hash (&t->id, sizeof (struct MESH_TunnelID), &hash); + if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_remove (tunnels, &hash, t)) + { + GNUNET_break (0); + r = GNUNET_SYSERR; + } + + if (NULL != c) + { + GNUNET_CRYPTO_hash (&t->local_tid, sizeof (MESH_TunnelNumber), &hash); + if (GNUNET_YES != + GNUNET_CONTAINER_multihashmap_remove (c->own_tunnels, &hash, t)) + { + GNUNET_break (0); + r = GNUNET_SYSERR; + } + } + + GNUNET_CRYPTO_hash (&t->local_tid_dest, sizeof (MESH_TunnelNumber), &hash); + for (i = 0; i < t->nclients; i++) + { + c = t->clients[i]; + if (GNUNET_YES != + GNUNET_CONTAINER_multihashmap_remove (c->incoming_tunnels, &hash, t)) + { + GNUNET_break (0); + r = GNUNET_SYSERR; + } + } + for (i = 0; i < t->nignore; i++) + { + c = t->ignore[i]; + if (GNUNET_YES != + GNUNET_CONTAINER_multihashmap_remove (c->ignore_tunnels, &hash, t)) + { + GNUNET_break (0); + r = GNUNET_SYSERR; + } + } + + (void) GNUNET_CONTAINER_multihashmap_remove (incoming_tunnels, &hash, t); + GNUNET_free_non_null (t->clients); + GNUNET_free_non_null (t->ignore); + GNUNET_free_non_null (t->clients_fc); + + if (NULL != t->peers) + { + GNUNET_CONTAINER_multihashmap_iterate (t->peers, &peer_info_delete_tunnel, + t); + GNUNET_CONTAINER_multihashmap_destroy (t->peers); + } + + GNUNET_CONTAINER_multihashmap_iterate (t->children_fc, + &tunnel_destroy_child, + t); + GNUNET_CONTAINER_multihashmap_destroy (t->children_fc); + t->children_fc = NULL; + + tree_iterate_children (t->tree, &tunnel_cancel_queues, t); + tree_destroy (t->tree); + + if (NULL != t->regex_search) + GNUNET_REGEX_search_cancel (t->regex_search->search_handle); + if (NULL != t->dht_get_type) + GNUNET_DHT_get_stop (t->dht_get_type); + if (GNUNET_SCHEDULER_NO_TASK != t->timeout_task) + GNUNET_SCHEDULER_cancel (t->timeout_task); + if (GNUNET_SCHEDULER_NO_TASK != t->path_refresh_task) + GNUNET_SCHEDULER_cancel (t->path_refresh_task); + + n_tunnels--; + GNUNET_STATISTICS_update (stats, "# tunnels", -1, GNUNET_NO); + GNUNET_free (t); + return r; +} + +#define TUNNEL_DESTROY_EMPTY_TIME GNUNET_TIME_UNIT_MILLISECONDS + +/** + * Tunnel is empty: destroy it. + * + * @param cls Closure (Tunnel). + * @param tc TaskContext. + */ +static void +tunnel_destroy_empty_delayed (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct MeshTunnel *t = cls; + + t->delayed_destroy = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + + if (0 != t->nclients || + 0 != tree_count_children (t->tree)) + return; + + #if MESH_DEBUG + { + struct GNUNET_PeerIdentity id; + + GNUNET_PEER_resolve (t->id.oid, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "executing destruction of empty tunnel %s [%X]\n", + GNUNET_i2s (&id), t->id.tid); + } + #endif + + tunnel_send_destroy (t, 0); + if (0 == t->pending_messages) + tunnel_destroy (t); + else + t->destroy = GNUNET_YES; +} + + +/** + * Schedule tunnel destruction if is empty and no new traffic comes in a time. + * + * @param t Tunnel to destroy if empty. + */ +static void +tunnel_destroy_empty (struct MeshTunnel *t) +{ + if (GNUNET_SCHEDULER_NO_TASK != t->delayed_destroy || + 0 != t->nclients || + 0 != tree_count_children (t->tree)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%u %u %u\n", + t->delayed_destroy, t->nclients, tree_count_children(t->tree)); + return; + } + + #if MESH_DEBUG + { + struct GNUNET_PeerIdentity id; + + GNUNET_PEER_resolve (t->id.oid, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "scheduling destruction of empty tunnel %s [%X]\n", + GNUNET_i2s (&id), t->id.tid); + } + #endif + + t->delayed_destroy = + GNUNET_SCHEDULER_add_delayed (TUNNEL_DESTROY_EMPTY_TIME, + &tunnel_destroy_empty_delayed, + t); +} + + +/** + * Create a new tunnel + * + * @param owner Who is the owner of the tunnel (short ID). + * @param tid Tunnel Number of the tunnel. + * @param client Clients that owns the tunnel, NULL for foreign tunnels. + * @param local Tunnel Number for the tunnel, for the client point of view. + * + * @return A new initialized tunnel. NULL on error. + */ +static struct MeshTunnel * +tunnel_new (GNUNET_PEER_Id owner, + MESH_TunnelNumber tid, + struct MeshClient *client, + MESH_TunnelNumber local) +{ + struct MeshTunnel *t; + struct GNUNET_HashCode hash; + + if (n_tunnels >= max_tunnels && NULL == client) + return NULL; + + t = GNUNET_malloc (sizeof (struct MeshTunnel)); + t->id.oid = owner; + t->id.tid = tid; + t->fwd_queue_max = (max_msgs_queue / max_tunnels) + 1; + t->bck_queue_max = t->fwd_queue_max; + t->tree = tree_new (owner); + t->owner = client; + t->fwd_pid = (uint32_t) -1; // Next (expected) = 0 + t->bck_pid = (uint32_t) -1; // Next (expected) = 0 + t->bck_ack = INITIAL_WINDOW_SIZE - 1; + t->last_fwd_ack = INITIAL_WINDOW_SIZE - 1; + t->local_tid = local; + t->children_fc = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); + n_tunnels++; + GNUNET_STATISTICS_update (stats, "# tunnels", 1, GNUNET_NO); + + GNUNET_CRYPTO_hash (&t->id, sizeof (struct MESH_TunnelID), &hash); + if (GNUNET_OK != + GNUNET_CONTAINER_multihashmap_put (tunnels, &hash, t, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + { + GNUNET_break (0); + tunnel_destroy (t); + if (NULL != client) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client->handle, GNUNET_SYSERR); + } + return NULL; + } + + if (NULL != client) + { + GNUNET_CRYPTO_hash (&t->local_tid, sizeof (MESH_TunnelNumber), &hash); + if (GNUNET_OK != + GNUNET_CONTAINER_multihashmap_put (client->own_tunnels, &hash, t, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + { + tunnel_destroy (t); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client->handle, GNUNET_SYSERR); + return NULL; + } + } + + return t; +} + +/** + * Callback when removing children from a tunnel tree. Notify owner. + * + * @param cls Closure (tunnel). + * @param peer_id Short ID of the peer deleted. + */ +void +tunnel_child_removed (void *cls, GNUNET_PEER_Id peer_id) +{ + struct MeshTunnel *t = cls; + + client_notify_peer_disconnected (t->owner, t, peer_id); +} + +/** + * Removes an explicit path from a tunnel, freeing all intermediate nodes + * that are no longer needed, as well as nodes of no longer reachable peers. + * The tunnel itself is also destoyed if results in a remote empty tunnel. + * + * @param t Tunnel from which to remove the path. + * @param peer Short id of the peer which should be removed. + */ +static void +tunnel_delete_peer (struct MeshTunnel *t, GNUNET_PEER_Id peer) +{ + int r; + + r = tree_del_peer (t->tree, peer, &tunnel_child_removed, t); + if (GNUNET_NO == r) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Tunnel %u [%u] has no more nodes\n", + t->id.oid, t->id.tid); + } +} + + +/** + * tunnel_destroy_iterator: iterator for deleting each tunnel that belongs to a + * client when the client disconnects. If the client is not the owner, the + * owner will get notified if no more clients are in the tunnel and the client + * get removed from the tunnel's list. + * + * @param cls closure (client that is disconnecting) + * @param key the hash of the local tunnel id (used to access the hashmap) + * @param value the value stored at the key (tunnel to destroy) + * + * @return GNUNET_OK, keep iterating. + */ +static int +tunnel_destroy_iterator (void *cls, + const struct GNUNET_HashCode * key, + void *value) +{ + struct MeshTunnel *t = value; + struct MeshClient *c = cls; + + send_client_tunnel_disconnect (t, c); + if (c != t->owner) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %u is destination.\n", c->id); + tunnel_delete_client (t, c); + client_delete_tunnel (c, t); + tunnel_destroy_empty (t); + return GNUNET_OK; + } + tunnel_send_destroy (t, 0); + t->owner = NULL; + t->destroy = GNUNET_YES; + + return GNUNET_OK; +} + + +/** + * Timeout function, destroys tunnel if called + * + * @param cls Closure (tunnel to destroy). + * @param tc TaskContext + */ +static void +tunnel_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct MeshTunnel *t = cls; + struct GNUNET_PeerIdentity id; + + t->timeout_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + GNUNET_PEER_resolve(t->id.oid, &id); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Tunnel %s [%X] timed out. Destroying.\n", + GNUNET_i2s(&id), t->id.tid); + send_clients_tunnel_destroy (t); + tunnel_destroy (t); +} + +/** + * Resets the tunnel timeout. Starts it if no timeout was running. + * + * @param t Tunnel whose timeout to reset. + * + * TODO use heap to improve efficiency of scheduler. + */ +static void +tunnel_reset_timeout (struct MeshTunnel *t) +{ + if (GNUNET_SCHEDULER_NO_TASK != t->timeout_task) + GNUNET_SCHEDULER_cancel (t->timeout_task); + t->timeout_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (refresh_path_time, 4), &tunnel_timeout, t); +} + + +/******************************************************************************/ +/**************** MESH NETWORK HANDLER HELPERS ***********************/ +/******************************************************************************/ + +/** + * Function to send a create path packet to a peer. + * + * @param cls closure + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +send_core_path_create (void *cls, size_t size, void *buf) +{ + struct MeshPathInfo *info = cls; + struct GNUNET_MESH_ManipulatePath *msg; + struct GNUNET_PeerIdentity *peer_ptr; + struct MeshTunnel *t = info->t; + struct MeshPeerPath *p = info->path; + size_t size_needed; + uint32_t opt; + int i; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CREATE PATH sending...\n"); + size_needed = + sizeof (struct GNUNET_MESH_ManipulatePath) + + p->length * sizeof (struct GNUNET_PeerIdentity); + + if (size < size_needed || NULL == buf) + { + GNUNET_break (0); + return 0; + } + msg = (struct GNUNET_MESH_ManipulatePath *) buf; + msg->header.size = htons (size_needed); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_PATH_CREATE); + msg->tid = ntohl (t->id.tid); + + opt = 0; + if (GNUNET_YES == t->speed_min) + opt |= MESH_TUNNEL_OPT_SPEED_MIN; + if (GNUNET_YES == t->nobuffer) + opt |= MESH_TUNNEL_OPT_NOBUFFER; + msg->opt = htonl(opt); + msg->reserved = 0; + + peer_ptr = (struct GNUNET_PeerIdentity *) &msg[1]; + for (i = 0; i < p->length; i++) + { + GNUNET_PEER_resolve (p->peers[i], peer_ptr++); + } + + path_destroy (p); + GNUNET_free (info); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "CREATE PATH (%u bytes long) sent!\n", size_needed); + return size_needed; +} + + +/** + * Fill the core buffer + * + * @param cls closure (data itself) + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * + * @return number of bytes written to buf + */ +static size_t +send_core_data_multicast (void *cls, size_t size, void *buf) +{ + struct MeshTransmissionDescriptor *info = cls; + size_t total_size; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Multicast callback.\n"); + GNUNET_assert (NULL != info); + GNUNET_assert (NULL != info->peer); + total_size = info->mesh_data->data_len; + GNUNET_assert (total_size < GNUNET_SERVER_MAX_MESSAGE_SIZE); + + if (total_size > size) + { + GNUNET_break (0); + return 0; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " copying data...\n"); + memcpy (buf, info->mesh_data->data, total_size); +#if MESH_DEBUG + { + struct GNUNET_MESH_Multicast *mc; + struct GNUNET_MessageHeader *mh; + + mh = buf; + if (ntohs (mh->type) == GNUNET_MESSAGE_TYPE_MESH_MULTICAST) + { + mc = (struct GNUNET_MESH_Multicast *) mh; + mh = (struct GNUNET_MessageHeader *) &mc[1]; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " multicast, payload type %s\n", + GNUNET_MESH_DEBUG_M2S (ntohs (mh->type))); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " multicast, payload size %u\n", ntohs (mh->size)); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " type %s\n", + GNUNET_MESH_DEBUG_M2S (ntohs (mh->type))); + } + } +#endif + data_descriptor_decrement_rc (info->mesh_data); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "freeing info...\n"); + GNUNET_free (info); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "return %u\n", total_size); + return total_size; +} + + +/** + * Creates a path ack message in buf and frees all unused resources. + * + * @param cls closure (MeshTransmissionDescriptor) + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +send_core_path_ack (void *cls, size_t size, void *buf) +{ + struct MeshTransmissionDescriptor *info = cls; + struct GNUNET_MESH_PathACK *msg = buf; + + GNUNET_assert (NULL != info); + if (sizeof (struct GNUNET_MESH_PathACK) > size) + { + GNUNET_break (0); + return 0; + } + msg->header.size = htons (sizeof (struct GNUNET_MESH_PathACK)); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_PATH_ACK); + GNUNET_PEER_resolve (info->origin->oid, &msg->oid); + msg->tid = htonl (info->origin->tid); + msg->peer_id = my_full_id; + + GNUNET_free (info); + /* TODO add signature */ + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "PATH ACK sent!\n"); + return sizeof (struct GNUNET_MESH_PathACK); +} + + +/** + * Free a transmission that was already queued with all resources + * associated to the request. + * + * @param queue Queue handler to cancel. + * @param clear_cls Is it necessary to free associated cls? + */ +static void +queue_destroy (struct MeshPeerQueue *queue, int clear_cls) +{ + struct MeshTransmissionDescriptor *dd; + struct MeshPathInfo *path_info; + struct MeshTunnelChildInfo *cinfo; + struct GNUNET_PeerIdentity id; + unsigned int i; + unsigned int max; + + if (GNUNET_YES == clear_cls) + { + switch (queue->type) + { + case GNUNET_MESSAGE_TYPE_MESH_TUNNEL_DESTROY: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, " cancelling TUNNEL_DESTROY\n"); + GNUNET_break (GNUNET_YES == queue->tunnel->destroy); + /* fall through */ + case GNUNET_MESSAGE_TYPE_MESH_UNICAST: + case GNUNET_MESSAGE_TYPE_MESH_MULTICAST: + case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: + case GNUNET_MESSAGE_TYPE_MESH_ACK: + case GNUNET_MESSAGE_TYPE_MESH_POLL: + case GNUNET_MESSAGE_TYPE_MESH_PATH_KEEPALIVE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " prebuilt message\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " type %s\n", + GNUNET_MESH_DEBUG_M2S(queue->type)); + dd = queue->cls; + data_descriptor_decrement_rc (dd->mesh_data); + break; + case GNUNET_MESSAGE_TYPE_MESH_PATH_CREATE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " type create path\n"); + path_info = queue->cls; + path_destroy (path_info->path); + break; + default: + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + " type %s unknown!\n", + GNUNET_MESH_DEBUG_M2S(queue->type)); + } + GNUNET_free_non_null (queue->cls); + } + GNUNET_CONTAINER_DLL_remove (queue->peer->queue_head, + queue->peer->queue_tail, + queue); + + /* Delete from child_fc in the appropiate tunnel */ + max = queue->tunnel->fwd_queue_max; + GNUNET_PEER_resolve (queue->peer->id, &id); + cinfo = tunnel_get_neighbor_fc (queue->tunnel, &id); + if (NULL != cinfo) + { + for (i = 0; i < cinfo->send_buffer_n; i++) + { + unsigned int i2; + i2 = (cinfo->send_buffer_start + i) % max; + if (cinfo->send_buffer[i2] == queue) + { + /* Found corresponding entry in the send_buffer. Move all others back. */ + unsigned int j; + unsigned int j2; + unsigned int j3; + + for (j = i, j2 = 0, j3 = 0; j < cinfo->send_buffer_n - 1; j++) + { + j2 = (cinfo->send_buffer_start + j) % max; + j3 = (cinfo->send_buffer_start + j + 1) % max; + cinfo->send_buffer[j2] = cinfo->send_buffer[j3]; + } + + cinfo->send_buffer[j3] = NULL; + cinfo->send_buffer_n--; + } + } + } + + GNUNET_free (queue); +} + + +/** + * @brief Get the next transmittable message from the queue. + * + * This will be the head, except in the case of being a data packet + * not allowed by the destination peer. + * + * @param peer Destination peer. + * + * @return The next viable MeshPeerQueue element to send to that peer. + * NULL when there are no transmittable messages. + */ +struct MeshPeerQueue * +queue_get_next (const struct MeshPeerInfo *peer) +{ + struct MeshPeerQueue *q; + struct MeshTunnel *t; + struct MeshTransmissionDescriptor *info; + struct MeshTunnelChildInfo *cinfo; + struct GNUNET_MESH_Unicast *ucast; + struct GNUNET_MESH_ToOrigin *to_orig; + struct GNUNET_MESH_Multicast *mcast; + struct GNUNET_PeerIdentity id; + uint32_t pid; + uint32_t ack; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* selecting message\n"); + for (q = peer->queue_head; NULL != q; q = q->next) + { + t = q->tunnel; + info = q->cls; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* %s\n", + GNUNET_MESH_DEBUG_M2S(q->type)); + switch (q->type) + { + case GNUNET_MESSAGE_TYPE_MESH_UNICAST: + ucast = (struct GNUNET_MESH_Unicast *) info->mesh_data->data; + pid = ntohl (ucast->pid); + GNUNET_PEER_resolve (info->peer->id, &id); + cinfo = tunnel_get_neighbor_fc(t, &id); + ack = cinfo->fwd_ack; + break; + case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: + to_orig = (struct GNUNET_MESH_ToOrigin *) info->mesh_data->data; + pid = ntohl (to_orig->pid); + ack = t->bck_ack; + break; + case GNUNET_MESSAGE_TYPE_MESH_MULTICAST: + mcast = (struct GNUNET_MESH_Multicast *) info->mesh_data->data; + if (GNUNET_MESSAGE_TYPE_MESH_MULTICAST != ntohs(mcast->header.type)) + { + // Not a multicast payload: multicast control traffic (destroy, etc) + return q; + } + pid = ntohl (mcast->pid); + GNUNET_PEER_resolve (info->peer->id, &id); + cinfo = tunnel_get_neighbor_fc(t, &id); + ack = cinfo->fwd_ack; + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* OK!\n"); + return q; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* ACK: %u, PID: %u\n", + ack, pid); + if (GNUNET_NO == GMC_is_pid_bigger(pid, ack)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* OK!\n"); + return q; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* NEXT!\n"); + } + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* nothing found\n"); + return NULL; +} + + +/** + * Core callback to write a queued packet to core buffer + * + * @param cls Closure (peer info). + * @param size Number of bytes available in buf. + * @param buf Where the to write the message. + * + * @return number of bytes written to buf + */ +static size_t +queue_send (void *cls, size_t size, void *buf) +{ + struct MeshPeerInfo *peer = cls; + struct GNUNET_MessageHeader *msg; + struct MeshPeerQueue *queue; + struct MeshTunnel *t; + struct MeshTunnelChildInfo *cinfo; + struct GNUNET_PeerIdentity dst_id; + size_t data_size; + + peer->core_transmit = NULL; + cinfo = NULL; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* Queue send\n"); + queue = queue_get_next (peer); + + /* Queue has no internal mesh traffic nor sendable payload */ + if (NULL == queue) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* not ready, return\n"); + if (NULL == peer->queue_head) + GNUNET_break (0); // Should've been canceled + return 0; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* not empty\n"); + + GNUNET_PEER_resolve (peer->id, &dst_id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* towards %s\n", + GNUNET_i2s(&dst_id)); + /* Check if buffer size is enough for the message */ + if (queue->size > size) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* not enough room, reissue\n"); + peer->core_transmit = + GNUNET_CORE_notify_transmit_ready (core_handle, + 0, + 0, + GNUNET_TIME_UNIT_FOREVER_REL, + &dst_id, + queue->size, + &queue_send, + peer); + return 0; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* size ok\n"); + + t = queue->tunnel; + GNUNET_assert (0 < t->pending_messages); + t->pending_messages--; + if (GNUNET_MESSAGE_TYPE_MESH_UNICAST == queue->type) + { + t->fwd_queue_n--; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* unicast: t->q (%u/%u)\n", + t->fwd_queue_n, t->fwd_queue_max); + } + else if (GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN == queue->type) + { + t->bck_queue_n--; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* to origin\n"); + } + + /* Fill buf */ + switch (queue->type) + { + case 0: + case GNUNET_MESSAGE_TYPE_MESH_ACK: + case GNUNET_MESSAGE_TYPE_MESH_POLL: + case GNUNET_MESSAGE_TYPE_MESH_PATH_BROKEN: + case GNUNET_MESSAGE_TYPE_MESH_PATH_DESTROY: + case GNUNET_MESSAGE_TYPE_MESH_TUNNEL_DESTROY: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* raw: %s\n", + GNUNET_MESH_DEBUG_M2S (queue->type)); + /* Fall through */ + case GNUNET_MESSAGE_TYPE_MESH_UNICAST: + case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: + data_size = send_core_data_raw (queue->cls, size, buf); + msg = (struct GNUNET_MessageHeader *) buf; + switch (ntohs (msg->type)) // Type of preconstructed message + { + case GNUNET_MESSAGE_TYPE_MESH_UNICAST: + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_UNICAST); + break; + case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: + tunnel_send_bck_ack (t, GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN); + break; + default: + break; + } + break; + case GNUNET_MESSAGE_TYPE_MESH_MULTICAST: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* multicast\n"); + { + struct MeshTransmissionDescriptor *info = queue->cls; + + if ((1 == info->mesh_data->reference_counter + && GNUNET_YES == t->speed_min) + || + (info->mesh_data->total_out == info->mesh_data->reference_counter + && GNUNET_NO == t->speed_min)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* considered sent\n"); + t->fwd_queue_n--; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* NOT considered sent yet\n"); + t->pending_messages++; + } + } + data_size = send_core_data_multicast(queue->cls, size, buf); + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_MULTICAST); + break; + case GNUNET_MESSAGE_TYPE_MESH_PATH_CREATE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* path create\n"); + data_size = send_core_path_create (queue->cls, size, buf); + break; + case GNUNET_MESSAGE_TYPE_MESH_PATH_ACK: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* path ack\n"); + data_size = send_core_path_ack (queue->cls, size, buf); + break; + case GNUNET_MESSAGE_TYPE_MESH_PATH_KEEPALIVE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* path keepalive\n"); + data_size = send_core_data_multicast (queue->cls, size, buf); + break; + default: + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "********* type unknown: %u\n", + queue->type); + data_size = 0; + } + switch (queue->type) + { + case GNUNET_MESSAGE_TYPE_MESH_UNICAST: + case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: + case GNUNET_MESSAGE_TYPE_MESH_MULTICAST: + cinfo = tunnel_get_neighbor_fc (t, &dst_id); + if (cinfo->send_buffer[cinfo->send_buffer_start] != queue) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "at pos %u (%p) != %p\n", + cinfo->send_buffer_start, + cinfo->send_buffer[cinfo->send_buffer_start], + queue); + } + if (cinfo->send_buffer_n > 0) + { + cinfo->send_buffer[cinfo->send_buffer_start] = NULL; + cinfo->send_buffer_n--; + cinfo->send_buffer_start++; + cinfo->send_buffer_start %= t->fwd_queue_max; + } + else + { + GNUNET_break (0); + } + break; + default: + break; + } + + /* Free queue, but cls was freed by send_core_* */ + queue_destroy (queue, GNUNET_NO); + + if (GNUNET_YES == t->destroy && 0 == t->pending_messages) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* destroying tunnel!\n"); + tunnel_destroy (t); + } + + /* If more data in queue, send next */ + queue = queue_get_next(peer); + if (NULL != queue) + { + struct GNUNET_PeerIdentity id; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* more data!\n"); + GNUNET_PEER_resolve (peer->id, &id); + peer->core_transmit = + GNUNET_CORE_notify_transmit_ready(core_handle, + 0, + 0, + GNUNET_TIME_UNIT_FOREVER_REL, + &id, + queue->size, + &queue_send, + peer); + } + else + { + if (NULL != peer->queue_head) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "********* %s stalled\n", + GNUNET_i2s(&my_full_id)); + if (NULL == cinfo) + cinfo = tunnel_get_neighbor_fc (t, &dst_id); + // FIXME unify bck/fwd structures, bck does not have cinfo right now + if (NULL != cinfo && GNUNET_SCHEDULER_NO_TASK == cinfo->fc_poll) + { + cinfo->fc_poll = GNUNET_SCHEDULER_add_delayed (cinfo->fc_poll_time, + &tunnel_poll, cinfo); + } + } + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* return %d\n", data_size); + return data_size; +} + + +/** + * @brief Queue and pass message to core when possible. + * + * If type is payload (UNICAST, TO_ORIGIN, MULTICAST) checks for queue status + * and accounts for it. In case the queue is full, the message is dropped and + * a break issued. + * + * Otherwise, message is treated as internal and allowed to go regardless of + * queue status. + * + * @param cls Closure (@c type dependant). It will be used by queue_send to + * build the message to be sent if not already prebuilt. + * @param type Type of the message, 0 for a raw message. + * @param size Size of the message. + * @param dst Neighbor to send message to. + * @param t Tunnel this message belongs to. + */ +static void +queue_add (void *cls, uint16_t type, size_t size, + struct MeshPeerInfo *dst, struct MeshTunnel *t) +{ + struct MeshPeerQueue *queue; + struct MeshTunnelChildInfo *cinfo; + struct GNUNET_PeerIdentity id; + unsigned int *max; + unsigned int *n; + unsigned int i; + + n = NULL; + if (GNUNET_MESSAGE_TYPE_MESH_UNICAST == type || + GNUNET_MESSAGE_TYPE_MESH_MULTICAST == type) + { + n = &t->fwd_queue_n; + max = &t->fwd_queue_max; + } + else if (GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN == type) + { + n = &t->bck_queue_n; + max = &t->bck_queue_max; + } + if (NULL != n) + { + if (*n >= *max) + { + GNUNET_break(0); + GNUNET_STATISTICS_update(stats, + "# messages dropped (buffer full)", + 1, GNUNET_NO); + return; // Drop message + } + (*n)++; + } + queue = GNUNET_malloc (sizeof (struct MeshPeerQueue)); + queue->cls = cls; + queue->type = type; + queue->size = size; + queue->peer = dst; + queue->tunnel = t; + GNUNET_CONTAINER_DLL_insert_tail (dst->queue_head, dst->queue_tail, queue); + GNUNET_PEER_resolve (dst->id, &id); + if (NULL == dst->core_transmit) + { + dst->core_transmit = + GNUNET_CORE_notify_transmit_ready (core_handle, + 0, + 0, + GNUNET_TIME_UNIT_FOREVER_REL, + &id, + size, + &queue_send, + dst); + } + t->pending_messages++; + if (NULL == n) // Is this internal mesh traffic? + return; + + // It's payload, keep track of buffer per peer. + cinfo = tunnel_get_neighbor_fc(t, &id); + i = (cinfo->send_buffer_start + cinfo->send_buffer_n) % t->fwd_queue_max; + if (NULL != cinfo->send_buffer[i]) + { + GNUNET_break (cinfo->send_buffer_n == t->fwd_queue_max); // aka i == start + queue_destroy (cinfo->send_buffer[cinfo->send_buffer_start], GNUNET_YES); + cinfo->send_buffer_start++; + cinfo->send_buffer_start %= t->fwd_queue_max; + } + else + { + cinfo->send_buffer_n++; + } + cinfo->send_buffer[i] = queue; + if (cinfo->send_buffer_n > t->fwd_queue_max) + { + GNUNET_break (0); + cinfo->send_buffer_n = t->fwd_queue_max; + } +} + + +/******************************************************************************/ +/******************** MESH NETWORK HANDLERS **************************/ +/******************************************************************************/ + + +/** + * Core handler for path creation + * + * @param cls closure + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_mesh_path_create (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + unsigned int own_pos; + uint16_t size; + uint16_t i; + MESH_TunnelNumber tid; + struct GNUNET_MESH_ManipulatePath *msg; + struct GNUNET_PeerIdentity *pi; + struct GNUNET_HashCode hash; + struct MeshPeerPath *path; + struct MeshPeerInfo *dest_peer_info; + struct MeshPeerInfo *orig_peer_info; + struct MeshTunnel *t; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received a path create msg [%s]\n", + GNUNET_i2s (&my_full_id)); + size = ntohs (message->size); + if (size < sizeof (struct GNUNET_MESH_ManipulatePath)) + { + GNUNET_break_op (0); + return GNUNET_OK; + } + + size -= sizeof (struct GNUNET_MESH_ManipulatePath); + if (size % sizeof (struct GNUNET_PeerIdentity)) + { + GNUNET_break_op (0); + return GNUNET_OK; + } + size /= sizeof (struct GNUNET_PeerIdentity); + if (size < 2) + { + GNUNET_break_op (0); + return GNUNET_OK; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " path has %u hops.\n", size); + msg = (struct GNUNET_MESH_ManipulatePath *) message; + + tid = ntohl (msg->tid); + pi = (struct GNUNET_PeerIdentity *) &msg[1]; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " path is for tunnel %s [%X].\n", GNUNET_i2s (pi), tid); + t = tunnel_get (pi, tid); + if (NULL == t) // FIXME only for INCOMING tunnels? + { + uint32_t opt; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Creating tunnel\n"); + t = tunnel_new (GNUNET_PEER_intern (pi), tid, NULL, 0); + if (NULL == t) + { + // FIXME notify failure + return GNUNET_OK; + } + opt = ntohl (msg->opt); + t->speed_min = (0 != (opt & MESH_TUNNEL_OPT_SPEED_MIN)) ? + GNUNET_YES : GNUNET_NO; + if (0 != (opt & MESH_TUNNEL_OPT_NOBUFFER)) + { + t->nobuffer = GNUNET_YES; + t->last_fwd_ack = t->fwd_pid + 1; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " speed_min: %d, nobuffer:%d\n", + t->speed_min, t->nobuffer); + + if (GNUNET_YES == t->nobuffer) + { + t->bck_queue_max = 1; + t->fwd_queue_max = 1; + } + + // FIXME only assign a local tid if a local client is interested (on demand) + while (NULL != tunnel_get_incoming (next_local_tid)) + next_local_tid = (next_local_tid + 1) | GNUNET_MESH_LOCAL_TUNNEL_ID_SERV; + t->local_tid_dest = next_local_tid++; + next_local_tid = next_local_tid | GNUNET_MESH_LOCAL_TUNNEL_ID_SERV; + // FIXME end + + tunnel_reset_timeout (t); + GNUNET_CRYPTO_hash (&t->local_tid_dest, sizeof (MESH_TunnelNumber), &hash); + if (GNUNET_OK != + GNUNET_CONTAINER_multihashmap_put (incoming_tunnels, &hash, t, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + { + tunnel_destroy (t); + GNUNET_break (0); + return GNUNET_OK; + } + } + dest_peer_info = + GNUNET_CONTAINER_multihashmap_get (peers, &pi[size - 1].hashPubKey); + if (NULL == dest_peer_info) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " Creating PeerInfo for destination.\n"); + dest_peer_info = GNUNET_malloc (sizeof (struct MeshPeerInfo)); + dest_peer_info->id = GNUNET_PEER_intern (&pi[size - 1]); + GNUNET_CONTAINER_multihashmap_put (peers, &pi[size - 1].hashPubKey, + dest_peer_info, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + } + orig_peer_info = GNUNET_CONTAINER_multihashmap_get (peers, &pi->hashPubKey); + if (NULL == orig_peer_info) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " Creating PeerInfo for origin.\n"); + orig_peer_info = GNUNET_malloc (sizeof (struct MeshPeerInfo)); + orig_peer_info->id = GNUNET_PEER_intern (pi); + GNUNET_CONTAINER_multihashmap_put (peers, &pi->hashPubKey, orig_peer_info, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Creating path...\n"); + path = path_new (size); + own_pos = 0; + for (i = 0; i < size; i++) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " ... adding %s\n", + GNUNET_i2s (&pi[i])); + path->peers[i] = GNUNET_PEER_intern (&pi[i]); + if (path->peers[i] == myid) + own_pos = i; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Own position: %u\n", own_pos); + if (own_pos == 0) + { + /* cannot be self, must be 'not found' */ + /* create path: self not found in path through self */ + GNUNET_break_op (0); + path_destroy (path); + tunnel_destroy (t); + return GNUNET_OK; + } + path_add_to_peers (path, GNUNET_NO); + tunnel_add_path (t, path, own_pos); + if (own_pos == size - 1) + { + /* It is for us! Send ack. */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " It's for us!\n"); + peer_info_add_path_to_origin (orig_peer_info, path, GNUNET_NO); + if (NULL == t->peers) + { + /* New tunnel! Notify clients on first payload message. */ + t->peers = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO); + } + GNUNET_break (GNUNET_SYSERR != + GNUNET_CONTAINER_multihashmap_put (t->peers, + &my_full_id.hashPubKey, + peer_info_get + (&my_full_id), + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE)); + send_path_ack (t); + } + else + { + struct MeshPeerPath *path2; + + /* It's for somebody else! Retransmit. */ + path2 = path_duplicate (path); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Retransmitting.\n"); + peer_info_add_path (dest_peer_info, path2, GNUNET_NO); + path2 = path_duplicate (path); + peer_info_add_path_to_origin (orig_peer_info, path2, GNUNET_NO); + send_create_path (dest_peer_info, path, t); + } + return GNUNET_OK; +} + + +/** + * Core handler for path destruction + * + * @param cls closure + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_mesh_path_destroy (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct GNUNET_MESH_ManipulatePath *msg; + struct GNUNET_PeerIdentity *pi; + struct MeshPeerPath *path; + struct MeshTunnel *t; + unsigned int own_pos; + unsigned int i; + size_t size; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received a PATH DESTROY msg from %s\n", GNUNET_i2s (peer)); + size = ntohs (message->size); + if (size < sizeof (struct GNUNET_MESH_ManipulatePath)) + { + GNUNET_break_op (0); + return GNUNET_OK; + } + + size -= sizeof (struct GNUNET_MESH_ManipulatePath); + if (size % sizeof (struct GNUNET_PeerIdentity)) + { + GNUNET_break_op (0); + return GNUNET_OK; + } + size /= sizeof (struct GNUNET_PeerIdentity); + if (size < 2) + { + GNUNET_break_op (0); + return GNUNET_OK; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " path has %u hops.\n", size); + + msg = (struct GNUNET_MESH_ManipulatePath *) message; + pi = (struct GNUNET_PeerIdentity *) &msg[1]; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " path is for tunnel %s [%X].\n", GNUNET_i2s (pi), + msg->tid); + t = tunnel_get (pi, ntohl (msg->tid)); + if (NULL == t) + { + /* TODO notify back: we don't know this tunnel */ + GNUNET_break_op (0); + return GNUNET_OK; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Creating path...\n"); + path = path_new (size); + own_pos = 0; + for (i = 0; i < size; i++) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " ... adding %s\n", + GNUNET_i2s (&pi[i])); + path->peers[i] = GNUNET_PEER_intern (&pi[i]); + if (path->peers[i] == myid) + own_pos = i; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Own position: %u\n", own_pos); + if (own_pos < path->length - 1) + send_prebuilt_message (message, &pi[own_pos + 1], t); + else + send_client_tunnel_disconnect(t, NULL); + + tunnel_delete_peer (t, path->peers[path->length - 1]); + path_destroy (path); + return GNUNET_OK; +} + + +/** + * Core handler for notifications of broken paths + * + * @param cls closure + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_mesh_path_broken (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct GNUNET_MESH_PathBroken *msg; + struct MeshTunnel *t; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received a PATH BROKEN msg from %s\n", GNUNET_i2s (peer)); + msg = (struct GNUNET_MESH_PathBroken *) message; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " regarding %s\n", + GNUNET_i2s (&msg->peer1)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " regarding %s\n", + GNUNET_i2s (&msg->peer2)); + t = tunnel_get (&msg->oid, ntohl (msg->tid)); + if (NULL == t) + { + GNUNET_break_op (0); + return GNUNET_OK; + } + tunnel_notify_connection_broken (t, GNUNET_PEER_search (&msg->peer1), + GNUNET_PEER_search (&msg->peer2)); + return GNUNET_OK; + +} + + +/** + * Core handler for tunnel destruction + * + * @param cls closure + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_mesh_tunnel_destroy (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct GNUNET_MESH_TunnelDestroy *msg; + struct MeshTunnel *t; + GNUNET_PEER_Id parent; + GNUNET_PEER_Id pid; + + msg = (struct GNUNET_MESH_TunnelDestroy *) message; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got a TUNNEL DESTROY packet from %s\n", + GNUNET_i2s (peer)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " for tunnel %s [%u]\n", + GNUNET_i2s (&msg->oid), ntohl (msg->tid)); + t = tunnel_get (&msg->oid, ntohl (msg->tid)); + /* Check signature */ + if (NULL == t) + { + /* Probably already got the message from another path, + * destroyed the tunnel and retransmitted to children. + * Safe to ignore. + */ + GNUNET_STATISTICS_update (stats, "# control on unknown tunnel", + 1, GNUNET_NO); + return GNUNET_OK; + } + parent = tree_get_predecessor (t->tree); + pid = GNUNET_PEER_search (peer); + if (pid != parent) + { + unsigned int nc; + + tree_del_peer (t->tree, pid, &tunnel_child_removed, t); + nc = tree_count_children (t->tree); + if (nc > 0 || NULL != t->owner || t->nclients > 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "still in use: %u cl, %u ch\n", + t->nclients, nc); + return GNUNET_OK; + } + } + if (t->local_tid_dest >= GNUNET_MESH_LOCAL_TUNNEL_ID_SERV) + { + /* Tunnel was incoming, notify clients */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "INCOMING TUNNEL %X %X\n", + t->local_tid, t->local_tid_dest); + send_clients_tunnel_destroy (t); + } + tunnel_send_destroy (t, parent); + t->destroy = GNUNET_YES; + // TODO: add timeout to destroy the tunnel anyway + return GNUNET_OK; +} + + +/** + * Core handler for mesh network traffic going from the origin to a peer + * + * @param cls closure + * @param peer peer identity this notification is about + * @param message message + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_mesh_data_unicast (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct GNUNET_MESH_Unicast *msg; + struct GNUNET_PeerIdentity *neighbor; + struct MeshTunnelChildInfo *cinfo; + struct MeshTunnel *t; + GNUNET_PEER_Id dest_id; + uint32_t pid; + uint32_t ttl; + size_t size; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got a unicast packet from %s\n", + GNUNET_i2s (peer)); + /* Check size */ + size = ntohs (message->size); + if (size < + sizeof (struct GNUNET_MESH_Unicast) + + sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break (0); + return GNUNET_OK; + } + msg = (struct GNUNET_MESH_Unicast *) message; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " of type %s\n", + GNUNET_MESH_DEBUG_M2S (ntohs (msg[1].header.type))); + /* Check tunnel */ + t = tunnel_get (&msg->oid, ntohl (msg->tid)); + if (NULL == t) + { + /* TODO notify back: we don't know this tunnel */ + GNUNET_STATISTICS_update (stats, "# data on unknown tunnel", 1, GNUNET_NO); + GNUNET_break_op (0); + return GNUNET_OK; + } + pid = ntohl (msg->pid); + if (t->fwd_pid == pid) + { + GNUNET_STATISTICS_update (stats, "# duplicate PID drops", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + " Already seen pid %u, DROPPING!\n", pid); + return GNUNET_OK; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " pid %u not seen yet, forwarding\n", pid); + } + + t->skip += (pid - t->fwd_pid) - 1; + t->fwd_pid = pid; + + if (GMC_is_pid_bigger (pid, t->last_fwd_ack)) + { + GNUNET_STATISTICS_update (stats, "# unsolicited unicast", 1, GNUNET_NO); + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received PID %u, ACK %u\n", + pid, t->last_fwd_ack); + return GNUNET_OK; + } + + tunnel_reset_timeout (t); + dest_id = GNUNET_PEER_search (&msg->destination); + if (dest_id == myid) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " it's for us! sending to clients...\n"); + GNUNET_STATISTICS_update (stats, "# unicast received", 1, GNUNET_NO); + send_subscribed_clients (message, &msg[1].header, t); + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_UNICAST); + return GNUNET_OK; + } + ttl = ntohl (msg->ttl); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " ttl: %u\n", ttl); + if (ttl == 0) + { + GNUNET_STATISTICS_update (stats, "# TTL drops", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, " TTL is 0, DROPPING!\n"); + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_ACK); + return GNUNET_OK; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " not for us, retransmitting...\n"); + + neighbor = tree_get_first_hop (t->tree, dest_id); + cinfo = tunnel_get_neighbor_fc (t, neighbor); + cinfo->fwd_pid = pid; + GNUNET_CONTAINER_multihashmap_iterate (t->children_fc, + &tunnel_add_skip, + &neighbor); + if (GNUNET_YES == t->nobuffer && + GNUNET_YES == GMC_is_pid_bigger (pid, cinfo->fwd_ack)) + { + GNUNET_STATISTICS_update (stats, "# unsolicited unicast", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, " %u > %u\n", pid, cinfo->fwd_ack); + GNUNET_break_op (0); + return GNUNET_OK; + } + send_prebuilt_message (message, neighbor, t); + GNUNET_STATISTICS_update (stats, "# unicast forwarded", 1, GNUNET_NO); + return GNUNET_OK; +} + + +/** + * Core handler for mesh network traffic going from the origin to all peers + * + * @param cls closure + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + * + * TODO: Check who we got this from, to validate route. + */ +static int +handle_mesh_data_multicast (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct GNUNET_MESH_Multicast *msg; + struct MeshTunnel *t; + size_t size; + uint32_t pid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got a multicast packet from %s\n", + GNUNET_i2s (peer)); + size = ntohs (message->size); + if (sizeof (struct GNUNET_MESH_Multicast) + + sizeof (struct GNUNET_MessageHeader) > size) + { + GNUNET_break_op (0); + return GNUNET_OK; + } + msg = (struct GNUNET_MESH_Multicast *) message; + t = tunnel_get (&msg->oid, ntohl (msg->tid)); + + if (NULL == t) + { + /* TODO notify that we dont know that tunnel */ + GNUNET_STATISTICS_update (stats, "# data on unknown tunnel", 1, GNUNET_NO); + GNUNET_break_op (0); + return GNUNET_OK; + } + pid = ntohl (msg->pid); + if (t->fwd_pid == pid) + { + /* already seen this packet, drop */ + GNUNET_STATISTICS_update (stats, "# duplicate PID drops", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " Already seen pid %u, DROPPING!\n", pid); + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_ACK); + return GNUNET_OK; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " pid %u not seen yet, forwarding\n", pid); + } + t->skip += (pid - t->fwd_pid) - 1; + t->fwd_pid = pid; + tunnel_reset_timeout (t); + + /* Transmit to locally interested clients */ + if (NULL != t->peers && + GNUNET_CONTAINER_multihashmap_contains (t->peers, &my_full_id.hashPubKey)) + { + GNUNET_STATISTICS_update (stats, "# multicast received", 1, GNUNET_NO); + send_subscribed_clients (message, &msg[1].header, t); + tunnel_send_fwd_ack(t, GNUNET_MESSAGE_TYPE_MESH_MULTICAST); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " ttl: %u\n", ntohl (msg->ttl)); + if (ntohl (msg->ttl) == 0) + { + GNUNET_STATISTICS_update (stats, "# TTL drops", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, " TTL is 0, DROPPING!\n"); + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_ACK); + return GNUNET_OK; + } + GNUNET_STATISTICS_update (stats, "# multicast forwarded", 1, GNUNET_NO); + tunnel_send_multicast (t, message); + return GNUNET_OK; +} + + +/** + * Core handler for mesh network traffic toward the owner of a tunnel + * + * @param cls closure + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_mesh_data_to_orig (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct GNUNET_MESH_ToOrigin *msg; + struct GNUNET_PeerIdentity id; + struct MeshPeerInfo *peer_info; + struct MeshTunnel *t; + struct MeshTunnelChildInfo *cinfo; + GNUNET_PEER_Id predecessor; + size_t size; + uint32_t pid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got a ToOrigin packet from %s\n", + GNUNET_i2s (peer)); + size = ntohs (message->size); + if (size < sizeof (struct GNUNET_MESH_ToOrigin) + /* Payload must be */ + sizeof (struct GNUNET_MessageHeader)) /* at least a header */ + { + GNUNET_break_op (0); + return GNUNET_OK; + } + msg = (struct GNUNET_MESH_ToOrigin *) message; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " of type %s\n", + GNUNET_MESH_DEBUG_M2S (ntohs (msg[1].header.type))); + t = tunnel_get (&msg->oid, ntohl (msg->tid)); + pid = ntohl (msg->pid); + + if (NULL == t) + { + /* TODO notify that we dont know this tunnel (whom)? */ + GNUNET_STATISTICS_update (stats, "# data on unknown tunnel", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received to_origin with PID %u on unknown tunnel %s [%u]\n", + pid, GNUNET_i2s (&msg->oid), ntohl (msg->tid)); + return GNUNET_OK; + } + + cinfo = tunnel_get_neighbor_fc(t, peer); + if (NULL == cinfo) + { + GNUNET_break (0); + return GNUNET_OK; + } + + if (cinfo->bck_pid == pid) + { + /* already seen this packet, drop */ + GNUNET_STATISTICS_update (stats, "# duplicate PID drops BCK", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " Already seen pid %u, DROPPING!\n", pid); + tunnel_send_bck_ack (t, GNUNET_MESSAGE_TYPE_MESH_ACK); + return GNUNET_OK; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " pid %u not seen yet, forwarding\n", pid); + cinfo->bck_pid = pid; + + if (NULL != t->owner) + { + char cbuf[size]; + struct GNUNET_MESH_ToOrigin *copy; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " it's for us! sending to clients...\n"); + /* TODO signature verification */ + memcpy (cbuf, message, size); + copy = (struct GNUNET_MESH_ToOrigin *) cbuf; + copy->tid = htonl (t->local_tid); + t->bck_pid++; + copy->pid = htonl (t->bck_pid); + GNUNET_STATISTICS_update (stats, "# to origin received", 1, GNUNET_NO); + GNUNET_SERVER_notification_context_unicast (nc, t->owner->handle, + ©->header, GNUNET_NO); + tunnel_send_bck_ack (t, GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN); + return GNUNET_OK; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " not for us, retransmitting...\n"); + + peer_info = peer_info_get (&msg->oid); + if (NULL == peer_info) + { + /* unknown origin of tunnel */ + GNUNET_break (0); + return GNUNET_OK; + } + predecessor = tree_get_predecessor (t->tree); + if (0 == predecessor) + { + if (GNUNET_YES == t->destroy) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "to orig received on a dying tunnel %s [%X]\n", + GNUNET_i2s (&msg->oid), ntohl(msg->tid)); + return GNUNET_OK; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "unknown to origin at %s\n", + GNUNET_i2s (&my_full_id)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "from peer %s\n", + GNUNET_i2s (peer)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "for tunnel %s [%X]\n", + GNUNET_i2s (&msg->oid), ntohl(msg->tid)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "current tree:\n"); + tree_debug (t->tree); + return GNUNET_OK; + } + GNUNET_PEER_resolve (predecessor, &id); + send_prebuilt_message (message, &id, t); + GNUNET_STATISTICS_update (stats, "# to origin forwarded", 1, GNUNET_NO); + + return GNUNET_OK; +} + + +/** + * Core handler for mesh network traffic point-to-point acks. + * + * @param cls closure + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_mesh_ack (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct GNUNET_MESH_ACK *msg; + struct MeshTunnel *t; + uint32_t ack; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got an ACK packet from %s!\n", + GNUNET_i2s (peer)); + msg = (struct GNUNET_MESH_ACK *) message; + + t = tunnel_get (&msg->oid, ntohl (msg->tid)); + + if (NULL == t) + { + /* TODO notify that we dont know this tunnel (whom)? */ + GNUNET_STATISTICS_update (stats, "# ack on unknown tunnel", 1, GNUNET_NO); + return GNUNET_OK; + } + ack = ntohl (msg->pid); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " ACK %u\n", ack); + + /* Is this a forward or backward ACK? */ + if (tree_get_predecessor(t->tree) != GNUNET_PEER_search(peer)) + { + struct MeshTunnelChildInfo *cinfo; + + debug_bck_ack++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " FWD ACK\n"); + cinfo = tunnel_get_neighbor_fc (t, peer); + cinfo->fwd_ack = ack; + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_ACK); + tunnel_unlock_fwd_queues (t); + if (GNUNET_SCHEDULER_NO_TASK != cinfo->fc_poll) + { + GNUNET_SCHEDULER_cancel (cinfo->fc_poll); + cinfo->fc_poll = GNUNET_SCHEDULER_NO_TASK; + cinfo->fc_poll_time = GNUNET_TIME_UNIT_SECONDS; + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " BCK ACK\n"); + t->bck_ack = ack; + tunnel_send_bck_ack (t, GNUNET_MESSAGE_TYPE_MESH_ACK); + tunnel_unlock_bck_queue (t); + } + return GNUNET_OK; +} + + +/** + * Core handler for mesh network traffic point-to-point ack polls. + * + * @param cls closure + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_mesh_poll (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct GNUNET_MESH_Poll *msg; + struct MeshTunnel *t; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got an POLL packet from %s!\n", + GNUNET_i2s (peer)); + + msg = (struct GNUNET_MESH_Poll *) message; + + t = tunnel_get (&msg->oid, ntohl (msg->tid)); + + if (NULL == t) + { + /* TODO notify that we dont know this tunnel (whom)? */ + GNUNET_STATISTICS_update (stats, "# poll on unknown tunnel", 1, GNUNET_NO); + GNUNET_break_op (0); + return GNUNET_OK; + } + + /* Is this a forward or backward ACK? */ + if (tree_get_predecessor(t->tree) != GNUNET_PEER_search(peer)) + { + struct MeshTunnelChildInfo *cinfo; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " from FWD\n"); + cinfo = tunnel_get_neighbor_fc (t, peer); + cinfo->bck_ack = cinfo->fwd_pid; // mark as ready to send + tunnel_send_bck_ack (t, GNUNET_MESSAGE_TYPE_MESH_POLL); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " from BCK\n"); + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_POLL); + } + + return GNUNET_OK; +} + +/** + * Core handler for path ACKs + * + * @param cls closure + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_mesh_path_ack (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct GNUNET_MESH_PathACK *msg; + struct GNUNET_PeerIdentity id; + struct MeshPeerInfo *peer_info; + struct MeshPeerPath *p; + struct MeshTunnel *t; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received a path ACK msg [%s]\n", + GNUNET_i2s (&my_full_id)); + msg = (struct GNUNET_MESH_PathACK *) message; + t = tunnel_get (&msg->oid, ntohl(msg->tid)); + if (NULL == t) + { + /* TODO notify that we don't know the tunnel */ + GNUNET_STATISTICS_update (stats, "# control on unknown tunnel", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " don't know the tunnel %s [%X]!\n", + GNUNET_i2s (&msg->oid), ntohl(msg->tid)); + return GNUNET_OK; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " on tunnel %s [%X]\n", + GNUNET_i2s (&msg->oid), ntohl(msg->tid)); + + peer_info = peer_info_get (&msg->peer_id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by peer %s\n", + GNUNET_i2s (&msg->peer_id)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " via peer %s\n", + GNUNET_i2s (peer)); + + if (NULL != t->regex_search && t->regex_search->peer == peer_info->id) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "connect_by_string completed, stopping search\n"); + regex_cancel_search (t->regex_search); + t->regex_search = NULL; + } + + /* Add paths to peers? */ + p = tree_get_path_to_peer (t->tree, peer_info->id); + if (NULL != p) + { + path_add_to_peers (p, GNUNET_YES); + path_destroy (p); + } + else + { + GNUNET_break (0); + } + + /* Message for us? */ + if (0 == memcmp (&msg->oid, &my_full_id, sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " It's for us!\n"); + if (NULL == t->owner) + { + GNUNET_break_op (0); + return GNUNET_OK; + } + if (NULL != t->dht_get_type) + { + GNUNET_DHT_get_stop (t->dht_get_type); + t->dht_get_type = NULL; + } + if (tree_get_status (t->tree, peer_info->id) != MESH_PEER_READY) + { + tree_set_status (t->tree, peer_info->id, MESH_PEER_READY); + send_client_peer_connected (t, peer_info->id); + } + return GNUNET_OK; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " not for us, retransmitting...\n"); + GNUNET_PEER_resolve (tree_get_predecessor (t->tree), &id); + peer_info = peer_info_get (&msg->oid); + if (NULL == peer_info) + { + /* If we know the tunnel, we should DEFINITELY know the peer */ + GNUNET_break (0); + return GNUNET_OK; + } + send_prebuilt_message (message, &id, t); + return GNUNET_OK; +} + + +/** + * Core handler for mesh keepalives. + * + * @param cls closure + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + * + * TODO: Check who we got this from, to validate route. + */ +static int +handle_mesh_keepalive (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct GNUNET_MESH_TunnelKeepAlive *msg; + struct MeshTunnel *t; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got a keepalive packet from %s\n", + GNUNET_i2s (peer)); + + msg = (struct GNUNET_MESH_TunnelKeepAlive *) message; + t = tunnel_get (&msg->oid, ntohl (msg->tid)); + + if (NULL == t) + { + /* TODO notify that we dont know that tunnel */ + GNUNET_STATISTICS_update (stats, "# keepalive on unknown tunnel", 1, + GNUNET_NO); + return GNUNET_OK; + } + + tunnel_reset_timeout (t); + + GNUNET_STATISTICS_update (stats, "# keepalives forwarded", 1, GNUNET_NO); + tunnel_send_multicast (t, message); + return GNUNET_OK; + } + + + +/** + * Functions to handle messages from core + */ +static struct GNUNET_CORE_MessageHandler core_handlers[] = { + {&handle_mesh_path_create, GNUNET_MESSAGE_TYPE_MESH_PATH_CREATE, 0}, + {&handle_mesh_path_destroy, GNUNET_MESSAGE_TYPE_MESH_PATH_DESTROY, 0}, + {&handle_mesh_path_broken, GNUNET_MESSAGE_TYPE_MESH_PATH_BROKEN, + sizeof (struct GNUNET_MESH_PathBroken)}, + {&handle_mesh_tunnel_destroy, GNUNET_MESSAGE_TYPE_MESH_TUNNEL_DESTROY, + sizeof (struct GNUNET_MESH_TunnelDestroy)}, + {&handle_mesh_data_unicast, GNUNET_MESSAGE_TYPE_MESH_UNICAST, 0}, + {&handle_mesh_data_multicast, GNUNET_MESSAGE_TYPE_MESH_MULTICAST, 0}, + {&handle_mesh_keepalive, GNUNET_MESSAGE_TYPE_MESH_PATH_KEEPALIVE, + sizeof (struct GNUNET_MESH_TunnelKeepAlive)}, + {&handle_mesh_data_to_orig, GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN, 0}, + {&handle_mesh_ack, GNUNET_MESSAGE_TYPE_MESH_ACK, + sizeof (struct GNUNET_MESH_ACK)}, + {&handle_mesh_poll, GNUNET_MESSAGE_TYPE_MESH_POLL, + sizeof (struct GNUNET_MESH_Poll)}, + {&handle_mesh_path_ack, GNUNET_MESSAGE_TYPE_MESH_PATH_ACK, + sizeof (struct GNUNET_MESH_PathACK)}, + {NULL, 0, 0} +}; + + + +/******************************************************************************/ +/**************** MESH LOCAL HANDLER HELPERS ***********************/ +/******************************************************************************/ + +/** + * deregister_app: iterator for removing each application registered by a client + * + * @param cls closure + * @param key the hash of the application id (used to access the hashmap) + * @param value the value stored at the key (client) + * + * @return GNUNET_OK on success + */ +static int +deregister_app (void *cls, const struct GNUNET_HashCode * key, void *value) +{ + struct GNUNET_CONTAINER_MultiHashMap *h = cls; + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (h, key, value)); + return GNUNET_OK; +} + +#if LATER +/** + * notify_client_connection_failure: notify a client that the connection to the + * requested remote peer is not possible (for instance, no route found) + * Function called when the socket is ready to queue more data. "buf" will be + * NULL and "size" zero if the socket was closed for writing in the meantime. + * + * @param cls closure + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +notify_client_connection_failure (void *cls, size_t size, void *buf) +{ + int size_needed; + struct MeshPeerInfo *peer_info; + struct GNUNET_MESH_PeerControl *msg; + struct GNUNET_PeerIdentity id; + + if (0 == size && NULL == buf) + { + // TODO retry? cancel? + return 0; + } + + size_needed = sizeof (struct GNUNET_MESH_PeerControl); + peer_info = (struct MeshPeerInfo *) cls; + msg = (struct GNUNET_MESH_PeerControl *) buf; + msg->header.size = htons (sizeof (struct GNUNET_MESH_PeerControl)); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_DISCONNECTED); +// msg->tunnel_id = htonl(peer_info->t->tid); + GNUNET_PEER_resolve (peer_info->id, &id); + memcpy (&msg->peer, &id, sizeof (struct GNUNET_PeerIdentity)); + + return size_needed; +} +#endif + + +/** + * Send keepalive packets for a peer + * + * @param cls Closure (tunnel for which to send the keepalive). + * @param tc Notification context. + */ +static void +path_refresh (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct MeshTunnel *t = cls; + struct GNUNET_MESH_TunnelKeepAlive *msg; + size_t size = sizeof (struct GNUNET_MESH_TunnelKeepAlive); + char cbuf[size]; + + t->path_refresh_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + { + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "sending keepalive for tunnel %d\n", t->id.tid); + + msg = (struct GNUNET_MESH_TunnelKeepAlive *) cbuf; + msg->header.size = htons (size); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_PATH_KEEPALIVE); + msg->oid = my_full_id; + msg->tid = htonl (t->id.tid); + tunnel_send_multicast (t, &msg->header); + + t->path_refresh_task = + GNUNET_SCHEDULER_add_delayed (refresh_path_time, &path_refresh, t); + tunnel_reset_timeout(t); +} + + +/** + * Function to process paths received for a new peer addition. The recorded + * paths form the initial tunnel, which can be optimized later. + * Called on each result obtained for the DHT search. + * + * @param cls closure + * @param exp when will this value expire + * @param key key of the result + * @param get_path path of the get request + * @param get_path_length lenght of get_path + * @param put_path path of the put request + * @param put_path_length length of the put_path + * @param type type of the result + * @param size number of bytes in data + * @param data pointer to the result data + * + * TODO: re-issue the request after certain time? cancel after X results? + */ +static void +dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp, + const struct GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_length, enum GNUNET_BLOCK_Type type, + size_t size, const void *data) +{ + struct MeshPathInfo *path_info = cls; + struct MeshPeerPath *p; + struct GNUNET_PeerIdentity pi; + int i; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got results from DHT!\n"); + GNUNET_PEER_resolve (path_info->peer->id, &pi); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " for %s\n", GNUNET_i2s (&pi)); + + p = path_build_from_dht (get_path, get_path_length, put_path, + put_path_length); + path_add_to_peers (p, GNUNET_NO); + path_destroy(p); + for (i = 0; i < path_info->peer->ntunnels; i++) + { + tunnel_add_peer (path_info->peer->tunnels[i], path_info->peer); + peer_info_connect (path_info->peer, path_info->t); + } + + return; +} + + +/** + * Function to process paths received for a new peer addition. The recorded + * paths form the initial tunnel, which can be optimized later. + * Called on each result obtained for the DHT search. + * + * @param cls closure + * @param exp when will this value expire + * @param key key of the result + * @param get_path path of the get request + * @param get_path_length lenght of get_path + * @param put_path path of the put request + * @param put_path_length length of the put_path + * @param type type of the result + * @param size number of bytes in data + * @param data pointer to the result data + */ +static void +dht_get_type_handler (void *cls, struct GNUNET_TIME_Absolute exp, + const struct GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_length, enum GNUNET_BLOCK_Type type, + size_t size, const void *data) +{ + const struct PBlock *pb = data; + const struct GNUNET_PeerIdentity *pi = &pb->id; + struct MeshTunnel *t = cls; + struct MeshPeerInfo *peer_info; + struct MeshPeerPath *p; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got type DHT result!\n"); + if (size != sizeof (struct PBlock)) + { + GNUNET_break_op (0); + return; + } + if (ntohl(pb->type) != t->type) + { + GNUNET_break_op (0); + return; + } + GNUNET_assert (NULL != t->owner); + peer_info = peer_info_get (pi); + (void) GNUNET_CONTAINER_multihashmap_put (t->peers, &pi->hashPubKey, + peer_info, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); + + p = path_build_from_dht (get_path, get_path_length, put_path, + put_path_length); + path_add_to_peers (p, GNUNET_NO); + path_destroy(p); + tunnel_add_peer (t, peer_info); + peer_info_connect (peer_info, t); +} + + +/******************************************************************************/ +/********************* MESH LOCAL HANDLES **************************/ +/******************************************************************************/ + + +/** + * Handler for client disconnection + * + * @param cls closure + * @param client identification of the client; NULL + * for the last call when the server is destroyed + */ +static void +handle_local_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client) +{ + struct MeshClient *c; + struct MeshClient *next; + unsigned int i; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disconnected\n"); + if (client == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " (SERVER DOWN)\n"); + return; + } + + c = clients; + while (NULL != c) + { + if (c->handle != client) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " ... searching\n"); + c = c->next; + continue; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "matching client found (%u)\n", + c->id); + GNUNET_SERVER_client_drop (c->handle); + c->shutting_down = GNUNET_YES; + GNUNET_assert (NULL != c->own_tunnels); + GNUNET_assert (NULL != c->incoming_tunnels); + GNUNET_CONTAINER_multihashmap_iterate (c->own_tunnels, + &tunnel_destroy_iterator, c); + GNUNET_CONTAINER_multihashmap_iterate (c->incoming_tunnels, + &tunnel_destroy_iterator, c); + GNUNET_CONTAINER_multihashmap_iterate (c->ignore_tunnels, + &tunnel_destroy_iterator, c); + GNUNET_CONTAINER_multihashmap_destroy (c->own_tunnels); + GNUNET_CONTAINER_multihashmap_destroy (c->incoming_tunnels); + GNUNET_CONTAINER_multihashmap_destroy (c->ignore_tunnels); + + /* deregister clients applications */ + if (NULL != c->apps) + { + GNUNET_CONTAINER_multihashmap_iterate (c->apps, &deregister_app, c->apps); + GNUNET_CONTAINER_multihashmap_destroy (c->apps); + } + if (0 == GNUNET_CONTAINER_multihashmap_size (applications) && + GNUNET_SCHEDULER_NO_TASK != announce_applications_task) + { + GNUNET_SCHEDULER_cancel (announce_applications_task); + announce_applications_task = GNUNET_SCHEDULER_NO_TASK; + } + if (NULL != c->types) + GNUNET_CONTAINER_multihashmap_destroy (c->types); + for (i = 0; i < c->n_regex; i++) + { + GNUNET_free (c->regexes[i].regex); + if (NULL != c->regexes[i].h) + GNUNET_REGEX_announce_cancel (c->regexes[i].h); + } + GNUNET_free_non_null (c->regexes); + if (GNUNET_SCHEDULER_NO_TASK != c->regex_announce_task) + GNUNET_SCHEDULER_cancel (c->regex_announce_task); + next = c->next; + GNUNET_CONTAINER_DLL_remove (clients, clients_tail, c); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " CLIENT FREE at %p\n", c); + GNUNET_free (c); + GNUNET_STATISTICS_update (stats, "# clients", -1, GNUNET_NO); + c = next; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " done!\n"); + return; +} + + +/** + * Handler for new clients + * + * @param cls closure + * @param client identification of the client + * @param message the actual message, which includes messages the client wants + */ +static void +handle_local_new_client (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_ClientConnect *cc_msg; + struct MeshClient *c; + GNUNET_MESH_ApplicationType *a; + unsigned int size; + uint16_t ntypes; + uint16_t *t; + uint16_t napps; + uint16_t i; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "new client connected\n"); + /* Check data sanity */ + size = ntohs (message->size) - sizeof (struct GNUNET_MESH_ClientConnect); + cc_msg = (struct GNUNET_MESH_ClientConnect *) message; + ntypes = ntohs (cc_msg->types); + napps = ntohs (cc_msg->applications); + if (size != + ntypes * sizeof (uint16_t) + napps * sizeof (GNUNET_MESH_ApplicationType)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Create new client structure */ + c = GNUNET_malloc (sizeof (struct MeshClient)); + c->id = next_client_id++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " CLIENT NEW %u\n", c->id); + c->handle = client; + GNUNET_SERVER_client_keep (client); + a = (GNUNET_MESH_ApplicationType *) &cc_msg[1]; + if (napps > 0) + { + GNUNET_MESH_ApplicationType at; + struct GNUNET_HashCode hc; + + c->apps = GNUNET_CONTAINER_multihashmap_create (napps, GNUNET_NO); + for (i = 0; i < napps; i++) + { + at = ntohl (a[i]); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " app type: %u\n", at); + GNUNET_CRYPTO_hash (&at, sizeof (at), &hc); + /* store in clients hashmap */ + GNUNET_CONTAINER_multihashmap_put (c->apps, &hc, (void *) (long) at, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + /* store in global hashmap, for announcements */ + GNUNET_CONTAINER_multihashmap_put (applications, &hc, c, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + } + if (GNUNET_SCHEDULER_NO_TASK == announce_applications_task) + announce_applications_task = + GNUNET_SCHEDULER_add_now (&announce_applications, NULL); + + } + if (ntypes > 0) + { + uint16_t u16; + struct GNUNET_HashCode hc; + + t = (uint16_t *) & a[napps]; + c->types = GNUNET_CONTAINER_multihashmap_create (ntypes, GNUNET_NO); + for (i = 0; i < ntypes; i++) + { + u16 = ntohs (t[i]); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " msg type: %u\n", u16); + GNUNET_CRYPTO_hash (&u16, sizeof (u16), &hc); + + /* store in clients hashmap */ + GNUNET_CONTAINER_multihashmap_put (c->types, &hc, c, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + /* store in global hashmap */ + GNUNET_CONTAINER_multihashmap_put (types, &hc, c, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + } + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " client has %u+%u subscriptions\n", napps, ntypes); + + GNUNET_CONTAINER_DLL_insert (clients, clients_tail, c); + c->own_tunnels = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); + c->incoming_tunnels = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); + c->ignore_tunnels = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); + GNUNET_SERVER_notification_context_add (nc, client); + GNUNET_STATISTICS_update (stats, "# clients", 1, GNUNET_NO); + + GNUNET_SERVER_receive_done (client, GNUNET_OK); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "new client processed\n"); +} + + +/** + * Handler for clients announcing available services by a regular expression. + * + * @param cls closure + * @param client identification of the client + * @param message the actual message, which includes messages the client wants + */ +static void +handle_local_announce_regex (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_MESH_RegexAnnounce *msg; + struct MeshRegexDescriptor rd; + struct MeshClient *c; + char *regex; + size_t len; + size_t offset; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "announce regex started\n"); + + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + msg = (const struct GNUNET_MESH_RegexAnnounce *) message; + + len = ntohs (message->size) - sizeof(struct GNUNET_MESH_RegexAnnounce); + if (NULL != c->partial_regex) + { + regex = c->partial_regex; + offset = strlen (c->partial_regex); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " continuation, already have %u bytes\n", + offset); + } + else + { + regex = NULL; + offset = 0; + } + + regex = GNUNET_realloc (regex, offset + len + 1); + memcpy (®ex[offset], &msg[1], len); + regex[offset + len] = '\0'; + if (0 == ntohs (msg->last)) + { + c->partial_regex = regex; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " not ended, stored %u bytes for later\n", + len); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + rd.regex = regex; + rd.compression = ntohs (msg->compression_characters); + rd.h = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " length %u\n", len); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " regex %s\n", regex); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " cm %u\n", ntohs(rd.compression)); + GNUNET_array_append (c->regexes, c->n_regex, rd); + c->partial_regex = NULL; + if (GNUNET_SCHEDULER_NO_TASK == c->regex_announce_task) + { + c->regex_announce_task = GNUNET_SCHEDULER_add_now(®ex_announce, c); + } + else + { + regex_put(&rd); + } + GNUNET_SERVER_receive_done (client, GNUNET_OK); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "announce regex processed\n"); +} + + +/** + * Handler for requests of new tunnels + * + * @param cls closure + * @param client identification of the client + * @param message the actual message + */ +static void +handle_local_tunnel_create (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_TunnelMessage *t_msg; + struct MeshTunnel *t; + struct MeshClient *c; + MESH_TunnelNumber tid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "new tunnel requested\n"); + + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + /* Message sanity check */ + if (sizeof (struct GNUNET_MESH_TunnelMessage) != ntohs (message->size)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + t_msg = (struct GNUNET_MESH_TunnelMessage *) message; + /* Sanity check for tunnel numbering */ + tid = ntohl (t_msg->tunnel_id); + if (0 == (tid & GNUNET_MESH_LOCAL_TUNNEL_ID_CLI)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + /* Sanity check for duplicate tunnel IDs */ + if (NULL != tunnel_get_by_local_id (c, tid)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + while (NULL != tunnel_get_by_pi (myid, next_tid)) + next_tid = (next_tid + 1) & ~GNUNET_MESH_LOCAL_TUNNEL_ID_CLI; + t = tunnel_new (myid, next_tid++, c, tid); + if (NULL == t) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Tunnel creation failed.\n"); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + next_tid = next_tid & ~GNUNET_MESH_LOCAL_TUNNEL_ID_CLI; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CREATED TUNNEL %s [%x] (%x)\n", + GNUNET_i2s (&my_full_id), t->id.tid, t->local_tid); + t->peers = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "new tunnel created\n"); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; +} + + +/** + * Handler for requests of deleting tunnels + * + * @param cls closure + * @param client identification of the client + * @param message the actual message + */ +static void +handle_local_tunnel_destroy (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_TunnelMessage *tunnel_msg; + struct MeshClient *c; + struct MeshTunnel *t; + MESH_TunnelNumber tid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got a DESTROY TUNNEL from client!\n"); + + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + /* Message sanity check */ + if (sizeof (struct GNUNET_MESH_TunnelMessage) != ntohs (message->size)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + tunnel_msg = (struct GNUNET_MESH_TunnelMessage *) message; + + /* Retrieve tunnel */ + tid = ntohl (tunnel_msg->tunnel_id); + t = tunnel_get_by_local_id(c, tid); + if (NULL == t) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, " tunnel %X not found\n", tid); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + if (c != t->owner || tid >= GNUNET_MESH_LOCAL_TUNNEL_ID_SERV) + { + client_ignore_tunnel (c, t); + tunnel_destroy_empty (t); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + send_client_tunnel_disconnect (t, c); + client_delete_tunnel (c, t); + + /* Don't try to ACK the client about the tunnel_destroy multicast packet */ + t->owner = NULL; + tunnel_send_destroy (t, 0); + t->destroy = GNUNET_YES; + /* The tunnel will be destroyed when the last message is transmitted. */ + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; +} + + +/** + * Handler for requests of seeting tunnel's speed. + * + * @param cls Closure (unused). + * @param client Identification of the client. + * @param message The actual message. + */ +static void +handle_local_tunnel_speed (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_TunnelMessage *tunnel_msg; + struct MeshClient *c; + struct MeshTunnel *t; + MESH_TunnelNumber tid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got a SPEED request from client!\n"); + + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + tunnel_msg = (struct GNUNET_MESH_TunnelMessage *) message; + + /* Retrieve tunnel */ + tid = ntohl (tunnel_msg->tunnel_id); + t = tunnel_get_by_local_id(c, tid); + if (NULL == t) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, " tunnel %X not found\n", tid); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + switch (ntohs(message->type)) + { + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_MIN: + t->speed_min = GNUNET_YES; + break; + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_MAX: + t->speed_min = GNUNET_NO; + break; + default: + GNUNET_break (0); + } + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Handler for requests of seeting tunnel's buffering policy. + * + * @param cls Closure (unused). + * @param client Identification of the client. + * @param message The actual message. + */ +static void +handle_local_tunnel_buffer (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_TunnelMessage *tunnel_msg; + struct MeshClient *c; + struct MeshTunnel *t; + MESH_TunnelNumber tid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got a BUFFER request from client!\n"); + + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + tunnel_msg = (struct GNUNET_MESH_TunnelMessage *) message; + + /* Retrieve tunnel */ + tid = ntohl (tunnel_msg->tunnel_id); + t = tunnel_get_by_local_id(c, tid); + if (NULL == t) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, " tunnel %X not found\n", tid); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + switch (ntohs(message->type)) + { + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_BUFFER: + t->nobuffer = GNUNET_NO; + break; + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_NOBUFFER: + t->nobuffer = GNUNET_YES; + break; + default: + GNUNET_break (0); + } + + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Handler for connection requests to new peers + * + * @param cls closure + * @param client identification of the client + * @param message the actual message (PeerControl) + */ +static void +handle_local_connect_add (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_PeerControl *peer_msg; + struct MeshPeerInfo *peer_info; + struct MeshClient *c; + struct MeshTunnel *t; + MESH_TunnelNumber tid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got connection request\n"); + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + peer_msg = (struct GNUNET_MESH_PeerControl *) message; + + /* Sanity check for message size */ + if (sizeof (struct GNUNET_MESH_PeerControl) != ntohs (peer_msg->header.size)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Tunnel exists? */ + tid = ntohl (peer_msg->tunnel_id); + t = tunnel_get_by_local_id (c, tid); + if (NULL == t) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Does client own tunnel? */ + if (t->owner->handle != client) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " for %s\n", + GNUNET_i2s (&peer_msg->peer)); + peer_info = peer_info_get (&peer_msg->peer); + + tunnel_add_peer (t, peer_info); + peer_info_connect (peer_info, t); + + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; +} + + +/** + * Handler for disconnection requests of peers in a tunnel + * + * @param cls closure + * @param client identification of the client + * @param message the actual message (PeerControl) + */ +static void +handle_local_connect_del (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_PeerControl *peer_msg; + struct MeshPeerInfo *peer_info; + struct MeshClient *c; + struct MeshTunnel *t; + MESH_TunnelNumber tid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a PEER DEL request\n"); + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + peer_msg = (struct GNUNET_MESH_PeerControl *) message; + + /* Sanity check for message size */ + if (sizeof (struct GNUNET_MESH_PeerControl) != ntohs (peer_msg->header.size)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Tunnel exists? */ + tid = ntohl (peer_msg->tunnel_id); + t = tunnel_get_by_local_id (c, tid); + if (NULL == t) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " on tunnel %X\n", t->id.tid); + + /* Does client own tunnel? */ + if (t->owner->handle != client) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " for peer %s\n", + GNUNET_i2s (&peer_msg->peer)); + /* Is the peer in the tunnel? */ + peer_info = + GNUNET_CONTAINER_multihashmap_get (t->peers, &peer_msg->peer.hashPubKey); + if (NULL == peer_info) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Ok, delete peer from tunnel */ + GNUNET_CONTAINER_multihashmap_remove_all (t->peers, + &peer_msg->peer.hashPubKey); + + send_destroy_path (t, peer_info->id); + tunnel_delete_peer (t, peer_info->id); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; +} + +/** + * Handler for blacklist requests of peers in a tunnel + * + * @param cls closure + * @param client identification of the client + * @param message the actual message (PeerControl) + * + * FIXME implement DHT block bloomfilter + */ +static void +handle_local_blacklist (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_PeerControl *peer_msg; + struct MeshClient *c; + struct MeshTunnel *t; + MESH_TunnelNumber tid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a PEER BLACKLIST request\n"); + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + peer_msg = (struct GNUNET_MESH_PeerControl *) message; + + /* Sanity check for message size */ + if (sizeof (struct GNUNET_MESH_PeerControl) != ntohs (peer_msg->header.size)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Tunnel exists? */ + tid = ntohl (peer_msg->tunnel_id); + t = tunnel_get_by_local_id (c, tid); + if (NULL == t) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " on tunnel %X\n", t->id.tid); + + GNUNET_array_append(t->blacklisted, t->nblacklisted, + GNUNET_PEER_intern(&peer_msg->peer)); +} + + +/** + * Handler for unblacklist requests of peers in a tunnel + * + * @param cls closure + * @param client identification of the client + * @param message the actual message (PeerControl) + */ +static void +handle_local_unblacklist (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_PeerControl *peer_msg; + struct MeshClient *c; + struct MeshTunnel *t; + MESH_TunnelNumber tid; + GNUNET_PEER_Id pid; + unsigned int i; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a PEER UNBLACKLIST request\n"); + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + peer_msg = (struct GNUNET_MESH_PeerControl *) message; + + /* Sanity check for message size */ + if (sizeof (struct GNUNET_MESH_PeerControl) != ntohs (peer_msg->header.size)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Tunnel exists? */ + tid = ntohl (peer_msg->tunnel_id); + t = tunnel_get_by_local_id (c, tid); + if (NULL == t) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " on tunnel %X\n", t->id.tid); + + /* if peer is not known, complain */ + pid = GNUNET_PEER_search (&peer_msg->peer); + if (0 == pid) + { + GNUNET_break (0); + return; + } + + /* search and remove from list */ + for (i = 0; i < t->nblacklisted; i++) + { + if (t->blacklisted[i] == pid) + { + t->blacklisted[i] = t->blacklisted[t->nblacklisted - 1]; + GNUNET_array_grow (t->blacklisted, t->nblacklisted, t->nblacklisted - 1); + return; + } + } + + /* if peer hasn't been blacklisted, complain */ + GNUNET_break (0); +} + + +/** + * Handler for connection requests to new peers by type + * + * @param cls closure + * @param client identification of the client + * @param message the actual message (ConnectPeerByType) + */ +static void +handle_local_connect_by_type (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_ConnectPeerByType *connect_msg; + struct MeshClient *c; + struct MeshTunnel *t; + struct GNUNET_HashCode hash; + MESH_TunnelNumber tid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got connect by type request\n"); + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + connect_msg = (struct GNUNET_MESH_ConnectPeerByType *) message; + + /* Sanity check for message size */ + if (sizeof (struct GNUNET_MESH_ConnectPeerByType) != + ntohs (connect_msg->header.size)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Tunnel exists? */ + tid = ntohl (connect_msg->tunnel_id); + t = tunnel_get_by_local_id (c, tid); + if (NULL == t) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Does client own tunnel? */ + if (t->owner->handle != client) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Do WE have the service? */ + t->type = ntohl (connect_msg->type); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " type requested: %u\n", t->type); + GNUNET_CRYPTO_hash (&t->type, sizeof (GNUNET_MESH_ApplicationType), &hash); + if (GNUNET_CONTAINER_multihashmap_contains (applications, &hash) == + GNUNET_YES) + { + /* Yes! Fast forward, add ourselves to the tunnel and send the + * good news to the client, and alert the destination client of + * an incoming tunnel. + * + * FIXME send a path create to self, avoid code duplication + */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " available locally\n"); + GNUNET_CONTAINER_multihashmap_put (t->peers, &my_full_id.hashPubKey, + peer_info_get (&my_full_id), + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " notifying client\n"); + send_client_peer_connected (t, myid); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Done\n"); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + + t->local_tid_dest = next_local_tid++; + GNUNET_CRYPTO_hash (&t->local_tid_dest, sizeof (MESH_TunnelNumber), &hash); + GNUNET_CONTAINER_multihashmap_put (incoming_tunnels, &hash, t, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + + return; + } + /* Ok, lets find a peer offering the service */ + if (NULL != t->dht_get_type) + { + GNUNET_DHT_get_stop (t->dht_get_type); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " looking in DHT for %s\n", + GNUNET_h2s (&hash)); + t->dht_get_type = + GNUNET_DHT_get_start (dht_handle, + GNUNET_BLOCK_TYPE_MESH_PEER_BY_TYPE, + &hash, + dht_replication_level, + GNUNET_DHT_RO_RECORD_ROUTE | + GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, + NULL, 0, + &dht_get_type_handler, t); + + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; +} + + +/** + * Handler for connection requests to new peers by a string service description. + * + * @param cls closure + * @param client identification of the client + * @param message the actual message, which includes messages the client wants + */ +static void +handle_local_connect_by_string (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_ConnectPeerByString *msg; + struct MeshRegexSearchInfo *info; + struct GNUNET_HashCode key; + struct MeshTunnel *t; + struct MeshClient *c; + MESH_TunnelNumber tid; + const char *string; + size_t size; + size_t len; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connect by string started\n"); + msg = (struct GNUNET_MESH_ConnectPeerByString *) message; + size = htons (message->size); + + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + /* Message size sanity check */ + if (sizeof(struct GNUNET_MESH_ConnectPeerByString) >= size) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Tunnel exists? */ + tid = ntohl (msg->tunnel_id); + t = tunnel_get_by_local_id (c, tid); + if (NULL == t) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Does client own tunnel? */ + if (t->owner->handle != client) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " on tunnel %s [%u]\n", + GNUNET_i2s(&my_full_id), + t->id.tid); + + /* Only one connect_by_string allowed at the same time! */ + /* FIXME: allow more, return handle at api level to cancel, document */ + if (NULL != t->regex_search) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Find string itself */ + len = size - sizeof(struct GNUNET_MESH_ConnectPeerByString); + string = (const char *) &msg[1]; + + /* Initialize context */ + size = GNUNET_REGEX_get_first_key (string, len, &key); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " consumed %u bits out of %u\n", size, len); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " looking for %s\n", GNUNET_h2s (&key)); + + info = GNUNET_malloc (sizeof (struct MeshRegexSearchInfo)); + info->t = t; + info->description = GNUNET_strndup (string, len); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " string: %s\n", info->description); + + t->regex_search = info; + + info->search_handle = GNUNET_REGEX_search (dht_handle, + info->description, + ®ex_found_handler, info, + stats); + + GNUNET_SERVER_receive_done (client, GNUNET_OK); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "connect by string processed\n"); +} + + +/** + * Handler for client traffic directed to one peer + * + * @param cls closure + * @param client identification of the client + * @param message the actual message + */ +static void +handle_local_unicast (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct MeshClient *c; + struct MeshTunnel *t; + struct MeshPeerInfo *pi; + struct GNUNET_MESH_Unicast *data_msg; + MESH_TunnelNumber tid; + size_t size; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got a unicast request from a client!\n"); + + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + data_msg = (struct GNUNET_MESH_Unicast *) message; + + /* Sanity check for message size */ + size = ntohs (message->size); + if (sizeof (struct GNUNET_MESH_Unicast) + + sizeof (struct GNUNET_MessageHeader) > size) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Tunnel exists? */ + tid = ntohl (data_msg->tid); + t = tunnel_get_by_local_id (c, tid); + if (NULL == t) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Is it a local tunnel? Then, does client own the tunnel? */ + if (t->owner->handle != client) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + pi = GNUNET_CONTAINER_multihashmap_get (t->peers, + &data_msg->destination.hashPubKey); + /* Is the selected peer in the tunnel? */ + if (NULL == pi) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* PID should be as expected */ + if (ntohl (data_msg->pid) != t->fwd_pid + 1) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unicast PID, expected %u, got %u\n", + t->fwd_pid + 1, ntohl (data_msg->pid)); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Ok, everything is correct, send the message + * (pretend we got it from a mesh peer) + */ + { + /* Work around const limitation */ + char buf[ntohs (message->size)] GNUNET_ALIGN; + struct GNUNET_MESH_Unicast *copy; + + copy = (struct GNUNET_MESH_Unicast *) buf; + memcpy (buf, data_msg, size); + copy->oid = my_full_id; + copy->tid = htonl (t->id.tid); + copy->ttl = htonl (default_ttl); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " calling generic handler...\n"); + handle_mesh_data_unicast (NULL, &my_full_id, ©->header, NULL, 0); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "receive done OK\n"); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + + return; +} + + +/** + * Handler for client traffic directed to the origin + * + * @param cls closure + * @param client identification of the client + * @param message the actual message + */ +static void +handle_local_to_origin (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_ToOrigin *data_msg; + struct MeshTunnelClientInfo *clinfo; + struct MeshClient *c; + struct MeshTunnel *t; + MESH_TunnelNumber tid; + size_t size; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got a ToOrigin request from a client!\n"); + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + data_msg = (struct GNUNET_MESH_ToOrigin *) message; + + /* Sanity check for message size */ + size = ntohs (message->size); + if (sizeof (struct GNUNET_MESH_ToOrigin) + + sizeof (struct GNUNET_MessageHeader) > size) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Tunnel exists? */ + tid = ntohl (data_msg->tid); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " on tunnel %X\n", tid); + if (tid < GNUNET_MESH_LOCAL_TUNNEL_ID_SERV) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + t = tunnel_get_by_local_id (c, tid); + if (NULL == t) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Tunnel %X unknown.\n", tid); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, " for client %u.\n", c->id); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* It should be sent by someone who has this as incoming tunnel. */ + if (GNUNET_NO == client_knows_tunnel (c, t)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* PID should be as expected */ + clinfo = tunnel_get_client_fc (t, c); + if (ntohl (data_msg->pid) != clinfo->bck_pid + 1) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "To Origin PID, expected %u, got %u\n", + clinfo->bck_pid + 1, + ntohl (data_msg->pid)); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + clinfo->bck_pid++; + + /* Ok, everything is correct, send the message + * (pretend we got it from a mesh peer) + */ + { + char buf[ntohs (message->size)] GNUNET_ALIGN; + struct GNUNET_MESH_ToOrigin *copy; + + /* Work around const limitation */ + copy = (struct GNUNET_MESH_ToOrigin *) buf; + memcpy (buf, data_msg, size); + GNUNET_PEER_resolve (t->id.oid, ©->oid); + copy->tid = htonl (t->id.tid); + copy->ttl = htonl (default_ttl); + copy->pid = htonl (t->bck_pid + 1); + + copy->sender = my_full_id; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " calling generic handler...\n"); + handle_mesh_data_to_orig (NULL, &my_full_id, ©->header, NULL, 0); + } + GNUNET_SERVER_receive_done (client, GNUNET_OK); + + return; +} + + +/** + * Handler for client traffic directed to all peers in a tunnel + * + * @param cls closure + * @param client identification of the client + * @param message the actual message + */ +static void +handle_local_multicast (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct MeshClient *c; + struct MeshTunnel *t; + struct GNUNET_MESH_Multicast *data_msg; + MESH_TunnelNumber tid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got a multicast request from a client!\n"); + + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + data_msg = (struct GNUNET_MESH_Multicast *) message; + + /* Sanity check for message size */ + if (sizeof (struct GNUNET_MESH_Multicast) + + sizeof (struct GNUNET_MessageHeader) > ntohs (data_msg->header.size)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Tunnel exists? */ + tid = ntohl (data_msg->tid); + t = tunnel_get_by_local_id (c, tid); + if (NULL == t) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Tunnel %X unknown.\n", tid); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, " for client %u.\n", c->id); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Does client own tunnel? */ + if (t->owner->handle != client) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* PID should be as expected */ + if (ntohl (data_msg->pid) != t->fwd_pid + 1) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Multicast PID, expected %u, got %u\n", + t->fwd_pid + 1, ntohl (data_msg->pid)); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + { + char buf[ntohs (message->size)] GNUNET_ALIGN; + struct GNUNET_MESH_Multicast *copy; + + copy = (struct GNUNET_MESH_Multicast *) buf; + memcpy (buf, message, ntohs (message->size)); + copy->oid = my_full_id; + copy->tid = htonl (t->id.tid); + copy->ttl = htonl (default_ttl); + GNUNET_assert (ntohl (copy->pid) == (t->fwd_pid + 1)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " calling generic handler...\n"); + handle_mesh_data_multicast (client, &my_full_id, ©->header, NULL, 0); + } + + GNUNET_SERVER_receive_done (t->owner->handle, GNUNET_OK); + return; +} + + +/** + * Handler for client's ACKs for payload traffic. + * + * @param cls Closure (unused). + * @param client Identification of the client. + * @param message The actual message. + */ +static void +handle_local_ack (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_LocalAck *msg; + struct MeshTunnel *t; + struct MeshClient *c; + MESH_TunnelNumber tid; + uint32_t ack; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a local ACK\n"); + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + msg = (struct GNUNET_MESH_LocalAck *) message; + + /* Tunnel exists? */ + tid = ntohl (msg->tunnel_id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " on tunnel %X\n", tid); + t = tunnel_get_by_local_id (c, tid); + if (NULL == t) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Tunnel %X unknown.\n", tid); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, " for client %u.\n", c->id); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + ack = ntohl (msg->max_pid); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " ack %u\n", ack); + + /* Does client own tunnel? I.E: Is this an ACK for BCK traffic? */ + if (NULL != t->owner && t->owner->handle == client) + { + /* The client owns the tunnel, ACK is for data to_origin, send BCK ACK. */ + t->bck_ack = ack; + tunnel_send_bck_ack(t, GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK); + } + else + { + /* The client doesn't own the tunnel, this ACK is for FWD traffic. */ + tunnel_set_client_fwd_ack (t, c, ack); + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK); + } + + GNUNET_SERVER_receive_done (client, GNUNET_OK); + + return; +} + + +/** + * Iterator over all peers to send a monitoring client info about a tunnel. + * + * @param cls Closure (message being built). + * @param key Key (hashed tunnel ID, unused). + * @param value Peer info. + * + * @return GNUNET_YES, to keep iterating. + */ +static int +monitor_peers_iterator (void *cls, + const struct GNUNET_HashCode * key, + void *value) +{ + struct GNUNET_MESH_LocalMonitor *msg = cls; + struct GNUNET_PeerIdentity *id; + struct MeshPeerInfo *info = value; + + id = (struct GNUNET_PeerIdentity *) &msg[1]; + GNUNET_PEER_resolve (info->id, &id[msg->npeers]); + msg->npeers++; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "* sending info about peer %s [%u]\n", + GNUNET_i2s (&id[msg->npeers - 1]), msg->npeers); + + return GNUNET_YES; +} + + + +/** + * Iterator over all tunnels to send a monitoring client info about each tunnel. + * + * @param cls Closure (client handle). + * @param key Key (hashed tunnel ID, unused). + * @param value Tunnel info. + * + * @return GNUNET_YES, to keep iterating. + */ +static int +monitor_all_tunnels_iterator (void *cls, + const struct GNUNET_HashCode * key, + void *value) +{ + struct GNUNET_SERVER_Client *client = cls; + struct MeshTunnel *t = value; + struct GNUNET_MESH_LocalMonitor *msg; + uint32_t npeers; + + npeers = GNUNET_CONTAINER_multihashmap_size (t->peers); + msg = GNUNET_malloc (sizeof(struct GNUNET_MESH_LocalMonitor) + + npeers * sizeof (struct GNUNET_PeerIdentity)); + GNUNET_PEER_resolve(t->id.oid, &msg->owner); + msg->tunnel_id = htonl (t->id.tid); + msg->header.size = htons (sizeof (struct GNUNET_MESH_LocalMonitor) + + npeers * sizeof (struct GNUNET_PeerIdentity)); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_INFO_TUNNELS); + msg->npeers = 0; + (void) GNUNET_CONTAINER_multihashmap_iterate (t->peers, + monitor_peers_iterator, + msg); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "* sending info about tunnel %s [%u] (%u peers)\n", + GNUNET_i2s (&msg->owner), t->id.tid, npeers); + + if (msg->npeers != npeers) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Get tunnels fail: size %u - iter %u\n", + npeers, msg->npeers); + } + + msg->npeers = htonl (npeers); + GNUNET_SERVER_notification_context_unicast (nc, client, + &msg->header, GNUNET_NO); + return GNUNET_YES; +} + + +/** + * Handler for client's MONITOR request. + * + * @param cls Closure (unused). + * @param client Identification of the client. + * @param message The actual message. + */ +static void +handle_local_get_tunnels (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct MeshClient *c; + + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received get tunnels request from client %u\n", + c->id); + GNUNET_CONTAINER_multihashmap_iterate (tunnels, + monitor_all_tunnels_iterator, + client); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Get tunnels request from client %u completed\n", + c->id); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Data needed to build a Monitor_Tunnel message. + */ +struct MeshMonitorTunnelContext +{ + /** + * Partial message, including peer count. + */ + struct GNUNET_MESH_LocalMonitor *msg; + + /** + * Hashmap with positions: peer->position. + */ + struct GNUNET_CONTAINER_MultiHashMap *lookup; + + /** + * Index of the parent of each peer in the message, realtive to the absolute + * order in the array (can be in a previous message). + */ + uint32_t parents[1024]; + + /** + * Peers visited so far in the tree, aka position of the current peer. + */ + unsigned int npeers; + + /** + * Client requesting the info. + */ + struct MeshClient *c; +}; + + +/** + * Send a client a message about the structure of a tunnel. + * + * @param ctx Context of the tunnel iteration, with info regarding the state + * of the execution and the number of peers visited for this message. + */ +static void +send_client_tunnel_info (struct MeshMonitorTunnelContext *ctx) +{ + struct GNUNET_MESH_LocalMonitor *resp = ctx->msg; + struct GNUNET_PeerIdentity *pid; + unsigned int *parent; + size_t size; + + size = sizeof (struct GNUNET_MESH_LocalMonitor); + size += (sizeof (struct GNUNET_PeerIdentity) + sizeof (int)) * resp->npeers; + resp->header.size = htons (size); + pid = (struct GNUNET_PeerIdentity *) &resp[1]; + parent = (unsigned int *) &pid[resp->npeers]; + memcpy (parent, ctx->parents, sizeof(uint32_t) * resp->npeers); + GNUNET_SERVER_notification_context_unicast (nc, ctx->c->handle, + &resp->header, GNUNET_NO); +} + +/** + * Iterator over a tunnel tree to build a message containing all peers + * the in the tunnel, including relay nodes. + * + * @param cls Closure (pointer to pointer of message being built). + * @param peer Short ID of a peer. + * @param parent Short ID of the @c peer 's parent. + */ +static void +tunnel_tree_iterator (void *cls, + GNUNET_PEER_Id peer, + GNUNET_PEER_Id parent) +{ + struct MeshMonitorTunnelContext *ctx = cls; + struct GNUNET_MESH_LocalMonitor *msg; + struct GNUNET_PeerIdentity *pid; + struct GNUNET_PeerIdentity ppid; + + msg = ctx->msg; + pid = (struct GNUNET_PeerIdentity *) &msg[1]; + GNUNET_PEER_resolve (peer, &pid[msg->npeers]); + GNUNET_CONTAINER_multihashmap_put (ctx->lookup, + &pid[msg->npeers].hashPubKey, + (void *) (long) ctx->npeers, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + GNUNET_PEER_resolve (parent, &ppid); + ctx->parents[msg->npeers] = + htonl ((long) GNUNET_CONTAINER_multihashmap_get (ctx->lookup, + &ppid.hashPubKey)); + + ctx->npeers++; + msg->npeers++; + + if (sizeof (struct GNUNET_MESH_LocalMonitor) + + (msg->npeers + 1) * + (sizeof (struct GNUNET_PeerIdentity) + sizeof (uint32_t)) + > USHRT_MAX) + { + send_client_tunnel_info (ctx); + msg->npeers = 0; + } +} + + +/** + * Handler for client's MONITOR_TUNNEL request. + * + * @param cls Closure (unused). + * @param client Identification of the client. + * @param message The actual message. + */ +static void +handle_local_show_tunnel (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_MESH_LocalMonitor *msg; + struct GNUNET_MESH_LocalMonitor *resp; + struct MeshMonitorTunnelContext ctx; + struct MeshClient *c; + struct MeshTunnel *t; + + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + msg = (struct GNUNET_MESH_LocalMonitor *) message; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received tunnel info request from client %u for tunnel %s[%X]\n", + c->id, + &msg->owner, + ntohl (msg->tunnel_id)); + t = tunnel_get (&msg->owner, ntohl (msg->tunnel_id)); + if (NULL == t) + { + /* We don't know the tunnel */ + struct GNUNET_MESH_LocalMonitor warn; + + warn = *msg; + warn.npeers = htonl (UINT_MAX); + GNUNET_SERVER_notification_context_unicast (nc, client, + &warn.header, + GNUNET_NO); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + + /* Initialize context */ + resp = GNUNET_malloc (USHRT_MAX); /* avoid realloc'ing on each step */ + *resp = *msg; + resp->npeers = 0; + ctx.msg = resp; + ctx.lookup = GNUNET_CONTAINER_multihashmap_create (4 * t->peers_total, + GNUNET_YES); + ctx.c = c; + + /* Collect and send information */ + tree_iterate_all (t->tree, &tunnel_tree_iterator, &ctx); + send_client_tunnel_info (&ctx); + + /* Free context */ + GNUNET_CONTAINER_multihashmap_destroy (ctx.lookup); + GNUNET_free (resp); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Monitor tunnel request from client %u completed\n", + c->id); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Functions to handle messages from clients + */ +static struct GNUNET_SERVER_MessageHandler client_handlers[] = { + {&handle_local_new_client, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_CONNECT, 0}, + {&handle_local_announce_regex, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_ANNOUNCE_REGEX, 0}, + {&handle_local_tunnel_create, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_CREATE, + sizeof (struct GNUNET_MESH_TunnelMessage)}, + {&handle_local_tunnel_destroy, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_DESTROY, + sizeof (struct GNUNET_MESH_TunnelMessage)}, + {&handle_local_tunnel_speed, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_MIN, + sizeof (struct GNUNET_MESH_TunnelMessage)}, + {&handle_local_tunnel_speed, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_MAX, + sizeof (struct GNUNET_MESH_TunnelMessage)}, + {&handle_local_tunnel_buffer, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_BUFFER, + sizeof (struct GNUNET_MESH_TunnelMessage)}, + {&handle_local_tunnel_buffer, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_NOBUFFER, + sizeof (struct GNUNET_MESH_TunnelMessage)}, + {&handle_local_connect_add, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_ADD, + sizeof (struct GNUNET_MESH_PeerControl)}, + {&handle_local_connect_del, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_DEL, + sizeof (struct GNUNET_MESH_PeerControl)}, + {&handle_local_blacklist, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_BLACKLIST, + sizeof (struct GNUNET_MESH_PeerControl)}, + {&handle_local_unblacklist, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_UNBLACKLIST, + sizeof (struct GNUNET_MESH_PeerControl)}, + {&handle_local_connect_by_type, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_ADD_BY_TYPE, + sizeof (struct GNUNET_MESH_ConnectPeerByType)}, + {&handle_local_connect_by_string, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_ADD_BY_STRING, 0}, + {&handle_local_unicast, NULL, + GNUNET_MESSAGE_TYPE_MESH_UNICAST, 0}, + {&handle_local_to_origin, NULL, + GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN, 0}, + {&handle_local_multicast, NULL, + GNUNET_MESSAGE_TYPE_MESH_MULTICAST, 0}, + {&handle_local_ack, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK, + sizeof (struct GNUNET_MESH_LocalAck)}, + {&handle_local_get_tunnels, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_INFO_TUNNELS, + sizeof (struct GNUNET_MessageHeader)}, + {&handle_local_show_tunnel, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_INFO_TUNNEL, + sizeof (struct GNUNET_MESH_LocalMonitor)}, + {NULL, NULL, 0, 0} +}; + + +/** + * To be called on core init/fail. + * + * @param cls service closure + * @param server handle to the server for this service + * @param identity the public identity of this peer + */ +static void +core_init (void *cls, struct GNUNET_CORE_Handle *server, + const struct GNUNET_PeerIdentity *identity) +{ + static int i = 0; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Core init\n"); + core_handle = server; + if (0 != memcmp (identity, &my_full_id, sizeof (my_full_id)) || + NULL == server) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Wrong CORE service\n")); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + " core id %s\n", + GNUNET_i2s (identity)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + " my id %s\n", + GNUNET_i2s (&my_full_id)); + GNUNET_SCHEDULER_shutdown (); // Try gracefully + if (10 < i++) + GNUNET_abort(); // Try harder + } + return; +} + + +/** + * Method called whenever a given peer connects. + * + * @param cls closure + * @param peer peer identity this notification is about + * @param atsi performance data for the connection + * @param atsi_count number of records in 'atsi' + */ +static void +core_connect (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct MeshPeerInfo *peer_info; + struct MeshPeerPath *path; + + DEBUG_CONN ("Peer connected\n"); + DEBUG_CONN (" %s\n", GNUNET_i2s (&my_full_id)); + peer_info = peer_info_get (peer); + if (myid == peer_info->id) + { + DEBUG_CONN (" (self)\n"); + return; + } + else + { + DEBUG_CONN (" %s\n", GNUNET_i2s (peer)); + } + path = path_new (2); + path->peers[0] = myid; + path->peers[1] = peer_info->id; + GNUNET_PEER_change_rc (myid, 1); + GNUNET_PEER_change_rc (peer_info->id, 1); + peer_info_add_path (peer_info, path, GNUNET_YES); + GNUNET_STATISTICS_update (stats, "# peers", 1, GNUNET_NO); + return; +} + + +/** + * Method called whenever a peer disconnects. + * + * @param cls closure + * @param peer peer identity this notification is about + */ +static void +core_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer) +{ + struct MeshPeerInfo *pi; + struct MeshPeerQueue *q; + struct MeshPeerQueue *n; + + DEBUG_CONN ("Peer disconnected\n"); + pi = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey); + if (NULL == pi) + { + GNUNET_break (0); + return; + } + q = pi->queue_head; + while (NULL != q) + { + n = q->next; + /* TODO try to reroute this traffic instead */ + queue_destroy(q, GNUNET_YES); + q = n; + } + if (NULL != pi->core_transmit) + { + GNUNET_CORE_notify_transmit_ready_cancel(pi->core_transmit); + pi->core_transmit = NULL; + } + peer_info_remove_path (pi, pi->id, myid); + if (myid == pi->id) + { + DEBUG_CONN (" (self)\n"); + } + GNUNET_STATISTICS_update (stats, "# peers", -1, GNUNET_NO); + return; +} + + +/******************************************************************************/ +/************************ MAIN FUNCTIONS ****************************/ +/******************************************************************************/ + +/** + * Iterator over tunnel hash map entries to destroy the tunnel during shutdown. + * + * @param cls closure + * @param key current key code + * @param value value in the hash map + * @return GNUNET_YES if we should continue to iterate, + * GNUNET_NO if not. + */ +static int +shutdown_tunnel (void *cls, const struct GNUNET_HashCode * key, void *value) +{ + struct MeshTunnel *t = value; + + tunnel_destroy (t); + return GNUNET_YES; +} + +/** + * Iterator over peer hash map entries to destroy the tunnel during shutdown. + * + * @param cls closure + * @param key current key code + * @param value value in the hash map + * @return GNUNET_YES if we should continue to iterate, + * GNUNET_NO if not. + */ +static int +shutdown_peer (void *cls, const struct GNUNET_HashCode * key, void *value) +{ + struct MeshPeerInfo *p = value; + struct MeshPeerQueue *q; + struct MeshPeerQueue *n; + + q = p->queue_head; + while (NULL != q) + { + n = q->next; + if (q->peer == p) + { + queue_destroy(q, GNUNET_YES); + } + q = n; + } + peer_info_destroy (p); + return GNUNET_YES; +} + + +/** + * Task run during shutdown. + * + * @param cls unused + * @param tc unused + */ +static void +shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "shutting down\n"); + + if (core_handle != NULL) + { + GNUNET_CORE_disconnect (core_handle); + core_handle = NULL; + } + if (NULL != keygen) + { + GNUNET_CRYPTO_rsa_key_create_stop (keygen); + keygen = NULL; + } + GNUNET_CONTAINER_multihashmap_iterate (tunnels, &shutdown_tunnel, NULL); + GNUNET_CONTAINER_multihashmap_iterate (peers, &shutdown_peer, NULL); + if (dht_handle != NULL) + { + GNUNET_DHT_disconnect (dht_handle); + dht_handle = NULL; + } + if (nc != NULL) + { + GNUNET_SERVER_notification_context_destroy (nc); + nc = NULL; + } + if (GNUNET_SCHEDULER_NO_TASK != announce_id_task) + { + GNUNET_SCHEDULER_cancel (announce_id_task); + announce_id_task = GNUNET_SCHEDULER_NO_TASK; + } + if (GNUNET_SCHEDULER_NO_TASK != announce_applications_task) + { + GNUNET_SCHEDULER_cancel (announce_applications_task); + announce_applications_task = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "shut down\n"); +} + + +/** + * Callback for hostkey read/generation + * + * @param cls Closure (Configuration handle). + * @param pk the private key + * @param emsg error message + */ +static void +key_generation_cb (void *cls, + struct GNUNET_CRYPTO_RsaPrivateKey *pk, + const char *emsg) +{ + const struct GNUNET_CONFIGURATION_Handle *c = cls; + struct MeshPeerInfo *peer; + struct MeshPeerPath *p; + + keygen = NULL; + if (NULL == pk) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Mesh service could not access hostkey: %s. Exiting.\n"), + emsg); + GNUNET_SCHEDULER_shutdown (); + return; + } + my_private_key = pk; + GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key); + GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), + &my_full_id.hashPubKey); + myid = GNUNET_PEER_intern (&my_full_id); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Mesh for peer [%s] starting\n", + GNUNET_i2s(&my_full_id)); + +// transport_handle = GNUNET_TRANSPORT_connect(c, +// &my_full_id, +// NULL, +// NULL, +// NULL, +// NULL); + + core_handle = GNUNET_CORE_connect (c, /* Main configuration */ + NULL, /* Closure passed to MESH functions */ + &core_init, /* Call core_init once connected */ + &core_connect, /* Handle connects */ + &core_disconnect, /* remove peers on disconnects */ + NULL, /* Don't notify about all incoming messages */ + GNUNET_NO, /* For header only in notification */ + NULL, /* Don't notify about all outbound messages */ + GNUNET_NO, /* For header-only out notification */ + core_handlers); /* Register these handlers */ + + if (core_handle == NULL) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + + next_tid = 0; + next_local_tid = GNUNET_MESH_LOCAL_TUNNEL_ID_SERV; + + + GNUNET_SERVER_add_handlers (server_handle, client_handlers); + nc = GNUNET_SERVER_notification_context_create (server_handle, 1); + GNUNET_SERVER_disconnect_notify (server_handle, + &handle_local_client_disconnect, NULL); + + + clients = NULL; + clients_tail = NULL; + next_client_id = 0; + + announce_applications_task = GNUNET_SCHEDULER_NO_TASK; + announce_id_task = GNUNET_SCHEDULER_add_now (&announce_id, cls); + + /* Create a peer_info for the local peer */ + peer = peer_info_get (&my_full_id); + p = path_new (1); + p->peers[0] = myid; + GNUNET_PEER_change_rc (myid, 1); + peer_info_add_path (peer, p, GNUNET_YES); + GNUNET_SERVER_resume (server_handle); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Mesh service running\n"); +} + + +/** + * Process mesh requests. + * + * @param cls closure + * @param server the initialized server + * @param c configuration to use + */ +static void +run (void *cls, struct GNUNET_SERVER_Handle *server, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + char *keyfile; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "starting to run\n"); + server_handle = server; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (c, "GNUNETD", "HOSTKEY", + &keyfile)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("%s service is lacking key configuration settings (%s). Exiting.\n"), + "mesh", "hostkey"); + GNUNET_SCHEDULER_shutdown (); + return; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (c, "MESH", "REFRESH_PATH_TIME", + &refresh_path_time)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("%s service is lacking key configuration settings (%s). Exiting.\n"), + "mesh", "refresh path time"); + GNUNET_SCHEDULER_shutdown (); + return; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (c, "MESH", "APP_ANNOUNCE_TIME", + &app_announce_time)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("%s service is lacking key configuration settings (%s). Exiting.\n"), + "mesh", "app announce time"); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "APP_ANNOUNCE_TIME %llu ms\n", + app_announce_time.rel_value); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (c, "MESH", "ID_ANNOUNCE_TIME", + &id_announce_time)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("%s service is lacking key configuration settings (%s). Exiting.\n"), + "mesh", "id announce time"); + GNUNET_SCHEDULER_shutdown (); + return; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (c, "MESH", "CONNECT_TIMEOUT", + &connect_timeout)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("%s service is lacking key configuration settings (%s). Exiting.\n"), + "mesh", "connect timeout"); + GNUNET_SCHEDULER_shutdown (); + return; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, "MESH", "MAX_MSGS_QUEUE", + &max_msgs_queue)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("%s service is lacking key configuration settings (%s). Exiting.\n"), + "mesh", "max msgs queue"); + GNUNET_SCHEDULER_shutdown (); + return; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, "MESH", "MAX_TUNNELS", + &max_tunnels)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("%s service is lacking key configuration settings (%s). Exiting.\n"), + "mesh", "max tunnels"); + GNUNET_SCHEDULER_shutdown (); + return; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, "MESH", "DEFAULT_TTL", + &default_ttl)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("%s service is lacking key configuration settings (%s). Using default (%u).\n"), + "mesh", "default ttl", 64); + default_ttl = 64; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, "MESH", "MAX_PEERS", + &max_peers)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("%s service is lacking key configuration settings (%s). Using default (%u).\n"), + "mesh", "max peers", 1000); + max_peers = 1000; + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, "MESH", "DHT_REPLICATION_LEVEL", + &dht_replication_level)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("%s service is lacking key configuration settings (%s). Using default (%u).\n"), + "mesh", "dht replication level", 3); + dht_replication_level = 3; + } + + tunnels = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); + incoming_tunnels = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); + peers = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); + applications = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); + types = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); + + dht_handle = GNUNET_DHT_connect (c, 64); + if (NULL == dht_handle) + { + GNUNET_break (0); + } + stats = GNUNET_STATISTICS_create ("mesh", c); + + GNUNET_SERVER_suspend (server_handle); + /* Scheduled the task to clean up when shutdown is called */ + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, + NULL); + keygen = GNUNET_CRYPTO_rsa_key_create_start (keyfile, + &key_generation_cb, + (void *) c); + GNUNET_free (keyfile); +} + + +/** + * The main function for the mesh 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) +{ + int ret; + int r; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "main()\n"); + r = GNUNET_SERVICE_run (argc, argv, "mesh", GNUNET_SERVICE_OPTION_NONE, &run, + NULL); + ret = (GNUNET_OK == r) ? 0 : 1; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "main() END\n"); + + INTERVAL_SHOW; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Mesh for peer [%s] FWD ACKs %u, BCK ACKs %u\n", + GNUNET_i2s(&my_full_id), debug_fwd_ack, debug_bck_ack); + + return ret; +} diff --git a/src/mesh/gnunet-service-mesh.c b/src/mesh/gnunet-service-mesh.c index 36c6115..2ee5b9d 100644 --- a/src/mesh/gnunet-service-mesh.c +++ b/src/mesh/gnunet-service-mesh.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2001 - 2011 Christian Grothoff (and other contributing authors) + (C) 2001-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 @@ -37,41 +37,33 @@ * TODO: * - error reporting (CREATE/CHANGE/ADD/DEL?) -- new message! * - partial disconnect reporting -- same as error reporting? - * - add vs create? change vs. keep-alive? same msg or different ones? -- thinking... - * - speed requirement specification (change?) in mesh API -- API call * - add ping message * - relay corking down to core * - set ttl relative to tree depth + * - Add data ACK count in path ACK + * - Make common GNUNET_MESH_Data header for unicast, to_orig, multicast * TODO END */ #include "platform.h" #include "mesh.h" #include "mesh_protocol.h" -#include "gnunet_dht_service.h" #include "mesh_tunnel_tree.h" +#include "block_mesh.h" +#include "gnunet_dht_service.h" +#include "gnunet_statistics_service.h" +#include "gnunet_regex_lib.h" -/* TODO: move into configuration file */ -#define REFRESH_PATH_TIME GNUNET_TIME_relative_multiply(\ - GNUNET_TIME_UNIT_SECONDS,\ - 300) -#define APP_ANNOUNCE_TIME GNUNET_TIME_relative_multiply(\ - GNUNET_TIME_UNIT_SECONDS,\ - 5) - -#define ID_ANNOUNCE_TIME GNUNET_TIME_relative_multiply(\ - GNUNET_TIME_UNIT_SECONDS,\ - 5) - -#define UNACKNOWLEDGED_WAIT GNUNET_TIME_relative_multiply(\ - GNUNET_TIME_UNIT_SECONDS,\ - 2) -#define DEFAULT_TTL 64 +#define MESH_BLOOM_SIZE 128 -/* TODO END */ +#define MESH_DEBUG_REGEX GNUNET_YES +#define MESH_DEBUG_DHT GNUNET_NO +#define MESH_DEBUG_CONNECTION GNUNET_NO +#define MESH_DEBUG_TIMING __LINUX__ && GNUNET_NO -#define MESH_DEBUG_DHT GNUNET_YES -#define MESH_DEBUG_CONNECTION GNUNET_NO +#define MESH_MAX_POLL_TIME GNUNET_TIME_relative_multiply (\ + GNUNET_TIME_UNIT_MINUTES,\ + 10) #if MESH_DEBUG_CONNECTION #define DEBUG_CONN(...) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__) @@ -85,12 +77,43 @@ #define DEBUG_DHT(...) #endif +#if MESH_DEBUG_REGEX +#define DEBUG_REGEX(...) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__) +#else +#define DEBUG_REGEX(...) +#endif + +#if MESH_DEBUG_TIMING +#include <time.h> +double __sum; +uint64_t __count; +struct timespec __mesh_start; +struct timespec __mesh_end; +#define INTERVAL_START clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &(__mesh_start)) +#define INTERVAL_END \ +do {\ + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &(__mesh_end));\ + double __diff = __mesh_end.tv_nsec - __mesh_start.tv_nsec;\ + if (__diff < 0) __diff += 1000000000;\ + __sum += __diff;\ + __count++;\ +} while (0) +#define INTERVAL_SHOW \ +if (0 < __count)\ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "AVG process time: %f ns\n", __sum/__count) +#else +#define INTERVAL_START +#define INTERVAL_END +#define INTERVAL_SHOW +#endif + /******************************************************************************/ /************************ DATA STRUCTURES ****************************/ /******************************************************************************/ /** FWD declaration */ struct MeshPeerInfo; +struct MeshClient; /** @@ -101,12 +124,11 @@ struct MeshData /** Tunnel it belongs to. */ struct MeshTunnel *t; - /** In case of a multicast, task to allow a client to send more data if - * some neighbor is too slow. */ - GNUNET_SCHEDULER_TaskIdentifier *task; + /** How many remaining neighbors still hav't got it. */ + unsigned int reference_counter; /** How many remaining neighbors we need to send this to. */ - unsigned int *reference_counter; + unsigned int total_out; /** Size of the data. */ size_t data_len; @@ -117,6 +139,121 @@ struct MeshData /** + * Struct containing info about a queued transmission to this peer + */ +struct MeshPeerQueue +{ + /** + * DLL next + */ + struct MeshPeerQueue *next; + + /** + * DLL previous + */ + struct MeshPeerQueue *prev; + + /** + * Peer this transmission is directed to. + */ + struct MeshPeerInfo *peer; + + /** + * Tunnel this message belongs to. + */ + struct MeshTunnel *tunnel; + + /** + * Pointer to info stucture used as cls. + */ + void *cls; + + /** + * Type of message + */ + uint16_t type; + + /** + * Size of the message + */ + size_t size; +}; + + +/** + * Struct to store regex information announced by clients. + */ +struct MeshRegexDescriptor +{ + /** + * Regular expression itself. + */ + char *regex; + + /** + * How many characters per edge can we squeeze? + */ + uint16_t compression; + + /** + * Handle to announce the regex. + */ + struct GNUNET_REGEX_announce_handle *h; +}; + + +/** + * Struct to keep information of searches of services described by a regex + * using a user-provided string service description. + */ +struct MeshRegexSearchInfo +{ + /** + * Which tunnel is this for + */ + struct MeshTunnel *t; + + /** + * User provided description of the searched service. + */ + char *description; + + /** + * Regex search handle. + */ + struct GNUNET_REGEX_search_handle *search_handle; + + /** + * Peer that is connecting via connect_by_string. When connected, free ctx. + */ + GNUNET_PEER_Id peer; + + /** + * Other peers that are found but not yet being connected to. + */ + GNUNET_PEER_Id *peers; + + /** + * Number of elements in peers. + */ + unsigned int n_peers; + + /** + * Next peer to try to connect to. + */ + unsigned int i_peer; + + /** + * Timeout for a connect attempt. + * When reached, try to connect to a different peer, if any. If not, + * try the same peer again. + */ + GNUNET_SCHEDULER_TaskIdentifier timeout; + +}; + + +/** * Struct containing all info possibly needed to build a package when called * back by core. */ @@ -131,9 +268,6 @@ struct MeshTransmissionDescriptor /** Ultimate destination of the packet */ GNUNET_PEER_Id destination; - /** Which handler was used to request the transmission */ - unsigned int handler_n; - /** Data descriptor */ struct MeshData* mesh_data; }; @@ -155,6 +289,11 @@ struct MeshPeerInfo struct GNUNET_TIME_Absolute last_contact; /** + * Task handler for delayed connect task; + */ + GNUNET_SCHEDULER_TaskIdentifier connect_task; + + /** * Number of attempts to reconnect so far */ int n_reconnect_attempts; @@ -180,21 +319,6 @@ struct MeshPeerInfo struct MeshPathInfo *dhtgetcls; /** - * Handles to stop queued transmissions for this peer - */ - struct GNUNET_CORE_TransmitHandle *core_transmit[CORE_QUEUE_SIZE]; - - /** - * Pointer to info stuctures used as cls for queued transmissions - */ - void *infos[CORE_QUEUE_SIZE]; - - /** - * Type of message being in each transmission - */ - uint16_t types[CORE_QUEUE_SIZE]; - - /** * Array of tunnels this peer participates in * (most probably a small amount, therefore not a hashmap) * When the path to the peer changes, notify these tunnels to let them @@ -206,46 +330,29 @@ struct MeshPeerInfo * Number of tunnels this peers participates in */ unsigned int ntunnels; -}; + /** + * Transmission queue to core DLL head + */ + struct MeshPeerQueue *queue_head; -/** - * Data scheduled to transmit (to local client or remote peer) - */ -struct MeshQueue -{ - /** - * Double linked list - */ - struct MeshQueue *next; - struct MeshQueue *prev; - - /** - * Target of the data (NULL if target is client) - */ - struct MeshPeerInfo *peer; - - /** - * Client to send the data to (NULL if target is peer) - */ - struct MeshClient *client; - - /** - * Size of the message to transmit - */ - unsigned int size; + /** + * Transmission queue to core DLL tail + */ + struct MeshPeerQueue *queue_tail; - /** - * How old is the data? - */ - struct GNUNET_TIME_Absolute timestamp; + /** + * How many messages are in the queue to this peer. + */ + unsigned int queue_n; - /** - * Data itself - */ - struct GNUNET_MessageHeader *data; + /** + * Handle to for queued transmissions + */ + struct GNUNET_CORE_TransmitHandle *core_transmit; }; + /** * Globally unique tunnel identification (owner + number) * DO NOT USE OVER THE NETWORK @@ -265,8 +372,6 @@ struct MESH_TunnelID }; -struct MeshClient; /* FWD declaration */ - /** * Struct containing all information regarding a tunnel * For an intermediate node the improtant info used will be: @@ -294,9 +399,77 @@ struct MeshTunnel MESH_TunnelNumber local_tid_dest; /** - * ID of the last multicast packet seen/sent. + * Is the speed on the tunnel limited to the slowest peer? + */ + int speed_min; + + /** + * Is the tunnel bufferless (minimum latency)? + */ + int nobuffer; + + /** + * Packet ID of the last fwd packet seen (sent/retransmitted/received). + */ + uint32_t fwd_pid; + + /** + * Packet ID of the last bck packet sent (unique counter per hop). + */ + uint32_t bck_pid; + + /** + * SKIP value for this tunnel. + */ + uint32_t skip; + + /** + * Force sending ACK? Flag to allow duplicate ACK on POLL. + */ + int force_ack; + + /** + * MeshTunnelChildInfo of all children, indexed by GNUNET_PEER_Id. + * Contains the Flow Control info: FWD ACK value received, + * last BCK ACK sent, PID and SKIP values. + */ + struct GNUNET_CONTAINER_MultiHashMap *children_fc; + + /** + * Last ACK sent towards the origin (for traffic towards leaf node). + */ + uint32_t last_fwd_ack; + + /** + * BCK ACK value received from the hop towards the owner of the tunnel, + * (previous node / owner): up to what message PID can we sent back to him. + */ + uint32_t bck_ack; + + /** + * How many messages are in the forward queue (towards leaves). + */ + unsigned int fwd_queue_n; + + /** + * How many messages do we accept in the forward queue. + */ + unsigned int fwd_queue_max; + + /** + * How many messages are in the backward queue (towards origin). */ - uint32_t mid; + unsigned int bck_queue_n; + + /** + * How many messages do we accept in the backward queue. + */ + unsigned int bck_queue_max; + + /** + * Task to poll peer in case of a stall. + */ + GNUNET_SCHEDULER_TaskIdentifier fc_poll_bck; /** * Last time the tunnel was used @@ -325,17 +498,22 @@ struct MeshTunnel struct MeshClient *owner; /** - * Clients that have been informed about the tunnel, if any + * Clients that have been informed about and want to stay in the tunnel. */ struct MeshClient **clients; /** - * Number of elements in clients + * Flow control info for each client. + */ + struct MeshTunnelClientInfo *clients_fc; + + /** + * Number of elements in clients/clients_fc */ unsigned int nclients; /** - * Clients that have requested to leave the tunnel + * Clients that have been informed but requested to leave the tunnel. */ struct MeshClient **ignore; @@ -345,10 +523,19 @@ struct MeshTunnel unsigned int nignore; /** - * Messages ready to transmit + * Blacklisted peers + */ + GNUNET_PEER_Id *blacklisted; + + /** + * Number of elements in blacklisted */ - struct MeshQueue *queue_head; - struct MeshQueue *queue_tail; + unsigned int nblacklisted; + + /** + * Bloomfilter (for peer identities) to stop circular routes + */ + char bloomfilter[MESH_BLOOM_SIZE]; /** * Tunnel paths @@ -365,18 +552,165 @@ struct MeshTunnel */ struct GNUNET_DHT_GetHandle *dht_get_type; + /** + * Handle for the regex search for a connect_by_string + */ + struct MeshRegexSearchInfo *regex_search; + + /** + * Task to keep the used paths alive + */ + GNUNET_SCHEDULER_TaskIdentifier path_refresh_task; + + /** + * Task to destroy the tunnel after timeout + * + * FIXME: merge the two? a tunnel will have either + * a path refresh OR a timeout, never both! + */ + GNUNET_SCHEDULER_TaskIdentifier timeout_task; + + /** + * Flag to signal the destruction of the tunnel. + * If this is set GNUNET_YES the tunnel will be destroyed + * when the queue is empty. + */ + int destroy; + + /** + * Total messages pending for this tunnels, payload or not. + */ + unsigned int pending_messages; + /** - * Task to keep the used paths alive + * If the tunnel is empty, destoy it. */ - GNUNET_SCHEDULER_TaskIdentifier path_refresh_task; + GNUNET_SCHEDULER_TaskIdentifier delayed_destroy; +}; + + +/** + * Info about a child node in a tunnel, needed to perform flow control. + */ +struct MeshTunnelChildInfo +{ + /** + * ID of the child node. + */ + GNUNET_PEER_Id id; + + /** + * SKIP value. + */ + uint32_t skip; + + /** + * Last sent PID. + */ + uint32_t fwd_pid; + + /** + * Last received PID. + */ + uint32_t bck_pid; + + /** + * Maximum PID allowed (FWD ACK received). + */ + uint32_t fwd_ack; + + /** + * Last ACK sent to that child (BCK ACK). + */ + uint32_t bck_ack; + + /** + * Circular buffer pointing to MeshPeerQueue elements for all + * payload traffic going to this child. + * Size determined by the tunnel queue size (@c t->fwd_queue_max). + */ + struct MeshPeerQueue **send_buffer; + + /** + * Index of the oldest element in the send_buffer. + */ + unsigned int send_buffer_start; + + /** + * How many elements are already in the buffer. + */ + unsigned int send_buffer_n; + + /** + * Tunnel this info is about + */ + struct MeshTunnel *t; + + /** + * Task to poll peer in case of a stall. + */ + GNUNET_SCHEDULER_TaskIdentifier fc_poll; + + /** + * Time to use for next polling call. + */ + struct GNUNET_TIME_Relative fc_poll_time; +}; + + +/** + * Info about a leaf client of a tunnel, needed to perform flow control. + */ +struct MeshTunnelClientInfo +{ + /** + * PID of the last packet sent to the client (FWD). + */ + uint32_t fwd_pid; /** - * Task to destroy the tunnel after timeout - * - * FIXME: merge the two? a tunnel will have either - * a path refresh OR a timeout, never both! + * PID of the last packet received from the client (BCK). */ - GNUNET_SCHEDULER_TaskIdentifier timeout_task; + uint32_t bck_pid; + + /** + * Maximum PID allowed (FWD ACK received). + */ + uint32_t fwd_ack; + + /** + * Last ACK sent to that child (BCK ACK). + */ + uint32_t bck_ack; +}; + + + +/** + * Info collected during iteration of child nodes in order to get the ACK value + * for a tunnel. + */ +struct MeshTunnelChildIteratorContext +{ + /** + * Tunnel whose info is being collected. + */ + struct MeshTunnel *t; + + /** + * Is this context initialized? Is the value in max_child_ack valid? + */ + int init; + + /** + * Maximum child ACK so far. + */ + uint32_t max_child_ack; + + /** + * Number of children nodes + */ + unsigned int nchildren; }; @@ -399,11 +733,6 @@ struct MeshPathInfo * Path itself */ struct MeshPeerPath *path; - - /** - * Position in peer's transmit queue - */ - unsigned int pos; }; @@ -461,9 +790,28 @@ struct MeshClient * ID of the client, mainly for debug messages */ unsigned int id; + + /** + * Regular expressions describing the services offered by this client. + */ + struct MeshRegexDescriptor *regexes; // FIXME regex add timeout? API to remove a regex? -}; + /** + * Number of regular expressions in regexes. + */ + unsigned int n_regex; + + /** + * Task to refresh all regular expresions in the DHT. + */ + GNUNET_SCHEDULER_TaskIdentifier regex_announce_task; + + /** + * Tmp store for partially retrieved regex. + */ + char *partial_regex; +}; /******************************************************************************/ @@ -491,71 +839,145 @@ mesh_debug (void *cls, int success) } #endif +unsigned int debug_fwd_ack; +unsigned int debug_bck_ack; + #endif /******************************************************************************/ /*********************** GLOBAL VARIABLES ****************************/ /******************************************************************************/ +/************************** Configuration parameters **************************/ + +/** + * How often to send tunnel keepalives. Tunnels timeout after 4 missed. + */ +static struct GNUNET_TIME_Relative refresh_path_time; + +/** + * How often to PUT local application numbers in the DHT. + */ +static struct GNUNET_TIME_Relative app_announce_time; + +/** + * How often to PUT own ID in the DHT. + */ +static struct GNUNET_TIME_Relative id_announce_time; + +/** + * Maximum time allowed to connect to a peer found by string. + */ +static struct GNUNET_TIME_Relative connect_timeout; + /** - * All the clients + * Default TTL for payload packets. + */ +static unsigned long long default_ttl; + +/** + * DHT replication level, see DHT API: GNUNET_DHT_get_start, GNUNET_DHT_put. + */ +static unsigned long long dht_replication_level; + +/** + * How many tunnels are we willing to maintain. + * Local tunnels are always allowed, even if there are more tunnels than max. + */ +static unsigned long long max_tunnels; + +/** + * How many messages *in total* are we willing to queue, divided by number of + * tunnels to get tunnel queue size. + */ +static unsigned long long max_msgs_queue; + +/** + * How many peers do we want to remember? + */ +static unsigned long long max_peers; + + +/*************************** Static global variables **************************/ + +/** + * Hostkey generation context + */ +static struct GNUNET_CRYPTO_RsaKeyGenerationContext *keygen; + +/** + * DLL with all the clients, head. */ static struct MeshClient *clients; + +/** + * DLL with all the clients, tail. + */ static struct MeshClient *clients_tail; /** - * Tunnels known, indexed by MESH_TunnelID (MeshTunnel) + * Tunnels known, indexed by MESH_TunnelID (MeshTunnel). */ static struct GNUNET_CONTAINER_MultiHashMap *tunnels; /** + * Number of tunnels known. + */ +static unsigned long long n_tunnels; + +/** * Tunnels incoming, indexed by MESH_TunnelNumber - * (which is greater than GNUNET_MESH_LOCAL_TUNNEL_ID_SERV) + * (which is greater than GNUNET_MESH_LOCAL_TUNNEL_ID_SERV). */ static struct GNUNET_CONTAINER_MultiHashMap *incoming_tunnels; /** - * Peers known, indexed by PeerIdentity (MeshPeerInfo) + * Peers known, indexed by PeerIdentity (MeshPeerInfo). */ static struct GNUNET_CONTAINER_MultiHashMap *peers; -/** - * Handle to communicate with core +/* + * Handle to communicate with transport */ -static struct GNUNET_CORE_Handle *core_handle; +// static struct GNUNET_TRANSPORT_Handle *transport_handle; /** - * Handle to communicate with transport + * Handle to communicate with core. */ -// static struct GNUNET_TRANSPORT_Handle *transport_handle; +static struct GNUNET_CORE_Handle *core_handle; /** - * Handle to use DHT + * Handle to use DHT. */ static struct GNUNET_DHT_Handle *dht_handle; /** - * Handle to server + * Handle to server. */ static struct GNUNET_SERVER_Handle *server_handle; /** - * Notification context, to send messages to local clients + * Handle to the statistics service. + */ +static struct GNUNET_STATISTICS_Handle *stats; + +/** + * Notification context, to send messages to local clients. */ static struct GNUNET_SERVER_NotificationContext *nc; /** - * Local peer own ID (memory efficient handle) + * Local peer own ID (memory efficient handle). */ static GNUNET_PEER_Id myid; /** - * Local peer own ID (full value) + * Local peer own ID (full value). */ static struct GNUNET_PeerIdentity my_full_id; /** - * Own private key + * Own private key. */ static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; @@ -565,47 +987,451 @@ static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key; /** - * Tunnel ID for the next created tunnel (global tunnel number) + * Tunnel ID for the next created tunnel (global tunnel number). */ static MESH_TunnelNumber next_tid; /** - * Tunnel ID for the next incoming tunnel (local tunnel number) + * Tunnel ID for the next incoming tunnel (local tunnel number). */ static MESH_TunnelNumber next_local_tid; /** - * All application types provided by this peer + * All application types provided by this peer. */ static struct GNUNET_CONTAINER_MultiHashMap *applications; /** - * All message types clients of this peer are interested in + * All message types clients of this peer are interested in. */ static struct GNUNET_CONTAINER_MultiHashMap *types; /** - * Task to periodically announce provided applications + * Task to periodically announce provided applications. */ GNUNET_SCHEDULER_TaskIdentifier announce_applications_task; /** - * Task to periodically announce itself in the network + * Task to periodically announce itself in the network. */ GNUNET_SCHEDULER_TaskIdentifier announce_id_task; /** - * Next ID to assign to a client + * Next ID to assign to a client. */ unsigned int next_client_id; +/******************************************************************************/ +/*********************** DECLARATIONS **************************/ +/******************************************************************************/ + +/** + * Function to process paths received for a new peer addition. The recorded + * paths form the initial tunnel, which can be optimized later. + * Called on each result obtained for the DHT search. + * + * @param cls closure + * @param exp when will this value expire + * @param key key of the result + * @param type type of the result + * @param size number of bytes in data + * @param data pointer to the result data + */ +static void +dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp, + const struct GNUNET_HashCode * key, + const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_length, enum GNUNET_BLOCK_Type type, + size_t size, const void *data); + + +/** + * Retrieve the MeshPeerInfo stucture associated with the peer, create one + * and insert it in the appropiate structures if the peer is not known yet. + * + * @param peer Full identity of the peer. + * + * @return Existing or newly created peer info. + */ +static struct MeshPeerInfo * +peer_info_get (const struct GNUNET_PeerIdentity *peer); + + +/** + * Retrieve the MeshPeerInfo stucture associated with the peer, create one + * and insert it in the appropiate structures if the peer is not known yet. + * + * @param peer Short identity of the peer. + * + * @return Existing or newly created peer info. + */ +static struct MeshPeerInfo * +peer_info_get_short (const GNUNET_PEER_Id peer); + + +/** + * Try to establish a new connection to this peer. + * Use the best path for the given tunnel. + * If the peer doesn't have any path to it yet, try to get one. + * If the peer already has some path, send a CREATE PATH towards it. + * + * @param peer PeerInfo of the peer. + * @param t Tunnel for which to create the path, if possible. + */ +static void +peer_info_connect (struct MeshPeerInfo *peer, struct MeshTunnel *t); + + +/** + * Build a PeerPath from the paths returned from the DHT, reversing the paths + * to obtain a local peer -> destination path and interning the peer ids. + * + * @return Newly allocated and created path + */ +static struct MeshPeerPath * +path_build_from_dht (const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_length); + + +/** + * Adds a path to the peer_infos of all the peers in the path + * + * @param p Path to process. + * @param confirmed Whether we know if the path works or not. + */ +static void +path_add_to_peers (struct MeshPeerPath *p, int confirmed); + + +/** + * Add a peer to a tunnel, accomodating paths accordingly and initializing all + * needed rescources. + * If peer already exists, reevaluate shortest path and change if different. + * + * @param t Tunnel we want to add a new peer to + * @param peer PeerInfo of the peer being added + * + */ +static void +tunnel_add_peer (struct MeshTunnel *t, struct MeshPeerInfo *peer); + + +/** + * Removes an explicit path from a tunnel, freeing all intermediate nodes + * that are no longer needed, as well as nodes of no longer reachable peers. + * The tunnel itself is also destoyed if results in a remote empty tunnel. + * + * @param t Tunnel from which to remove the path. + * @param peer Short id of the peer which should be removed. + */ +static void +tunnel_delete_peer (struct MeshTunnel *t, GNUNET_PEER_Id peer); + + +/** + * Search for a tunnel by global ID using full PeerIdentities. + * + * @param oid owner of the tunnel. + * @param tid global tunnel number. + * + * @return tunnel handler, NULL if doesn't exist. + */ +static struct MeshTunnel * +tunnel_get (const struct GNUNET_PeerIdentity *oid, MESH_TunnelNumber tid); + + +/** + * Delete an active client from the tunnel. + * + * @param t Tunnel. + * @param c Client. + */ +static void +tunnel_delete_active_client (struct MeshTunnel *t, const struct MeshClient *c); + +/** + * Notify a tunnel that a connection has broken that affects at least + * some of its peers. + * + * @param t Tunnel affected. + * @param p1 Peer that got disconnected from p2. + * @param p2 Peer that got disconnected from p1. + * + * @return Short ID of the peer disconnected (either p1 or p2). + * 0 if the tunnel remained unaffected. + */ +static GNUNET_PEER_Id +tunnel_notify_connection_broken (struct MeshTunnel *t, GNUNET_PEER_Id p1, + GNUNET_PEER_Id p2); + + +/** + * Get the current ack value for a tunnel, for data going from root to leaves, + * taking in account the tunnel mode and the status of all children and clients. + * + * @param t Tunnel. + * + * @return Maximum PID allowed. + */ +static uint32_t +tunnel_get_fwd_ack (struct MeshTunnel *t); + + +/** + * Add a client to a tunnel, initializing all needed data structures. + * + * @param t Tunnel to which add the client. + * @param c Client which to add to the tunnel. + */ +static void +tunnel_add_client (struct MeshTunnel *t, struct MeshClient *c); + + +/** + * @brief Queue and pass message to core when possible. + * + * If type is payload (UNICAST, TO_ORIGIN, MULTICAST) checks for queue status + * and accounts for it. In case the queue is full, the message is dropped and + * a break issued. + * + * Otherwise, message is treated as internal and allowed to go regardless of + * queue status. + * + * @param cls Closure (@c type dependant). It will be used by queue_send to + * build the message to be sent if not already prebuilt. + * @param type Type of the message, 0 for a raw message. + * @param size Size of the message. + * @param dst Neighbor to send message to. + * @param t Tunnel this message belongs to. + */ +static void +queue_add (void *cls, uint16_t type, size_t size, + struct MeshPeerInfo *dst, struct MeshTunnel *t); + + +/** + * Free a transmission that was already queued with all resources + * associated to the request. + * + * @param queue Queue handler to cancel. + * @param clear_cls Is it necessary to free associated cls? + */ +static void +queue_destroy (struct MeshPeerQueue *queue, int clear_cls); + + +/** + * @brief Get the next transmittable message from the queue. + * + * This will be the head, except in the case of being a data packet + * not allowed by the destination peer. + * + * @param peer Destination peer. + * + * @return The next viable MeshPeerQueue element to send to that peer. + * NULL when there are no transmittable messages. + */ +struct MeshPeerQueue * +queue_get_next (const struct MeshPeerInfo *peer); + + +/** + * Core callback to write a queued packet to core buffer + * + * @param cls Closure (peer info). + * @param size Number of bytes available in buf. + * @param buf Where the to write the message. + * + * @return number of bytes written to buf + */ +static size_t +queue_send (void *cls, size_t size, void *buf); /******************************************************************************/ -/************************ ITERATORS ****************************/ +/************************ REGEX INTEGRATION ****************************/ /******************************************************************************/ -/* FIXME move iterators here */ +/** + * Cancel a mesh regex search and free resources. + */ +static void +regex_cancel_search (struct MeshRegexSearchInfo *regex_search) +{ + DEBUG_REGEX ("Search for %s canelled.\n", regex_search->description); + GNUNET_REGEX_search_cancel (regex_search->search_handle); + if (0 < regex_search->n_peers) + GNUNET_free (regex_search->peers); + if (GNUNET_SCHEDULER_NO_TASK != regex_search->timeout) + { + GNUNET_SCHEDULER_cancel(regex_search->timeout); + } + GNUNET_free (regex_search); +} + + +/** + * Function called if the connect attempt to a peer found via + * connect_by_string times out. Try to connect to another peer, if any. + * Otherwise try to reconnect to the same peer. + * + * @param cls Closure (info about regex search). + * @param tc TaskContext. + */ +static void +regex_connect_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct MeshRegexSearchInfo *info = cls; + struct MeshPeerInfo *peer_info; + GNUNET_PEER_Id id; + GNUNET_PEER_Id old; + + DEBUG_REGEX ("Regex connect timeout\n"); + info->timeout = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + { + DEBUG_REGEX (" due to shutdown\n"); + return; + } + + old = info->peer; + DEBUG_REGEX (" timed out: %u\n", old); + + if (0 < info->n_peers) + { + // Select next peer, put current in that spot. + id = info->peers[info->i_peer]; + info->peers[info->i_peer] = info->peer; + info->i_peer = (info->i_peer + 1) % info->n_peers; + } + else + { + // Try to connect to same peer again. + id = info->peer; + } + DEBUG_REGEX (" trying: %u\n", id); + + peer_info = peer_info_get_short(id); + tunnel_add_peer (info->t, peer_info); + if (old != id) + tunnel_delete_peer (info->t, old); + peer_info_connect (peer_info, info->t); + info->timeout = GNUNET_SCHEDULER_add_delayed (connect_timeout, + ®ex_connect_timeout, + info); + DEBUG_REGEX ("Regex connect timeout END\n"); +} + + +/** + * Function to process DHT string to regex matching. + * Called on each result obtained for the DHT search. + * + * @param cls Closure provided in GNUNET_REGEX_search. + * @param id Peer providing a regex that matches the string. + * @param get_path Path of the get request. + * @param get_path_length Lenght of get_path. + * @param put_path Path of the put request. + * @param put_path_length Length of the put_path. + */ +static void +regex_found_handler (void *cls, + const struct GNUNET_PeerIdentity *id, + const struct GNUNET_PeerIdentity *get_path, + unsigned int get_path_length, + const struct GNUNET_PeerIdentity *put_path, + unsigned int put_path_length) +{ + struct MeshRegexSearchInfo *info = cls; + struct MeshPeerPath *p; + struct MeshPeerInfo *peer_info; + + DEBUG_REGEX ("Got regex results from DHT!\n"); + DEBUG_REGEX (" for %s\n", info->description); + + peer_info = peer_info_get (id); + p = path_build_from_dht (get_path, get_path_length, + put_path, put_path_length); + path_add_to_peers (p, GNUNET_NO); + path_destroy(p); + + tunnel_add_peer (info->t, peer_info); + peer_info_connect (peer_info, info->t); + if (0 == info->peer) + { + info->peer = peer_info->id; + } + else + { + GNUNET_array_append (info->peers, info->n_peers, peer_info->id); + } + + if (GNUNET_SCHEDULER_NO_TASK != info->timeout) + return; + + info->timeout = GNUNET_SCHEDULER_add_delayed (connect_timeout, + ®ex_connect_timeout, + info); + + return; +} + + +/** + * Store the regular expression describing a local service into the DHT. + * + * @param regex The regular expresion. + */ +static void +regex_put (struct MeshRegexDescriptor *regex) +{ + DEBUG_REGEX (" regex_put (%s) start\n", regex->regex); + if (NULL == regex->h) + { + DEBUG_REGEX (" first put, creating DFA\n"); + regex->h = GNUNET_REGEX_announce (dht_handle, + &my_full_id, + regex->regex, + regex->compression, + stats); + } + else + { + DEBUG_REGEX (" not first put, using cached data\n"); + GNUNET_REGEX_reannounce (regex->h); + } + DEBUG_REGEX (" regex_put (%s) end\n", regex->regex); +} + + +/** + * Periodically announce what applications are provided by local clients + * (by regex) + * + * @param cls closure + * @param tc task context + */ +static void +regex_announce (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct MeshClient *c = cls; + unsigned int i; + + c->regex_announce_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + DEBUG_REGEX ("Starting announce for regex\n"); + for (i = 0; i < c->n_regex; i++) + regex_put (&c->regexes[i]); + c->regex_announce_task = GNUNET_SCHEDULER_add_delayed (app_announce_time, + ®ex_announce, + cls); + DEBUG_REGEX ("Finished announce for regex\n"); +} /******************************************************************************/ @@ -623,24 +1449,39 @@ unsigned int next_client_id; * GNUNET_NO if not. */ static int -announce_application (void *cls, const GNUNET_HashCode * key, void *value) -{ - /* FIXME are hashes in multihash map equal on all aquitectures? */ - /* FIXME: keep return value of 'put' to possibly cancel!? */ - GNUNET_DHT_put (dht_handle, key, 10, - GNUNET_DHT_RO_RECORD_ROUTE | - GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, GNUNET_BLOCK_TYPE_TEST, - sizeof (struct GNUNET_PeerIdentity), - (const char *) &my_full_id, - GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), - APP_ANNOUNCE_TIME), - APP_ANNOUNCE_TIME, NULL, NULL); +announce_application (void *cls, const struct GNUNET_HashCode * key, void *value) +{ + struct PBlock block; + struct MeshClient *c; + + block.id = my_full_id; + c = GNUNET_CONTAINER_multihashmap_get (applications, key); + GNUNET_assert(NULL != c); + block.type = (long) GNUNET_CONTAINER_multihashmap_get (c->apps, key); + if (0 == block.type) + { + GNUNET_break(0); + return GNUNET_YES; + } + block.type = htonl (block.type); + + GNUNET_break (NULL != + GNUNET_DHT_put (dht_handle, key, + dht_replication_level, + GNUNET_DHT_RO_RECORD_ROUTE | + GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, + GNUNET_BLOCK_TYPE_MESH_PEER_BY_TYPE, + sizeof (block), + (const char *) &block, + GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_HOURS), /* FIXME: this should be an option */ + app_announce_time, NULL, NULL)); return GNUNET_OK; } /** * Periodically announce what applications are provided by local clients + * (by type) * * @param cls closure * @param tc task context @@ -648,7 +1489,7 @@ announce_application (void *cls, const GNUNET_HashCode * key, void *value) static void announce_applications (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { - if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN) + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) { announce_applications_task = GNUNET_SCHEDULER_NO_TASK; return; @@ -659,11 +1500,9 @@ announce_applications (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) GNUNET_CONTAINER_multihashmap_iterate (applications, &announce_application, NULL); announce_applications_task = - GNUNET_SCHEDULER_add_delayed (APP_ANNOUNCE_TIME, &announce_applications, + GNUNET_SCHEDULER_add_delayed (app_announce_time, &announce_applications, cls); DEBUG_DHT ("Finished PUT for apps\n"); - - return; } @@ -676,7 +1515,9 @@ announce_applications (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) static void announce_id (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { - if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN) + struct PBlock block; + + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) { announce_id_task = GNUNET_SCHEDULER_NO_TASK; return; @@ -687,83 +1528,44 @@ announce_id (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) */ DEBUG_DHT ("DHT_put for ID %s started.\n", GNUNET_i2s (&my_full_id)); + block.id = my_full_id; + block.type = htonl (0); GNUNET_DHT_put (dht_handle, /* DHT handle */ &my_full_id.hashPubKey, /* Key to use */ - 10, /* Replication level */ + dht_replication_level, /* Replication level */ GNUNET_DHT_RO_RECORD_ROUTE | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, /* DHT options */ - GNUNET_BLOCK_TYPE_TEST, /* Block type */ - sizeof (my_full_id), /* Size of the data */ - (char *) &my_full_id, /* Data itself */ + GNUNET_BLOCK_TYPE_MESH_PEER, /* Block type */ + sizeof (block), /* Size of the data */ + (const char *) &block, /* Data itself */ GNUNET_TIME_UNIT_FOREVER_ABS, /* Data expiration */ GNUNET_TIME_UNIT_FOREVER_REL, /* Retry time */ NULL, /* Continuation */ NULL); /* Continuation closure */ announce_id_task = - GNUNET_SCHEDULER_add_delayed (ID_ANNOUNCE_TIME, &announce_id, cls); + GNUNET_SCHEDULER_add_delayed (id_announce_time, &announce_id, cls); } -/** - * Function to process paths received for a new peer addition. The recorded - * paths form the initial tunnel, which can be optimized later. - * Called on each result obtained for the DHT search. - * - * @param cls closure - * @param exp when will this value expire - * @param key key of the result - * @param type type of the result - * @param size number of bytes in data - * @param data pointer to the result data - */ -static void -dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp, - const GNUNET_HashCode * key, - const struct GNUNET_PeerIdentity *get_path, - unsigned int get_path_length, - const struct GNUNET_PeerIdentity *put_path, - unsigned int put_path_length, enum GNUNET_BLOCK_Type type, - size_t size, const void *data); - - /******************************************************************************/ /****************** GENERAL HELPER FUNCTIONS ************************/ /******************************************************************************/ /** - * Search for a tunnel by global ID using full PeerIdentities - * - * @param oid owner of the tunnel - * @param tid global tunnel number + * Decrements the reference counter and frees all resources if needed * - * @return tunnel handler, NULL if doesn't exist - */ -static struct MeshTunnel * -tunnel_get (struct GNUNET_PeerIdentity *oid, MESH_TunnelNumber tid); - - -/** - * Delete an active client from the tunnel. - * - * @param t Tunnel. - * @param c Client. + * @param mesh_data Data Descriptor used in a multicast message. + * Freed no longer needed (last message). */ static void -tunnel_delete_active_client (struct MeshTunnel *t, const struct MeshClient *c); - -/** - * Notify a tunnel that a connection has broken that affects at least - * some of its peers. - * - * @param t Tunnel affected. - * @param p1 Peer that got disconnected from p2. - * @param p2 Peer that got disconnected from p1. - * - * @return Short ID of the peer disconnected (either p1 or p2). - * 0 if the tunnel remained unaffected. - */ -static GNUNET_PEER_Id -tunnel_notify_connection_broken (struct MeshTunnel *t, GNUNET_PEER_Id p1, - GNUNET_PEER_Id p2); +data_descriptor_decrement_rc (struct MeshData *mesh_data) +{ + if (0 == --(mesh_data->reference_counter)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Last copy!\n"); + GNUNET_free (mesh_data->data); + GNUNET_free (mesh_data); + } +} /** @@ -796,13 +1598,19 @@ client_get (struct GNUNET_SERVER_Client *client) * @param c Client to check * * @return GNUNET_YES or GNUNET_NO, depending on subscription status - * - * TODO inline? + * + * FIXME: use of crypto_hash slows it down + * The hash function alone takes 8-10us out of the ~55us for the whole + * process of retransmitting the message from one local client to another. + * Find faster implementation! */ static int client_is_subscribed (uint16_t message_type, struct MeshClient *c) { - GNUNET_HashCode hc; + struct GNUNET_HashCode hc; + + if (NULL == c->types) + return GNUNET_NO; GNUNET_CRYPTO_hash (&message_type, sizeof (uint16_t), &hc); return GNUNET_CONTAINER_multihashmap_contains (c->types, &hc); @@ -810,32 +1618,6 @@ client_is_subscribed (uint16_t message_type, struct MeshClient *c) /** - * Allow a client to send more data after transmitting a multicast message - * which some neighbor has not yet accepted altough a reasonable time has - * passed. - * - * @param cls Closure (DataDescriptor containing the task identifier) - * @param tc Task Context - * - * FIXME reference counter cshould be just int - */ -static void -client_allow_send (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct MeshData *mdata = cls; - - if (GNUNET_SCHEDULER_REASON_SHUTDOWN == tc->reason) - return; - GNUNET_assert (NULL != mdata->reference_counter); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "CLIENT ALLOW SEND DESPITE %u COPIES PENDING\n", - *(mdata->reference_counter)); - *(mdata->task) = GNUNET_SCHEDULER_NO_TASK; - GNUNET_SERVER_receive_done (mdata->t->owner->handle, GNUNET_OK); -} - - -/** * Check whether client wants traffic from a tunnel. * * @param c Client to check. @@ -891,9 +1673,9 @@ client_knows_tunnel (struct MeshClient *c, struct MeshTunnel *t) static void client_ignore_tunnel (struct MeshClient *c, struct MeshTunnel *t) { - GNUNET_HashCode hash; + struct GNUNET_HashCode hash; - GNUNET_CRYPTO_hash(&t->local_tid_dest, sizeof (MESH_TunnelNumber), &hash); + GNUNET_CRYPTO_hash (&t->local_tid_dest, sizeof (MESH_TunnelNumber), &hash); GNUNET_break (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (c->incoming_tunnels, &hash, t)); @@ -915,7 +1697,7 @@ client_ignore_tunnel (struct MeshClient *c, struct MeshTunnel *t) static void client_delete_tunnel (struct MeshClient *c, struct MeshTunnel *t) { - GNUNET_HashCode hash; + struct GNUNET_HashCode hash; if (c == t->owner) { @@ -938,7 +1720,32 @@ client_delete_tunnel (struct MeshClient *c, struct MeshTunnel *t) &hash, t)); } - +} + + +/** + * Notify the owner of a tunnel that a peer has disconnected. + * + * @param c Client (owner of tunnel). + * @param t Tunnel this message is about. + * @param peer_id Short ID of the disconnected peer. + */ +void +client_notify_peer_disconnected (struct MeshClient *c, + struct MeshTunnel *t, + GNUNET_PEER_Id peer_id) +{ + struct GNUNET_MESH_PeerControl msg; + + if (NULL == t->owner || NULL == nc) + return; + + msg.header.size = htons (sizeof (msg)); + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_DEL); + msg.tunnel_id = htonl (t->local_tid); + GNUNET_PEER_resolve (peer_id, &msg.peer); + GNUNET_SERVER_notification_context_unicast (nc, t->owner->handle, + &msg.header, GNUNET_NO); } @@ -947,15 +1754,16 @@ client_delete_tunnel (struct MeshClient *c, struct MeshTunnel *t) * * @param msg Pointer to the message itself * @param payload Pointer to the payload of the message. + * @param t The tunnel to whose clients this message goes. + * * @return number of clients this message was sent to */ static unsigned int send_subscribed_clients (const struct GNUNET_MessageHeader *msg, - const struct GNUNET_MessageHeader *payload) + const struct GNUNET_MessageHeader *payload, + struct MeshTunnel *t) { - struct GNUNET_PeerIdentity *oid; struct MeshClient *c; - struct MeshTunnel *t; MESH_TunnelNumber *tid; unsigned int count; uint16_t type; @@ -963,7 +1771,8 @@ send_subscribed_clients (const struct GNUNET_MessageHeader *msg, type = ntohs (payload->type); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending to clients...\n"); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "message of type %u\n", type); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "message of type %s\n", + GNUNET_MESH_DEBUG_M2S (type)); memcpy (cbuf, msg, sizeof (cbuf)); switch (htons (msg->type)) @@ -972,30 +1781,21 @@ send_subscribed_clients (const struct GNUNET_MessageHeader *msg, struct GNUNET_MESH_Multicast *mc; struct GNUNET_MESH_ToOrigin *to; - case GNUNET_MESSAGE_TYPE_MESH_UNICAST: - uc = (struct GNUNET_MESH_Unicast *) cbuf; - tid = &uc->tid; - oid = &uc->oid; - break; - case GNUNET_MESSAGE_TYPE_MESH_MULTICAST: - mc = (struct GNUNET_MESH_Multicast *) cbuf; - tid = &mc->tid; - oid = &mc->oid; - break; - case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: - to = (struct GNUNET_MESH_ToOrigin *) cbuf; - tid = &to->tid; - oid = &to->oid; - break; - default: - GNUNET_break (0); - return 0; - } - t = tunnel_get (oid, ntohl (*tid)); - if (NULL == t) - { - GNUNET_break (0); - return 0; + case GNUNET_MESSAGE_TYPE_MESH_UNICAST: + uc = (struct GNUNET_MESH_Unicast *) cbuf; + tid = &uc->tid; + break; + case GNUNET_MESSAGE_TYPE_MESH_MULTICAST: + mc = (struct GNUNET_MESH_Multicast *) cbuf; + tid = &mc->tid; + break; + case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: + to = (struct GNUNET_MESH_ToOrigin *) cbuf; + tid = &to->tid; + break; + default: + GNUNET_break (0); + return 0; } for (count = 0, c = clients; c != NULL; c = c->next) @@ -1015,15 +1815,21 @@ send_subscribed_clients (const struct GNUNET_MessageHeader *msg, { /* This client doesn't know the tunnel */ struct GNUNET_MESH_TunnelNotification tmsg; - GNUNET_HashCode hash; + struct GNUNET_HashCode hash; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " sending tunnel create\n"); tmsg.header.size = htons (sizeof (tmsg)); tmsg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_CREATE); GNUNET_PEER_resolve (t->id.oid, &tmsg.peer); tmsg.tunnel_id = htonl (t->local_tid_dest); + tmsg.opt = 0; + if (GNUNET_YES == t->speed_min) + tmsg.opt |= MESH_TUNNEL_OPT_SPEED_MIN; + if (GNUNET_YES == t->nobuffer) + tmsg.opt |= MESH_TUNNEL_OPT_NOBUFFER; GNUNET_SERVER_notification_context_unicast (nc, c->handle, &tmsg.header, GNUNET_NO); - GNUNET_array_append (t->clients, t->nclients, c); + tunnel_add_client (t, c); GNUNET_CRYPTO_hash (&t->local_tid_dest, sizeof (MESH_TunnelNumber), &hash); GNUNET_break (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put ( @@ -1040,9 +1846,10 @@ send_subscribed_clients (const struct GNUNET_MessageHeader *msg, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " sending\n"); GNUNET_SERVER_notification_context_unicast (nc, c->handle, (struct GNUNET_MessageHeader - *) cbuf, GNUNET_YES); + *) cbuf, GNUNET_NO); } } + return count; } @@ -1059,6 +1866,9 @@ send_client_peer_connected (const struct MeshTunnel *t, const GNUNET_PEER_Id id) { struct GNUNET_MESH_PeerControl pc; + if (NULL == t->owner || GNUNET_YES == t->destroy) + return; + pc.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_ADD); pc.header.size = htons (sizeof (struct GNUNET_MESH_PeerControl)); pc.tunnel_id = htonl (t->local_tid); @@ -1129,148 +1939,22 @@ send_client_tunnel_disconnect (struct MeshTunnel *t, struct MeshClient *c) /** - * Function called to notify a client about the socket - * being ready to queue more data. "buf" will be - * NULL and "size" zero if the socket was closed for - * writing in the meantime. - * - * @param cls closure - * @param size number of bytes available in buf - * @param buf where the callee should write the message - * @return number of bytes written to buf - */ -static size_t -send_core_create_path (void *cls, size_t size, void *buf); - - -/** - * Function called to notify a client about the socket - * being ready to queue more data. "buf" will be - * NULL and "size" zero if the socket was closed for - * writing in the meantime. - * - * @param cls closure (data itself) - * @param size number of bytes available in buf - * @param buf where the callee should write the message - * - * @return number of bytes written to buf - */ -static size_t -send_core_data_multicast (void *cls, size_t size, void *buf); - - -/** - * Decrements the reference counter and frees all resources if needed - * - * @param mesh_data Data Descriptor used in a multicast message. - * Freed no longer needed (last message). - */ -static void -data_descriptor_decrement_multicast (struct MeshData *mesh_data) -{ - /* Make sure it's a multicast packet */ - GNUNET_assert (NULL != mesh_data->reference_counter); - - if (0 == --(*(mesh_data->reference_counter))) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Last copy!\n"); - if (NULL != mesh_data->task) - { - if (GNUNET_SCHEDULER_NO_TASK != *(mesh_data->task)) - { - GNUNET_SCHEDULER_cancel (*(mesh_data->task)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " notifying client...\n"); - GNUNET_SERVER_receive_done (mesh_data->t->owner->handle, GNUNET_OK); - } - GNUNET_free (mesh_data->task); - } - GNUNET_free (mesh_data->reference_counter); - GNUNET_free (mesh_data->data); - GNUNET_free (mesh_data); - } -} - - -/** - * Cancel a core transmission that was already requested and free all resources - * associated to the request. + * Iterator over all the peers to remove the oldest not-used entry. * - * @param peer PeeInfo of the peer whose transmission is cancelled. - * @param i Position of the transmission to be cancelled. - */ -static void -peer_info_cancel_transmission (struct MeshPeerInfo *peer, unsigned int i) -{ - if (NULL != peer->core_transmit[i]) - { - struct MeshTransmissionDescriptor *dd; - struct MeshPathInfo *path_info; - -#if MESH_DEBUG - { - struct GNUNET_PeerIdentity id; - - GNUNET_PEER_resolve (peer->id, &id); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - " Cancelling data transmission at %s [%u]\n", - GNUNET_i2s (&id), i); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " message type %u\n", - peer->types[i]); - } -#endif - /* TODO: notify that tranmission has failed */ - switch (peer->types[i]) - { - case GNUNET_MESSAGE_TYPE_MESH_MULTICAST: - case GNUNET_MESSAGE_TYPE_MESH_UNICAST: - case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " type payload\n"); - dd = peer->infos[i]; - data_descriptor_decrement_multicast (dd->mesh_data); - break; - case GNUNET_MESSAGE_TYPE_MESH_PATH_CREATE: - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " type create path\n"); - path_info = peer->infos[i]; - path_destroy (path_info->path); - break; - default: - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " type unknown!\n"); - } - GNUNET_CORE_notify_transmit_ready_cancel (peer->core_transmit[i]); - peer->core_transmit[i] = NULL; - GNUNET_free (peer->infos[i]); - } -} - - -/** - * Get a unused CORE slot to transmit a message to a peer. If all the slots - * are used, cancel one and return it's position. + * @param cls Closure (unsued). + * @param key ID of the peer. + * @param value Peer_Info of the peer. * - * @param peer PeerInfo of the neighbor we want to transmit to. - * - * @return The index of an available slot to transmit to the neighbor. + * FIXME implement */ -static unsigned int -peer_info_transmit_slot (struct MeshPeerInfo *peer) +static int +peer_info_timeout (void *cls, + const struct GNUNET_HashCode *key, + void *value) { - unsigned int i; - - for (i = 0; peer->core_transmit[i]; i++) - { - if (i == (CORE_QUEUE_SIZE - 1)) - { - /* All positions are taken! Overwriting! */ - GNUNET_break (0); - peer_info_cancel_transmission (peer, 0); - return 0; - } - } - return i; + return GNUNET_YES; } - /** * Retrieve the MeshPeerInfo stucture associated with the peer, create one * and insert it in the appropiate structures if the peer is not known yet. @@ -1289,10 +1973,17 @@ peer_info_get (const struct GNUNET_PeerIdentity *peer) { peer_info = (struct MeshPeerInfo *) GNUNET_malloc (sizeof (struct MeshPeerInfo)); + if (GNUNET_CONTAINER_multihashmap_size (peers) > max_peers) + { + GNUNET_CONTAINER_multihashmap_iterate (peers, + &peer_info_timeout, + NULL); + } GNUNET_CONTAINER_multihashmap_put (peers, &peer->hashPubKey, peer_info, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); peer_info->id = GNUNET_PEER_intern (peer); } + peer_info->last_contact = GNUNET_TIME_absolute_get(); return peer_info; } @@ -1327,7 +2018,7 @@ peer_info_get_short (const GNUNET_PEER_Id peer) * @return always GNUNET_YES, to keep iterating */ static int -peer_info_delete_tunnel (void *cls, const GNUNET_HashCode * key, void *value) +peer_info_delete_tunnel (void *cls, const struct GNUNET_HashCode * key, void *value) { struct MeshTunnel *t = cls; struct MeshPeerInfo *peer = value; @@ -1349,7 +2040,7 @@ peer_info_delete_tunnel (void *cls, const GNUNET_HashCode * key, void *value) /** - * Core callback to write a + * Core callback to write a pre-constructed data packet to core buffer * * @param cls Closure (MeshTransmissionDescriptor with data in "data" member). * @param size Number of bytes available in buf. @@ -1371,76 +2062,111 @@ send_core_data_raw (void *cls, size_t size, void *buf) if (total_size > size) { - struct GNUNET_PeerIdentity id; - - GNUNET_PEER_resolve (info->peer->id, &id); - info->peer->core_transmit[info->handler_n] = - GNUNET_CORE_notify_transmit_ready (core_handle, 0, 100, - GNUNET_TIME_UNIT_FOREVER_REL, &id, - size, &send_core_data_raw, info); + GNUNET_break (0); return 0; } - info->peer->core_transmit[info->handler_n] = NULL; memcpy (buf, msg, total_size); - GNUNET_free (info->mesh_data); + data_descriptor_decrement_rc (info->mesh_data); GNUNET_free (info); return total_size; } /** - * Sends an already built message to a peer, properly registrating - * all used resources. + * Sends an already built non-multicast message to a peer, + * properly registrating all used resources. * - * @param message Message to send. Fucntion makes a copy of it. + * @param message Message to send. Function makes a copy of it. * @param peer Short ID of the neighbor whom to send the message. - * - * FIXME tunnel? + * @param t Tunnel on which this message is transmitted. */ static void -send_message (const struct GNUNET_MessageHeader *message, - const struct GNUNET_PeerIdentity *peer) +send_prebuilt_message (const struct GNUNET_MessageHeader *message, + const struct GNUNET_PeerIdentity *peer, + struct MeshTunnel *t) { struct MeshTransmissionDescriptor *info; struct MeshPeerInfo *neighbor; struct MeshPeerPath *p; - unsigned int i; size_t size; + uint16_t type; -// GNUNET_TRANSPORT_try_connect(); +// GNUNET_TRANSPORT_try_connect(); FIXME use? size = ntohs (message->size); info = GNUNET_malloc (sizeof (struct MeshTransmissionDescriptor)); info->mesh_data = GNUNET_malloc (sizeof (struct MeshData)); info->mesh_data->data = GNUNET_malloc (size); memcpy (info->mesh_data->data, message, size); + type = ntohs(message->type); + switch (type) + { + struct GNUNET_MESH_Unicast *m; + struct GNUNET_MESH_ToOrigin *to; + + case GNUNET_MESSAGE_TYPE_MESH_UNICAST: + m = (struct GNUNET_MESH_Unicast *) info->mesh_data->data; + m->ttl = htonl (ntohl (m->ttl) - 1); + break; + case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: + to = (struct GNUNET_MESH_ToOrigin *) info->mesh_data->data; + t->bck_pid++; + to->pid = htonl(t->bck_pid); + } info->mesh_data->data_len = size; + info->mesh_data->reference_counter = 1; + info->mesh_data->total_out = 1; neighbor = peer_info_get (peer); for (p = neighbor->path_head; NULL != p; p = p->next) { - if (2 == p->length) + if (2 >= p->length) { break; } } if (NULL == p) { - GNUNET_break (0); +#if MESH_DEBUG + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " %s IS NOT DIRECTLY CONNECTED\n", + GNUNET_i2s(peer)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " PATHS TO %s:\n", + GNUNET_i2s(peer)); + for (p = neighbor->path_head; NULL != p; p = p->next) + { + struct GNUNET_PeerIdentity debug_id; + unsigned int i; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " path with %u hops through:\n", + p->length); + for (i = 0; i < p->length; i++) + { + GNUNET_PEER_resolve(p->peers[i], &debug_id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " hop %u: %s\n", + i, GNUNET_i2s(&debug_id)); + } + } +#endif + GNUNET_break (0); // FIXME sometimes fails (testing disconnect?) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + " no direct connection to %s\n", + GNUNET_i2s (peer)); GNUNET_free (info->mesh_data->data); GNUNET_free (info->mesh_data); GNUNET_free (info); return; } - i = peer_info_transmit_slot (neighbor); - info->handler_n = i; info->peer = neighbor; - neighbor->types[i] = GNUNET_MESSAGE_TYPE_MESH_UNICAST; - neighbor->infos[i] = info; - neighbor->core_transmit[i] = - GNUNET_CORE_notify_transmit_ready (core_handle, 0, 100, - GNUNET_TIME_UNIT_FOREVER_REL, peer, - size, &send_core_data_raw, info); - + if (GNUNET_MESSAGE_TYPE_MESH_PATH_ACK == type) + type = 0; + queue_add (info, + type, + size, + neighbor, + t); } @@ -1459,6 +2185,7 @@ send_create_path (struct MeshPeerInfo *peer, struct MeshPeerPath *p, struct GNUNET_PeerIdentity id; struct MeshPathInfo *path_info; struct MeshPeerInfo *neighbor; + unsigned int i; if (NULL == p) @@ -1488,19 +2215,12 @@ send_create_path (struct MeshPeerInfo *peer, struct MeshPeerPath *p, path_info->t = t; neighbor = peer_info_get (&id); path_info->peer = neighbor; - path_info->pos = peer_info_transmit_slot (neighbor); - neighbor->types[path_info->pos] = GNUNET_MESSAGE_TYPE_MESH_PATH_CREATE; - neighbor->infos[path_info->pos] = path_info; - neighbor->core_transmit[path_info->pos] = - GNUNET_CORE_notify_transmit_ready (core_handle, /* handle */ - 0, /* cork */ - 0, /* priority */ - GNUNET_TIME_UNIT_FOREVER_REL, /* timeout */ - &id, /* target */ - sizeof (struct GNUNET_MESH_ManipulatePath) + - (p->length * sizeof (struct GNUNET_PeerIdentity)), /*size */ - &send_core_create_path, /* callback */ - path_info); /* cls */ + queue_add (path_info, + GNUNET_MESSAGE_TYPE_MESH_PATH_CREATE, + sizeof (struct GNUNET_MESH_ManipulatePath) + + (p->length * sizeof (struct GNUNET_PeerIdentity)), + neighbor, + t); } @@ -1539,13 +2259,40 @@ send_destroy_path (struct MeshTunnel *t, GNUNET_PEER_Id destination) { GNUNET_PEER_resolve (p->peers[i], &pi[i]); } - send_message (&msg->header, tree_get_first_hop (t->tree, destination)); + send_prebuilt_message (&msg->header, tree_get_first_hop (t->tree, destination), t); } path_destroy (p); } /** + * Sends a PATH ACK message in reponse to a received PATH_CREATE directed to us. + * + * @param t Tunnel which to confirm. + */ +static void +send_path_ack (struct MeshTunnel *t) +{ + struct MeshTransmissionDescriptor *info; + struct GNUNET_PeerIdentity id; + GNUNET_PEER_Id peer; + + peer = tree_get_predecessor (t->tree); + GNUNET_PEER_resolve (peer, &id); + info = GNUNET_malloc (sizeof (struct MeshTransmissionDescriptor)); + info->origin = &t->id; + info->peer = GNUNET_CONTAINER_multihashmap_get (peers, &id.hashPubKey); + GNUNET_assert (NULL != info->peer); + + queue_add (info, + GNUNET_MESSAGE_TYPE_MESH_PATH_ACK, + sizeof (struct GNUNET_MESH_PathACK), + info->peer, + t); +} + + +/** * Try to establish a new connection to this peer. * Use the best path for the given tunnel. * If the peer doesn't have any path to it yet, try to get one. @@ -1576,7 +2323,7 @@ peer_info_connect (struct MeshPeerInfo *peer, struct MeshTunnel *t) } else { - GNUNET_HashCode hash; + struct GNUNET_HashCode hash; path_destroy (p); send_client_peer_connected (t, myid); @@ -1604,11 +2351,13 @@ peer_info_connect (struct MeshPeerInfo *peer, struct MeshTunnel *t) " Starting DHT GET for peer %s\n", GNUNET_i2s (&id)); peer->dhtgetcls = path_info; peer->dhtget = GNUNET_DHT_get_start (dht_handle, /* handle */ - GNUNET_BLOCK_TYPE_TEST, /* type */ - &id.hashPubKey, /* key to search */ - 10, /* replication level */ - GNUNET_DHT_RO_RECORD_ROUTE | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, NULL, /* xquery */ - 0, /* xquery bits */ + GNUNET_BLOCK_TYPE_MESH_PEER, /* type */ + &id.hashPubKey, /* key to search */ + dht_replication_level, /* replication level */ + GNUNET_DHT_RO_RECORD_ROUTE | + GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, + NULL, /* xquery */ // FIXME BLOOMFILTER + 0, /* xquery bits */ // FIXME BLOOMFILTER SIZE &dht_get_id_handler, path_info); } /* Otherwise, there is no path but the DHT get is already started. */ @@ -1628,6 +2377,8 @@ peer_info_connect_task (void *cls, { struct MeshPathInfo *path_info = cls; + path_info->peer->connect_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) { GNUNET_free (cls); @@ -1651,7 +2402,6 @@ peer_info_destroy (struct MeshPeerInfo *pi) struct GNUNET_PeerIdentity id; struct MeshPeerPath *p; struct MeshPeerPath *nextp; - unsigned int i; GNUNET_PEER_resolve (pi->id, &id); GNUNET_PEER_change_rc (pi->id, -1); @@ -1668,10 +2418,6 @@ peer_info_destroy (struct MeshPeerInfo *pi) GNUNET_DHT_get_stop (pi->dhtget); GNUNET_free (pi->dhtgetcls); } - for (i = 0; i < CORE_QUEUE_SIZE; i++) - { - peer_info_cancel_transmission (pi, i); - } p = pi->path_head; while (NULL != p) { @@ -1680,6 +2426,10 @@ peer_info_destroy (struct MeshPeerInfo *pi) path_destroy (p); p = nextp; } + if (GNUNET_SCHEDULER_NO_TASK != pi->connect_task) + { + GNUNET_free (GNUNET_SCHEDULER_cancel (pi->connect_task)); + } GNUNET_free (pi); return GNUNET_OK; } @@ -1709,6 +2459,7 @@ peer_info_remove_path (struct MeshPeerInfo *peer, GNUNET_PEER_Id p1, unsigned int cost; unsigned int i; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "peer_info_remove_path\n"); destroyed = 0; p = peer->path_head; while (NULL != p) @@ -1769,6 +2520,7 @@ peer_info_remove_path (struct MeshPeerInfo *peer, GNUNET_PEER_Id p1, peer_info_connect (peer_d, peer->tunnels[i]); } } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "peer_info_remove_path END\n"); } @@ -1884,6 +2636,44 @@ peer_info_add_path_to_origin (struct MeshPeerInfo *peer_info, /** + * Function called if the connection to the peer has been stalled for a while, + * possibly due to a missed ACK. Poll the peer about its ACK status. + * + * @param cls Closure (cinfo). + * @param tc TaskContext. + */ +static void +tunnel_poll (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct MeshTunnelChildInfo *cinfo = cls; + struct GNUNET_MESH_Poll msg; + struct GNUNET_PeerIdentity id; + struct MeshTunnel *t; + + cinfo->fc_poll = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + { + return; + } + + t = cinfo->t; + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_POLL); + msg.header.size = htons (sizeof (msg)); + msg.tid = htonl (t->id.tid); + GNUNET_PEER_resolve (t->id.oid, &msg.oid); + msg.last_ack = htonl (cinfo->fwd_ack); + + GNUNET_PEER_resolve (cinfo->id, &id); + send_prebuilt_message (&msg.header, &id, cinfo->t); + cinfo->fc_poll_time = GNUNET_TIME_relative_min ( + MESH_MAX_POLL_TIME, + GNUNET_TIME_relative_multiply (cinfo->fc_poll_time, 2)); + cinfo->fc_poll = GNUNET_SCHEDULER_add_delayed (cinfo->fc_poll_time, + &tunnel_poll, cinfo); +} + + +/** * Build a PeerPath from the paths returned from the DHT, reversing the paths * to obtain a local peer -> destination path and interning the peer ids. * @@ -1974,7 +2764,7 @@ path_build_from_dht (const struct GNUNET_PeerIdentity *get_path, * Adds a path to the peer_infos of all the peers in the path * * @param p Path to process. - * @param confirmed Whether we know if the path works or not. FIXME use + * @param confirmed Whether we know if the path works or not. */ static void path_add_to_peers (struct MeshPeerPath *p, int confirmed) @@ -2018,7 +2808,7 @@ path_refresh (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); static struct MeshTunnel * tunnel_get_incoming (MESH_TunnelNumber tid) { - GNUNET_HashCode hash; + struct GNUNET_HashCode hash; GNUNET_assert (tid >= GNUNET_MESH_LOCAL_TUNNEL_ID_SERV); GNUNET_CRYPTO_hash (&tid, sizeof (MESH_TunnelNumber), &hash); @@ -2043,7 +2833,7 @@ tunnel_get_by_local_id (struct MeshClient *c, MESH_TunnelNumber tid) } else { - GNUNET_HashCode hash; + struct GNUNET_HashCode hash; GNUNET_CRYPTO_hash (&tid, sizeof (MESH_TunnelNumber), &hash); return GNUNET_CONTAINER_multihashmap_get (c->own_tunnels, &hash); @@ -2063,7 +2853,7 @@ static struct MeshTunnel * tunnel_get_by_pi (GNUNET_PEER_Id pi, MESH_TunnelNumber tid) { struct MESH_TunnelID id; - GNUNET_HashCode hash; + struct GNUNET_HashCode hash; id.oid = pi; id.tid = tid; @@ -2082,7 +2872,7 @@ tunnel_get_by_pi (GNUNET_PEER_Id pi, MESH_TunnelNumber tid) * @return tunnel handler, NULL if doesn't exist */ static struct MeshTunnel * -tunnel_get (struct GNUNET_PeerIdentity *oid, MESH_TunnelNumber tid) +tunnel_get (const struct GNUNET_PeerIdentity *oid, MESH_TunnelNumber tid) { return tunnel_get_by_pi (GNUNET_PEER_search (oid), tid); } @@ -2104,7 +2894,10 @@ tunnel_delete_active_client (struct MeshTunnel *t, const struct MeshClient *c) if (t->clients[i] == c) { t->clients[i] = t->clients[t->nclients - 1]; + t->clients_fc[i] = t->clients_fc[t->nclients - 1]; GNUNET_array_grow (t->clients, t->nclients, t->nclients - 1); + t->nclients++; + GNUNET_array_grow (t->clients_fc, t->nclients, t->nclients - 1); break; } } @@ -2150,6 +2943,51 @@ tunnel_delete_client (struct MeshTunnel *t, const struct MeshClient *c) /** + * @brief Iterator to destroy MeshTunnelChildInfo of tunnel children. + * + * Destroys queue elements of all waiting transmissions and frees all memory + * used by the struct and its elements. + * + * @param cls Closure (tunnel info). + * @param key Hash of GNUNET_PEER_Id (unused). + * @param value MeshTunnelChildInfo of the child. + * + * @return always GNUNET_YES, to keep iterating + */ +static int +tunnel_destroy_child (void *cls, + const struct GNUNET_HashCode * key, + void *value) +{ + struct MeshTunnelChildInfo *cinfo = value; + struct MeshTunnel *t = cls; + struct MeshPeerQueue *q; + unsigned int c; + unsigned int i; + + for (c = 0; c < cinfo->send_buffer_n; c++) + { + i = (cinfo->send_buffer_start + c) % t->fwd_queue_max; + q = cinfo->send_buffer[i]; + cinfo->send_buffer[i] = NULL; + if (NULL != q) + queue_destroy (q, GNUNET_YES); + else + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "%u %u\n", c, cinfo->send_buffer_n); + } + GNUNET_free_non_null (cinfo->send_buffer); + if (GNUNET_SCHEDULER_NO_TASK != cinfo->fc_poll) + { + GNUNET_SCHEDULER_cancel (cinfo->fc_poll); + cinfo->fc_poll = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_free (cinfo); + return GNUNET_YES; +} + + +/** * Callback used to notify a client owner of a tunnel that a peer has * disconnected, most likely because of a path change. * @@ -2157,28 +2995,20 @@ tunnel_delete_client (struct MeshTunnel *t, const struct MeshClient *c) * @param peer_id Short ID of disconnected peer. */ void -notify_peer_disconnected (void *cls, GNUNET_PEER_Id peer_id) +tunnel_notify_client_peer_disconnected (void *cls, GNUNET_PEER_Id peer_id) { struct MeshTunnel *t = cls; struct MeshPeerInfo *peer; struct MeshPathInfo *path_info; - if (NULL != t->owner && NULL != nc) - { - struct GNUNET_MESH_PeerControl msg; + client_notify_peer_disconnected (t->owner, t, peer_id); - msg.header.size = htons (sizeof (msg)); - msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_DEL); - msg.tunnel_id = htonl (t->local_tid); - GNUNET_PEER_resolve (peer_id, &msg.peer); - GNUNET_SERVER_notification_context_unicast (nc, t->owner->handle, - &msg.header, GNUNET_NO); - } peer = peer_info_get_short (peer_id); path_info = GNUNET_malloc (sizeof (struct MeshPathInfo)); path_info->peer = peer; path_info->t = t; - GNUNET_SCHEDULER_add_now (&peer_info_connect_task, path_info); + peer->connect_task = GNUNET_SCHEDULER_add_now (&peer_info_connect_task, + path_info); } @@ -2200,6 +3030,7 @@ tunnel_add_peer (struct MeshTunnel *t, struct MeshPeerInfo *peer) unsigned int best_cost; unsigned int cost; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tunnel_add_peer\n"); GNUNET_PEER_resolve (peer->id, &id); if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (t->peers, &id.hashPubKey)) @@ -2225,16 +3056,17 @@ tunnel_add_peer (struct MeshTunnel *t, struct MeshPeerInfo *peer) } p = p->next; } - tree_add_path (t->tree, best_p, ¬ify_peer_disconnected, t); + tree_add_path (t->tree, best_p, &tunnel_notify_client_peer_disconnected, t); if (GNUNET_SCHEDULER_NO_TASK == t->path_refresh_task) t->path_refresh_task = - GNUNET_SCHEDULER_add_delayed (REFRESH_PATH_TIME, &path_refresh, t); + GNUNET_SCHEDULER_add_delayed (refresh_path_time, &path_refresh, t); } else { /* Start a DHT get */ peer_info_connect (peer, t); } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tunnel_add_peer END\n"); } /** @@ -2253,6 +3085,7 @@ tunnel_add_path (struct MeshTunnel *t, struct MeshPeerPath *p, { struct GNUNET_PeerIdentity id; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tunnel_add_path\n"); GNUNET_assert (0 != own_pos); tree_add_path (t->tree, p, NULL, NULL); if (own_pos < p->length - 1) @@ -2260,6 +3093,27 @@ tunnel_add_path (struct MeshTunnel *t, struct MeshPeerPath *p, GNUNET_PEER_resolve (p->peers[own_pos + 1], &id); tree_update_first_hops (t->tree, myid, &id); } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tunnel_add_path END\n"); +} + +/** + * Add a client to a tunnel, initializing all needed data structures. + * + * @param t Tunnel to which add the client. + * @param c Client which to add to the tunnel. + */ +static void +tunnel_add_client (struct MeshTunnel *t, struct MeshClient *c) +{ + struct MeshTunnelClientInfo clinfo; + + GNUNET_array_append (t->clients, t->nclients, c); + clinfo.fwd_ack = t->fwd_pid + 1; + clinfo.bck_ack = t->nobuffer ? 1 : INITIAL_WINDOW_SIZE - 1; + clinfo.fwd_pid = t->fwd_pid; + clinfo.bck_pid = (uint32_t) -1; // Expected next: 0 + t->nclients--; + GNUNET_array_append (t->clients_fc, t->nclients, clinfo); } @@ -2283,7 +3137,8 @@ tunnel_notify_connection_broken (struct MeshTunnel *t, GNUNET_PEER_Id p1, GNUNET_PEER_Id pid; pid = - tree_notify_connection_broken (t->tree, p1, p2, ¬ify_peer_disconnected, + tree_notify_connection_broken (t->tree, p1, p2, + &tunnel_notify_client_peer_disconnected, t); if (myid != p1 && myid != p2) { @@ -2304,7 +3159,7 @@ tunnel_notify_connection_broken (struct MeshTunnel *t, GNUNET_PEER_Id p1, msg.peer1 = my_full_id; GNUNET_PEER_resolve (pid, &msg.peer2); GNUNET_PEER_resolve (tree_get_predecessor (t->tree), &neighbor); - send_message (&msg.header, &neighbor); + send_prebuilt_message (&msg.header, &neighbor, t); } } return pid; @@ -2323,49 +3178,45 @@ tunnel_send_multicast_iterator (void *cls, GNUNET_PEER_Id neighbor_id) struct MeshData *mdata = cls; struct MeshTransmissionDescriptor *info; struct GNUNET_PeerIdentity neighbor; - unsigned int i; + struct GNUNET_MessageHeader *msg; info = GNUNET_malloc (sizeof (struct MeshTransmissionDescriptor)); info->mesh_data = mdata; - (*(mdata->reference_counter)) ++; + (mdata->reference_counter) ++; info->destination = neighbor_id; GNUNET_PEER_resolve (neighbor_id, &neighbor); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " sending to %s...\n", GNUNET_i2s (&neighbor)); info->peer = peer_info_get (&neighbor); GNUNET_assert (NULL != info->peer); - i = peer_info_transmit_slot (info->peer); - info->handler_n = i; - info->peer->infos[i] = info; - info->peer->types[i] = GNUNET_MESSAGE_TYPE_MESH_MULTICAST; - info->peer->core_transmit[i] = - GNUNET_CORE_notify_transmit_ready (core_handle, 0, 0, - GNUNET_TIME_UNIT_FOREVER_REL, - &neighbor, info->mesh_data->data_len, - &send_core_data_multicast, info); + msg = (struct GNUNET_MessageHeader *) mdata->data; + queue_add(info, + ntohs (msg->type), + info->mesh_data->data_len, + info->peer, + mdata->t); } + /** - * Send a message in a tunnel in multicast, sending a copy to each child node + * Queue a message in a tunnel in multicast, sending a copy to each child node * down the local one in the tunnel tree. * * @param t Tunnel in which to send the data. * @param msg Message to be sent. - * @param internal Has the service generated this message? */ static void tunnel_send_multicast (struct MeshTunnel *t, - const struct GNUNET_MessageHeader *msg, - int internal) + const struct GNUNET_MessageHeader *msg) { struct MeshData *mdata; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " sending a multicast packet...\n"); + mdata = GNUNET_malloc (sizeof (struct MeshData)); mdata->data_len = ntohs (msg->size); - mdata->reference_counter = GNUNET_malloc (sizeof (unsigned int)); mdata->t = t; mdata->data = GNUNET_malloc (mdata->data_len); memcpy (mdata->data, msg, mdata->data_len); @@ -2374,6 +3225,21 @@ tunnel_send_multicast (struct MeshTunnel *t, struct GNUNET_MESH_Multicast *mcast; mcast = (struct GNUNET_MESH_Multicast *) mdata->data; + if (t->fwd_queue_n >= t->fwd_queue_max) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, " queue full!\n"); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + " message from %s!\n", + GNUNET_i2s(&mcast->oid)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + " message at %s!\n", + GNUNET_i2s(&my_full_id)); + GNUNET_free (mdata->data); + GNUNET_free (mdata); + return; + } + t->fwd_queue_n++; mcast->ttl = htonl (ntohl (mcast->ttl) - 1); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " data packet, ttl: %u\n", ntohl (mcast->ttl)); @@ -2382,30 +3248,20 @@ tunnel_send_multicast (struct MeshTunnel *t, { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " not a data packet, no ttl\n"); } - if (NULL != t->owner && GNUNET_YES != t->owner->shutting_down - && GNUNET_NO == internal) - { - mdata->task = GNUNET_malloc (sizeof (GNUNET_SCHEDULER_TaskIdentifier)); - (*(mdata->task)) = - GNUNET_SCHEDULER_add_delayed (UNACKNOWLEDGED_WAIT, &client_allow_send, - mdata); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "timeout task %u\n", - *(mdata->task)); - } tree_iterate_children (t->tree, &tunnel_send_multicast_iterator, mdata); - if (*(mdata->reference_counter) == 0) + if (mdata->reference_counter == 0) { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " no one to send data to\n"); GNUNET_free (mdata->data); - GNUNET_free (mdata->reference_counter); - if (NULL != mdata->task) - { - GNUNET_SCHEDULER_cancel(*(mdata->task)); - GNUNET_free (mdata->task); - GNUNET_SERVER_receive_done(t->owner->handle, GNUNET_OK); - } - // FIXME change order? GNUNET_free (mdata); + if (ntohs (msg->type) == GNUNET_MESSAGE_TYPE_MESH_MULTICAST) + t->fwd_queue_n--; + } + else + { + mdata->total_out = mdata->reference_counter; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " sending a multicast packet done\n"); @@ -2414,24 +3270,768 @@ tunnel_send_multicast (struct MeshTunnel *t, /** + * Increase the SKIP value of all peers that + * have not received a unicast message. + * + * @param cls Closure (ID of the peer that HAS received the message). + * @param key ID of the neighbor. + * @param value Information about the neighbor. + * + * @return GNUNET_YES to keep iterating. + */ +static int +tunnel_add_skip (void *cls, + const struct GNUNET_HashCode * key, + void *value) +{ + struct GNUNET_PeerIdentity *neighbor = cls; + struct MeshTunnelChildInfo *cinfo = value; + + /* TODO compare only pointers? key == neighbor? */ + if (0 == memcmp (&neighbor->hashPubKey, key, sizeof (struct GNUNET_HashCode))) + { + return GNUNET_YES; + } + cinfo->skip++; + return GNUNET_YES; +} + + +/** + * @brief Get neighbor's Flow Control information. + * + * Retrieves the MeshTunnelChildInfo containing Flow Control data about a direct + * descendant of the local node in a certain tunnel. + * If the info is not yet there (recently created path), creates the data struct + * and inserts it into the tunnel info, initialized to the current tunnel ACK + * values. + * + * @param t Tunnel related. + * @param peer Neighbor whose Flow Control info is needed. + * + * @return Neighbor's Flow Control info. + */ +static struct MeshTunnelChildInfo * +tunnel_get_neighbor_fc (struct MeshTunnel *t, + const struct GNUNET_PeerIdentity *peer) +{ + struct MeshTunnelChildInfo *cinfo; + + if (NULL == t->children_fc) + return NULL; + + cinfo = GNUNET_CONTAINER_multihashmap_get (t->children_fc, + &peer->hashPubKey); + if (NULL == cinfo) + { + uint32_t delta; + + cinfo = GNUNET_malloc (sizeof (struct MeshTunnelChildInfo)); + cinfo->id = GNUNET_PEER_intern (peer); + cinfo->skip = t->fwd_pid; + cinfo->t = t; + + delta = t->nobuffer ? 1 : INITIAL_WINDOW_SIZE; + cinfo->fwd_ack = t->fwd_pid + delta; + cinfo->bck_ack = delta; + cinfo->bck_pid = -1; + + cinfo->fc_poll = GNUNET_SCHEDULER_NO_TASK; + cinfo->fc_poll_time = GNUNET_TIME_UNIT_SECONDS; + + cinfo->send_buffer = + GNUNET_malloc (sizeof(struct MeshPeerQueue *) * t->fwd_queue_max); + + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (t->children_fc, + &peer->hashPubKey, + cinfo, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); + } + return cinfo; +} + + +/** + * Get the Flow Control info of a client. + * + * @param t Tunnel on which to look. + * @param c Client whose ACK to get. + * + * @return ACK value. + */ +static struct MeshTunnelClientInfo * +tunnel_get_client_fc (struct MeshTunnel *t, + struct MeshClient *c) +{ + unsigned int i; + + for (i = 0; i < t->nclients; i++) + { + if (t->clients[i] != c) + continue; + return &t->clients_fc[i]; + } + GNUNET_assert (0); + return NULL; // avoid compiler / coverity complaints +} + + +/** + * Iterator to get the appropiate ACK value from all children nodes. + * + * @param cls Closue (tunnel). + * @param id Id of the child node. + */ +static void +tunnel_get_child_fwd_ack (void *cls, + GNUNET_PEER_Id id) +{ + struct GNUNET_PeerIdentity peer_id; + struct MeshTunnelChildInfo *cinfo; + struct MeshTunnelChildIteratorContext *ctx = cls; + struct MeshTunnel *t = ctx->t; + uint32_t ack; + + GNUNET_PEER_resolve (id, &peer_id); + cinfo = tunnel_get_neighbor_fc (t, &peer_id); + ack = cinfo->fwd_ack; + + ctx->nchildren++; + if (GNUNET_NO == ctx->init) + { + ctx->max_child_ack = ack; + ctx->init = GNUNET_YES; + } + + if (GNUNET_YES == t->speed_min) + { + ctx->max_child_ack = ctx->max_child_ack > ack ? ack : ctx->max_child_ack; + } + else + { + ctx->max_child_ack = ctx->max_child_ack > ack ? ctx->max_child_ack : ack; + } + +} + + +/** + * Get the maximum PID allowed to transmit to any + * tunnel child of the local peer, depending on the tunnel + * buffering/speed settings. + * + * @param t Tunnel. + * + * @return Maximum PID allowed (uint32 MAX), -1LL if node has no children. + */ +static int64_t +tunnel_get_children_fwd_ack (struct MeshTunnel *t) +{ + struct MeshTunnelChildIteratorContext ctx; + ctx.t = t; + ctx.max_child_ack = 0; + ctx.nchildren = 0; + ctx.init = GNUNET_NO; + tree_iterate_children (t->tree, tunnel_get_child_fwd_ack, &ctx); + + if (0 == ctx.nchildren) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " tunnel has no children, no FWD ACK\n"); + return -1LL; + } + + if (GNUNET_YES == t->nobuffer && GMC_is_pid_bigger(ctx.max_child_ack, t->fwd_pid)) + ctx.max_child_ack = t->fwd_pid + 1; // Might overflow, it's ok. + + return (int64_t) ctx.max_child_ack; +} + + +/** + * Set the FWD ACK value of a client in a particular tunnel. + * + * @param t Tunnel affected. + * @param c Client whose ACK to set. + * @param ack ACK value. + */ +static void +tunnel_set_client_fwd_ack (struct MeshTunnel *t, + struct MeshClient *c, + uint32_t ack) +{ + unsigned int i; + + for (i = 0; i < t->nclients; i++) + { + if (t->clients[i] != c) + continue; + t->clients_fc[i].fwd_ack = ack; + return; + } + GNUNET_break (0); +} + + +/** + * Get the highest ACK value of all clients in a particular tunnel, + * according to the buffering/speed settings. + * + * @param t Tunnel on which to look. + * + * @return Corresponding ACK value (max uint32_t). + * If no clients are suscribed, -1LL. + */ +static int64_t +tunnel_get_clients_fwd_ack (struct MeshTunnel *t) +{ + unsigned int i; + int64_t ack; + + if (0 == t->nclients) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " tunnel has no clients, no FWD ACK\n"); + return -1LL; + } + + for (ack = -1LL, i = 0; i < t->nclients; i++) + { + if (-1LL == ack || + (GNUNET_YES == t->speed_min && + GNUNET_YES == GMC_is_pid_bigger (ack, t->clients_fc[i].fwd_ack)) || + (GNUNET_NO == t->speed_min && + GNUNET_YES == GMC_is_pid_bigger (t->clients_fc[i].fwd_ack, ack))) + { + ack = t->clients_fc[i].fwd_ack; + } + } + + if (GNUNET_YES == t->nobuffer && GMC_is_pid_bigger(ack, t->fwd_pid)) + ack = (uint32_t) t->fwd_pid + 1; // Might overflow, it's ok. + + return (uint32_t) ack; +} + + +/** + * Get the current fwd ack value for a tunnel, taking in account the tunnel + * mode and the status of all children nodes. + * + * @param t Tunnel. + * + * @return Maximum PID allowed. + */ +static uint32_t +tunnel_get_fwd_ack (struct MeshTunnel *t) +{ + uint32_t ack; + uint32_t count; + uint32_t buffer_free; + int64_t child_ack; + int64_t client_ack; + + count = t->fwd_pid - t->skip; + buffer_free = t->fwd_queue_max - t->fwd_queue_n; + child_ack = tunnel_get_children_fwd_ack (t); + client_ack = tunnel_get_clients_fwd_ack (t); + if (GNUNET_YES == t->nobuffer) + { + ack = count; + if (-1LL == child_ack) + child_ack = client_ack; + if (-1LL == child_ack) + { + GNUNET_break (0); + client_ack = child_ack = ack; + } + } + else + { + ack = count + buffer_free; // Overflow? OK! + } + if (-1LL == child_ack) + { + // Node has no children, child_ack AND core buffer are irrelevant. + if (-1LL == client_ack) // No children AND no clients? Not good! + { + GNUNET_STATISTICS_update (stats, "# mesh acks with no target", + 1, GNUNET_NO); + + } + return (uint32_t) client_ack; + } + if (-1LL == client_ack) + { + client_ack = ack; + } + if (GNUNET_YES == t->speed_min) + { + ack = GMC_min_pid ((uint32_t) child_ack, ack); + ack = GMC_min_pid ((uint32_t) client_ack, ack); + } + else + { + ack = GMC_max_pid ((uint32_t) child_ack, ack); + ack = GMC_max_pid ((uint32_t) client_ack, ack); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "c %u, bf %u, ch %lld, cl %lld, ACK: %u\n", + count, buffer_free, child_ack, client_ack, ack); + return ack; +} + + +/** + * Build a local ACK message and send it to a local client. + * + * @param t Tunnel on which to send the ACK. + * @param c Client to whom send the ACK. + * @param ack Value of the ACK. + */ +static void +send_local_ack (struct MeshTunnel *t, struct MeshClient *c, uint32_t ack) +{ + struct GNUNET_MESH_LocalAck msg; + + msg.header.size = htons (sizeof (msg)); + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK); + msg.tunnel_id = htonl (t->owner == c ? t->local_tid : t->local_tid_dest); + msg.max_pid = htonl (ack); + GNUNET_SERVER_notification_context_unicast(nc, + c->handle, + &msg.header, + GNUNET_NO); +} + +/** + * Build an ACK message and queue it to send to the given peer. + * + * @param t Tunnel on which to send the ACK. + * @param peer Peer to whom send the ACK. + * @param ack Value of the ACK. + */ +static void +send_ack (struct MeshTunnel *t, struct GNUNET_PeerIdentity *peer, uint32_t ack) +{ + struct GNUNET_MESH_ACK msg; + + GNUNET_PEER_resolve (t->id.oid, &msg.oid); + msg.header.size = htons (sizeof (msg)); + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_ACK); + msg.pid = htonl (ack); + msg.tid = htonl (t->id.tid); + + send_prebuilt_message (&msg.header, peer, t); +} + + +/** + * Notify a the owner of a tunnel about how many more + * payload packages will we accept on a given tunnel. + * + * @param t Tunnel on which to send the ACK. + */ +static void +tunnel_send_client_fwd_ack (struct MeshTunnel *t) +{ + uint32_t ack; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending client FWD ACK on tunnel %X\n", + t->local_tid); + + ack = tunnel_get_fwd_ack (t); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " ack %u\n", ack); + if (t->last_fwd_ack == ack) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " same as last, not sending!\n"); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " sending!\n"); + t->last_fwd_ack = ack; + send_local_ack (t, t->owner, ack); +} + + +/** + * Send an ACK informing the predecessor about the available buffer space. + * In case there is no predecessor, inform the owning client. + * If buffering is off, send only on behalf of children or self if endpoint. + * If buffering is on, send when sent to children and buffer space is free. + * Note that although the name is fwd_ack, the FWD mean forward *traffic*, + * the ACK itself goes "back" (towards root). + * + * @param t Tunnel on which to send the ACK. + * @param type Type of message that triggered the ACK transmission. + */ +static void +tunnel_send_fwd_ack (struct MeshTunnel *t, uint16_t type) +{ + struct GNUNET_PeerIdentity id; + uint32_t ack; + + if (NULL != t->owner) + { + tunnel_send_client_fwd_ack (t); + return; + } + /* Is it after unicast / multicast retransmission? */ + switch (type) + { + case GNUNET_MESSAGE_TYPE_MESH_UNICAST: + case GNUNET_MESSAGE_TYPE_MESH_MULTICAST: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "ACK due to FWD DATA retransmission\n"); + if (GNUNET_YES == t->nobuffer) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Not sending ACK, nobuffer\n"); + return; + } + break; + case GNUNET_MESSAGE_TYPE_MESH_ACK: + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK: + break; + case GNUNET_MESSAGE_TYPE_MESH_POLL: + t->force_ack = GNUNET_YES; + break; + default: + GNUNET_break (0); + } + + /* Check if we need to transmit the ACK */ + if (t->fwd_queue_max > t->fwd_queue_n * 4 && + GMC_is_pid_bigger(t->last_fwd_ack, t->fwd_pid) && + GNUNET_NO == t->force_ack) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Not sending ACK, buffer free\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " t->qmax: %u, t->qn: %u\n", + t->fwd_queue_max, t->fwd_queue_n); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " t->pid: %u, t->ack: %u\n", + t->fwd_pid, t->last_fwd_ack); + return; + } + + /* Ok, ACK might be necessary, what PID to ACK? */ + ack = tunnel_get_fwd_ack (t); + + /* If speed_min and not all children have ack'd, dont send yet */ + if (ack == t->last_fwd_ack && GNUNET_NO == t->force_ack) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Not sending FWD ACK, not ready\n"); + return; + } + + t->last_fwd_ack = ack; + GNUNET_PEER_resolve (tree_get_predecessor (t->tree), &id); + send_ack (t, &id, ack); + debug_fwd_ack++; + t->force_ack = GNUNET_NO; +} + + +/** + * Iterator to send a child node a BCK ACK to allow him to send more + * to_origin data. + * + * @param cls Closure (tunnel). + * @param id Id of the child node. + */ +static void +tunnel_send_child_bck_ack (void *cls, + GNUNET_PEER_Id id) +{ + struct MeshTunnel *t = cls; + struct MeshTunnelChildInfo *cinfo; + struct GNUNET_PeerIdentity peer; + uint32_t ack; + + GNUNET_PEER_resolve (id, &peer); + cinfo = tunnel_get_neighbor_fc (t, &peer); + ack = cinfo->bck_pid + t->bck_queue_max - t->bck_queue_n; + + if (cinfo->bck_ack == ack && GNUNET_NO == t->force_ack) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " Not sending ACK, not needed\n"); + return; + } + cinfo->bck_ack = ack; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " Sending BCK ACK %u (last sent: %u)\n", + ack, cinfo->bck_ack); + send_ack (t, &peer, ack); +} + + +/** + * @brief Send BCK ACKs to clients to allow them more to_origin traffic + * + * Iterates over all clients and sends BCK ACKs to the ones that need it. + * + * FIXME fc: what happens if we have 2 clients but q_size is 1? + * - implement a size 1 buffer in each client_fc AND children_fc + * to hold at least 1 message per "child". + * problem: violates no buffer policy + * - ack 0 and make "children" poll for transmission slots + * problem: big overhead, extra latency even in low traffic + * settings + * + * @param t Tunnel on which to send the BCK ACKs. + */ +static void +tunnel_send_clients_bck_ack (struct MeshTunnel *t) +{ + unsigned int i; + unsigned int tunnel_delta; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Sending BCK ACK to clients\n"); + + tunnel_delta = t->bck_queue_max - t->bck_queue_n; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " tunnel delta: %u\n", tunnel_delta); + + /* Find client whom to allow to send to origin (with lowest buffer space) */ + for (i = 0; i < t->nclients; i++) + { + struct MeshTunnelClientInfo *clinfo; + unsigned int delta; + + clinfo = &t->clients_fc[i]; + delta = clinfo->bck_ack - clinfo->bck_pid; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " client %u delta: %u\n", + t->clients[i]->id, delta); + + if ((GNUNET_NO == t->nobuffer && tunnel_delta > delta) || + (GNUNET_YES == t->nobuffer && 0 == delta)) + { + uint32_t ack; + + ack = clinfo->bck_pid; + ack += t->nobuffer ? 1 : tunnel_delta; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " sending ack to client %u: %u\n", + t->clients[i]->id, ack); + send_local_ack (t, t->clients[i], ack); + clinfo->bck_ack = ack; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " not sending ack to client %u (td %u, d %u)\n", + t->clients[i]->id, tunnel_delta, delta); + } + } +} + + +/** + * Send an ACK informing the children nodes and destination clients about + * the available buffer space. + * If buffering is off, send only on behalf of root (can be self). + * If buffering is on, send when sent to predecessor and buffer space is free. + * Note that although the name is bck_ack, the BCK mean backwards *traffic*, + * the ACK itself goes "forward" (towards children/clients). + * + * @param t Tunnel on which to send the ACK. + * @param type Type of message that triggered the ACK transmission. + */ +static void +tunnel_send_bck_ack (struct MeshTunnel *t, uint16_t type) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending BCK ACK on tunnel %u [%u] due to %s\n", + t->id.oid, t->id.tid, GNUNET_MESH_DEBUG_M2S(type)); + /* Is it after data to_origin retransmission? */ + switch (type) + { + case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: + if (GNUNET_YES == t->nobuffer) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " Not sending ACK, nobuffer\n"); + return; + } + break; + case GNUNET_MESSAGE_TYPE_MESH_ACK: + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK: + break; + case GNUNET_MESSAGE_TYPE_MESH_POLL: + t->force_ack = GNUNET_YES; + break; + default: + GNUNET_break (0); + } + + tunnel_send_clients_bck_ack (t); + tree_iterate_children (t->tree, &tunnel_send_child_bck_ack, t); + t->force_ack = GNUNET_NO; +} + + +/** + * @brief Re-initiate traffic to this peer if necessary. + * + * Check if there is traffic queued towards this peer + * and the core transmit handle is NULL (traffic was stalled). + * If so, call core tmt rdy. + * + * @param cls Closure (unused) + * @param peer_id Short ID of peer to which initiate traffic. + */ +static void +peer_unlock_queue(void *cls, GNUNET_PEER_Id peer_id) +{ + struct MeshPeerInfo *peer; + struct GNUNET_PeerIdentity id; + struct MeshPeerQueue *q; + size_t size; + + peer = peer_info_get_short(peer_id); + if (NULL != peer->core_transmit) + return; + + q = queue_get_next(peer); + if (NULL == q) + { + /* Might br multicast traffic already sent to this particular peer but + * not to other children in this tunnel. + * This way t->queue_n would be > 0 but the queue of this particular peer + * would be empty. + */ + return; + } + size = q->size; + GNUNET_PEER_resolve (peer->id, &id); + peer->core_transmit = + GNUNET_CORE_notify_transmit_ready(core_handle, + 0, + 0, + GNUNET_TIME_UNIT_FOREVER_REL, + &id, + size, + &queue_send, + peer); + return; +} + + +/** + * @brief Allow transmission of FWD traffic on this tunnel + * + * Check if there is traffic queued towards any children + * and the core transmit handle is NULL, and if so, call core tmt rdy. + * + * @param t Tunnel on which to unlock FWD traffic. + */ +static void +tunnel_unlock_fwd_queues (struct MeshTunnel *t) +{ + if (0 == t->fwd_queue_n) + return; + + tree_iterate_children (t->tree, &peer_unlock_queue, NULL); +} + + +/** + * @brief Allow transmission of BCK traffic on this tunnel + * + * Check if there is traffic queued towards the root of the tree + * and the core transmit handle is NULL, and if so, call core tmt rdy. + * + * @param t Tunnel on which to unlock BCK traffic. + */ +static void +tunnel_unlock_bck_queue (struct MeshTunnel *t) +{ + if (0 == t->bck_queue_n) + return; + + peer_unlock_queue(NULL, tree_get_predecessor(t->tree)); +} + + +/** * Send a message to all peers in this tunnel that the tunnel is no longer * valid. * * @param t The tunnel whose peers to notify. + * @param parent ID of the parent, in case the tree is already destroyed. */ static void -tunnel_send_destroy (struct MeshTunnel *t) +tunnel_send_destroy (struct MeshTunnel *t, GNUNET_PEER_Id parent) { struct GNUNET_MESH_TunnelDestroy msg; + struct GNUNET_PeerIdentity id; msg.header.size = htons (sizeof (msg)); msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_TUNNEL_DESTROY); GNUNET_PEER_resolve (t->id.oid, &msg.oid); msg.tid = htonl (t->id.tid); - tunnel_send_multicast (t, &msg.header, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " sending tunnel destroy for tunnel: %s [%X]\n", + GNUNET_i2s (&msg.oid), t->id.tid); + if (tree_count_children(t->tree) > 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " sending multicast to children\n"); + tunnel_send_multicast (t, &msg.header); + } + if (0 == parent) + parent = tree_get_predecessor (t->tree); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " parent: %u\n", parent); + if (0 == parent) + return; + + GNUNET_PEER_resolve (parent, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " sending back to %s\n", + GNUNET_i2s (&id)); + send_prebuilt_message (&msg.header, &id, t); } +/** + * Cancel all transmissions towards a neighbor that belong to a certain tunnel. + * + * @param cls Closure (Tunnel which to cancel). + * @param neighbor_id Short ID of the neighbor to whom cancel the transmissions. + */ +static void +tunnel_cancel_queues (void *cls, GNUNET_PEER_Id neighbor_id) +{ + struct MeshTunnel *t = cls; + struct MeshPeerInfo *peer_info; + struct MeshPeerQueue *pq; + struct MeshPeerQueue *next; + + peer_info = peer_info_get_short (neighbor_id); + for (pq = peer_info->queue_head; NULL != pq; pq = next) + { + next = pq->next; + if (pq->tunnel == t) + { + if (GNUNET_MESSAGE_TYPE_MESH_MULTICAST == pq->type || + GNUNET_MESSAGE_TYPE_MESH_UNICAST == pq->type || + GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN == pq->type) + { + // Should have been removed on destroy children + GNUNET_break (0); + } + queue_destroy (pq, GNUNET_YES); + } + } + if (NULL == peer_info->queue_head && NULL != peer_info->core_transmit) + { + GNUNET_CORE_notify_transmit_ready_cancel(peer_info->core_transmit); + peer_info->core_transmit = NULL; + } +} /** * Destroy the tunnel and free any allocated resources linked to it. @@ -2444,9 +4044,7 @@ static int tunnel_destroy (struct MeshTunnel *t) { struct MeshClient *c; - struct MeshQueue *q; - struct MeshQueue *qn; - GNUNET_HashCode hash; + struct GNUNET_HashCode hash; unsigned int i; int r; @@ -2470,16 +4068,21 @@ tunnel_destroy (struct MeshTunnel *t) GNUNET_CRYPTO_hash (&t->id, sizeof (struct MESH_TunnelID), &hash); if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_remove (tunnels, &hash, t)) { + GNUNET_break (0); r = GNUNET_SYSERR; } - GNUNET_CRYPTO_hash (&t->local_tid, sizeof (MESH_TunnelNumber), &hash); - if (NULL != c && - GNUNET_YES != - GNUNET_CONTAINER_multihashmap_remove (c->own_tunnels, &hash, t)) + if (NULL != c) { - r = GNUNET_SYSERR; + GNUNET_CRYPTO_hash (&t->local_tid, sizeof (MESH_TunnelNumber), &hash); + if (GNUNET_YES != + GNUNET_CONTAINER_multihashmap_remove (c->own_tunnels, &hash, t)) + { + GNUNET_break (0); + r = GNUNET_SYSERR; + } } + GNUNET_CRYPTO_hash (&t->local_tid_dest, sizeof (MESH_TunnelNumber), &hash); for (i = 0; i < t->nclients; i++) { @@ -2487,6 +4090,7 @@ tunnel_destroy (struct MeshTunnel *t) if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_remove (c->incoming_tunnels, &hash, t)) { + GNUNET_break (0); r = GNUNET_SYSERR; } } @@ -2496,45 +4100,207 @@ tunnel_destroy (struct MeshTunnel *t) if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_remove (c->ignore_tunnels, &hash, t)) { + GNUNET_break (0); r = GNUNET_SYSERR; } } - if (t->nclients > 0) - { - if (GNUNET_YES != - GNUNET_CONTAINER_multihashmap_remove (incoming_tunnels, &hash, t)) - { - r = GNUNET_SYSERR; - } - GNUNET_free (t->clients); - } + + (void) GNUNET_CONTAINER_multihashmap_remove (incoming_tunnels, &hash, t); + GNUNET_free_non_null (t->clients); + GNUNET_free_non_null (t->ignore); + GNUNET_free_non_null (t->clients_fc); + if (NULL != t->peers) { GNUNET_CONTAINER_multihashmap_iterate (t->peers, &peer_info_delete_tunnel, t); GNUNET_CONTAINER_multihashmap_destroy (t->peers); } - q = t->queue_head; - while (NULL != q) - { - if (NULL != q->data) - GNUNET_free (q->data); - qn = q->next; - GNUNET_free (q); - q = qn; - /* TODO cancel core transmit ready in case it was active */ - } + + GNUNET_CONTAINER_multihashmap_iterate (t->children_fc, + &tunnel_destroy_child, + t); + GNUNET_CONTAINER_multihashmap_destroy (t->children_fc); + t->children_fc = NULL; + + tree_iterate_children (t->tree, &tunnel_cancel_queues, t); tree_destroy (t->tree); + + if (NULL != t->regex_search) + GNUNET_REGEX_search_cancel (t->regex_search->search_handle); if (NULL != t->dht_get_type) GNUNET_DHT_get_stop (t->dht_get_type); if (GNUNET_SCHEDULER_NO_TASK != t->timeout_task) GNUNET_SCHEDULER_cancel (t->timeout_task); if (GNUNET_SCHEDULER_NO_TASK != t->path_refresh_task) GNUNET_SCHEDULER_cancel (t->path_refresh_task); + + n_tunnels--; + GNUNET_STATISTICS_update (stats, "# tunnels", -1, GNUNET_NO); GNUNET_free (t); return r; } +#define TUNNEL_DESTROY_EMPTY_TIME GNUNET_TIME_UNIT_MILLISECONDS + +/** + * Tunnel is empty: destroy it. + * + * @param cls Closure (Tunnel). + * @param tc TaskContext. + */ +static void +tunnel_destroy_empty_delayed (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct MeshTunnel *t = cls; + + t->delayed_destroy = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + + if (0 != t->nclients || + 0 != tree_count_children (t->tree)) + return; + + #if MESH_DEBUG + { + struct GNUNET_PeerIdentity id; + + GNUNET_PEER_resolve (t->id.oid, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "executing destruction of empty tunnel %s [%X]\n", + GNUNET_i2s (&id), t->id.tid); + } + #endif + + tunnel_send_destroy (t, 0); + if (0 == t->pending_messages) + tunnel_destroy (t); + else + t->destroy = GNUNET_YES; +} + + +/** + * Schedule tunnel destruction if is empty and no new traffic comes in a time. + * + * @param t Tunnel to destroy if empty. + */ +static void +tunnel_destroy_empty (struct MeshTunnel *t) +{ + if (GNUNET_SCHEDULER_NO_TASK != t->delayed_destroy || + 0 != t->nclients || + 0 != tree_count_children (t->tree)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%u %u %u\n", + t->delayed_destroy, t->nclients, tree_count_children(t->tree)); + return; + } + + #if MESH_DEBUG + { + struct GNUNET_PeerIdentity id; + + GNUNET_PEER_resolve (t->id.oid, &id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "scheduling destruction of empty tunnel %s [%X]\n", + GNUNET_i2s (&id), t->id.tid); + } + #endif + + t->delayed_destroy = + GNUNET_SCHEDULER_add_delayed (TUNNEL_DESTROY_EMPTY_TIME, + &tunnel_destroy_empty_delayed, + t); +} + + +/** + * Create a new tunnel + * + * @param owner Who is the owner of the tunnel (short ID). + * @param tid Tunnel Number of the tunnel. + * @param client Clients that owns the tunnel, NULL for foreign tunnels. + * @param local Tunnel Number for the tunnel, for the client point of view. + * + * @return A new initialized tunnel. NULL on error. + */ +static struct MeshTunnel * +tunnel_new (GNUNET_PEER_Id owner, + MESH_TunnelNumber tid, + struct MeshClient *client, + MESH_TunnelNumber local) +{ + struct MeshTunnel *t; + struct GNUNET_HashCode hash; + + if (n_tunnels >= max_tunnels && NULL == client) + return NULL; + + t = GNUNET_malloc (sizeof (struct MeshTunnel)); + t->id.oid = owner; + t->id.tid = tid; + t->fwd_queue_max = (max_msgs_queue / max_tunnels) + 1; + t->bck_queue_max = t->fwd_queue_max; + t->tree = tree_new (owner); + t->owner = client; + t->fwd_pid = (uint32_t) -1; // Next (expected) = 0 + t->bck_pid = (uint32_t) -1; // Next (expected) = 0 + t->bck_ack = INITIAL_WINDOW_SIZE - 1; + t->last_fwd_ack = INITIAL_WINDOW_SIZE - 1; + t->local_tid = local; + t->children_fc = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO); + n_tunnels++; + GNUNET_STATISTICS_update (stats, "# tunnels", 1, GNUNET_NO); + + GNUNET_CRYPTO_hash (&t->id, sizeof (struct MESH_TunnelID), &hash); + if (GNUNET_OK != + GNUNET_CONTAINER_multihashmap_put (tunnels, &hash, t, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + { + GNUNET_break (0); + tunnel_destroy (t); + if (NULL != client) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client->handle, GNUNET_SYSERR); + } + return NULL; + } + + if (NULL != client) + { + GNUNET_CRYPTO_hash (&t->local_tid, sizeof (MESH_TunnelNumber), &hash); + if (GNUNET_OK != + GNUNET_CONTAINER_multihashmap_put (client->own_tunnels, &hash, t, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + { + tunnel_destroy (t); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client->handle, GNUNET_SYSERR); + return NULL; + } + } + + return t; +} + +/** + * Callback when removing children from a tunnel tree. Notify owner. + * + * @param cls Closure (tunnel). + * @param peer_id Short ID of the peer deleted. + */ +void +tunnel_child_removed (void *cls, GNUNET_PEER_Id peer_id) +{ + struct MeshTunnel *t = cls; + + client_notify_peer_disconnected (t->owner, t, peer_id); +} /** * Removes an explicit path from a tunnel, freeing all intermediate nodes @@ -2547,8 +4313,15 @@ tunnel_destroy (struct MeshTunnel *t) static void tunnel_delete_peer (struct MeshTunnel *t, GNUNET_PEER_Id peer) { - if (GNUNET_NO == tree_del_peer (t->tree, peer, NULL, NULL)) - tunnel_destroy (t); + int r; + + r = tree_del_peer (t->tree, peer, &tunnel_child_removed, t); + if (GNUNET_NO == r) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Tunnel %u [%u] has no more nodes\n", + t->id.oid, t->id.tid); + } } @@ -2562,27 +4335,30 @@ tunnel_delete_peer (struct MeshTunnel *t, GNUNET_PEER_Id peer) * @param key the hash of the local tunnel id (used to access the hashmap) * @param value the value stored at the key (tunnel to destroy) * - * @return GNUNET_OK on success + * @return GNUNET_OK, keep iterating. */ static int -tunnel_destroy_iterator (void *cls, const GNUNET_HashCode * key, void *value) +tunnel_destroy_iterator (void *cls, + const struct GNUNET_HashCode * key, + void *value) { struct MeshTunnel *t = value; struct MeshClient *c = cls; - int r; - send_client_tunnel_disconnect(t, c); + send_client_tunnel_disconnect (t, c); if (c != t->owner) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Client %u is destination, keeping the tunnel alive.\n", c->id); - tunnel_delete_client(t, c); - client_delete_tunnel(c, t); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %u is destination.\n", c->id); + tunnel_delete_client (t, c); + client_delete_tunnel (c, t); + tunnel_destroy_empty (t); return GNUNET_OK; } - tunnel_send_destroy(t); - r = tunnel_destroy (t); - return r; + tunnel_send_destroy (t, 0); + t->owner = NULL; + t->destroy = GNUNET_YES; + + return GNUNET_OK; } @@ -2596,10 +4372,16 @@ static void tunnel_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct MeshTunnel *t = cls; + struct GNUNET_PeerIdentity id; - if (GNUNET_SCHEDULER_REASON_SHUTDOWN == tc->reason) - return; t->timeout_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + GNUNET_PEER_resolve(t->id.oid, &id); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Tunnel %s [%X] timed out. Destroying.\n", + GNUNET_i2s(&id), t->id.tid); + send_clients_tunnel_destroy (t); tunnel_destroy (t); } @@ -2607,6 +4389,8 @@ tunnel_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) * Resets the tunnel timeout. Starts it if no timeout was running. * * @param t Tunnel whose timeout to reset. + * + * TODO use heap to improve efficiency of scheduler. */ static void tunnel_reset_timeout (struct MeshTunnel *t) @@ -2615,7 +4399,7 @@ tunnel_reset_timeout (struct MeshTunnel *t) GNUNET_SCHEDULER_cancel (t->timeout_task); t->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply - (REFRESH_PATH_TIME, 4), &tunnel_timeout, t); + (refresh_path_time, 4), &tunnel_timeout, t); } @@ -2624,10 +4408,7 @@ tunnel_reset_timeout (struct MeshTunnel *t) /******************************************************************************/ /** - * Function called to notify a client about the socket - * being ready to queue more data. "buf" will be - * NULL and "size" zero if the socket was closed for - * writing in the meantime. + * Function to send a create path packet to a peer. * * @param cls closure * @param size number of bytes available in buf @@ -2635,15 +4416,15 @@ tunnel_reset_timeout (struct MeshTunnel *t) * @return number of bytes written to buf */ static size_t -send_core_create_path (void *cls, size_t size, void *buf) +send_core_path_create (void *cls, size_t size, void *buf) { struct MeshPathInfo *info = cls; struct GNUNET_MESH_ManipulatePath *msg; struct GNUNET_PeerIdentity *peer_ptr; - struct MeshPeerInfo *peer = info->peer; struct MeshTunnel *t = info->t; struct MeshPeerPath *p = info->path; size_t size_needed; + uint32_t opt; int i; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CREATE PATH sending...\n"); @@ -2653,35 +4434,22 @@ send_core_create_path (void *cls, size_t size, void *buf) if (size < size_needed || NULL == buf) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "create path retransmit!\n"); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " buf: %p\n", buf); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " size: (%u/%u)\n", size, - size_needed); - info->peer->core_transmit[info->pos] = - GNUNET_CORE_notify_transmit_ready (core_handle, 0, 0, - GNUNET_TIME_UNIT_FOREVER_REL, - tree_get_first_hop (t->tree, - peer->id), - size_needed, &send_core_create_path, - info); + GNUNET_break (0); return 0; } - info->peer->core_transmit[info->pos] = NULL; -#if MESH_DEBUG - { - struct GNUNET_PeerIdentity id; - - GNUNET_PEER_resolve (peer->id, &id); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - " setting core_transmit %s [%u] to NULL\n", - GNUNET_i2s (&id), info->pos); - } -#endif msg = (struct GNUNET_MESH_ManipulatePath *) buf; msg->header.size = htons (size_needed); msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_PATH_CREATE); msg->tid = ntohl (t->id.tid); + opt = 0; + if (GNUNET_YES == t->speed_min) + opt |= MESH_TUNNEL_OPT_SPEED_MIN; + if (GNUNET_YES == t->nobuffer) + opt |= MESH_TUNNEL_OPT_NOBUFFER; + msg->opt = htonl(opt); + msg->reserved = 0; + peer_ptr = (struct GNUNET_PeerIdentity *) &msg[1]; for (i = 0; i < p->length; i++) { @@ -2698,10 +4466,7 @@ send_core_create_path (void *cls, size_t size, void *buf) /** - * Function called to notify a client about the socket - * being ready to queue more data. "buf" will be - * NULL and "size" zero if the socket was closed for - * writing in the meantime. + * Fill the core buffer * * @param cls closure (data itself) * @param size number of bytes available in buf @@ -2723,22 +4488,9 @@ send_core_data_multicast (void *cls, size_t size, void *buf) if (total_size > size) { - /* Retry */ - struct GNUNET_PeerIdentity id; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Multicast: retransmitting... (%u/%u)\n", size, - total_size); - GNUNET_PEER_resolve (info->peer->id, &id); - info->peer->core_transmit[info->handler_n] = - GNUNET_CORE_notify_transmit_ready (core_handle, 0, 0, - GNUNET_TIME_UNIT_FOREVER_REL, &id, - total_size, - &send_core_data_multicast, info); + GNUNET_break (0); return 0; } - info->peer->core_transmit[info->handler_n] = NULL; - info->peer->infos[info->handler_n] = NULL; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " copying data...\n"); memcpy (buf, info->mesh_data->data, total_size); #if MESH_DEBUG @@ -2752,18 +4504,19 @@ send_core_data_multicast (void *cls, size_t size, void *buf) mc = (struct GNUNET_MESH_Multicast *) mh; mh = (struct GNUNET_MessageHeader *) &mc[1]; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - " multicast, payload type %u\n", ntohs (mh->type)); + " multicast, payload type %s\n", + GNUNET_MESH_DEBUG_M2S (ntohs (mh->type))); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " multicast, payload size %u\n", ntohs (mh->size)); } else { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " type %u\n", - ntohs (mh->type)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " type %s\n", + GNUNET_MESH_DEBUG_M2S (ntohs (mh->type))); } } #endif - data_descriptor_decrement_multicast (info->mesh_data); + data_descriptor_decrement_rc (info->mesh_data); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "freeing info...\n"); GNUNET_free (info); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "return %u\n", total_size); @@ -2772,10 +4525,7 @@ send_core_data_multicast (void *cls, size_t size, void *buf) /** - * Function called to notify a client about the socket - * being ready to queue more data. "buf" will be - * NULL and "size" zero if the socket was closed for - * writing in the meantime. + * Creates a path ack message in buf and frees all unused resources. * * @param cls closure (MeshTransmissionDescriptor) * @param size number of bytes available in buf @@ -2789,10 +4539,6 @@ send_core_path_ack (void *cls, size_t size, void *buf) struct GNUNET_MESH_PathACK *msg = buf; GNUNET_assert (NULL != info); - if (info->peer) - { - info->peer->core_transmit[info->handler_n] = NULL; - } if (sizeof (struct GNUNET_MESH_PathACK) > size) { GNUNET_break (0); @@ -2803,6 +4549,7 @@ send_core_path_ack (void *cls, size_t size, void *buf) GNUNET_PEER_resolve (info->origin->oid, &msg->oid); msg->tid = htonl (info->origin->tid); msg->peer_id = my_full_id; + GNUNET_free (info); /* TODO add signature */ @@ -2811,6 +4558,507 @@ send_core_path_ack (void *cls, size_t size, void *buf) } +/** + * Free a transmission that was already queued with all resources + * associated to the request. + * + * @param queue Queue handler to cancel. + * @param clear_cls Is it necessary to free associated cls? + */ +static void +queue_destroy (struct MeshPeerQueue *queue, int clear_cls) +{ + struct MeshTransmissionDescriptor *dd; + struct MeshPathInfo *path_info; + struct MeshTunnelChildInfo *cinfo; + struct GNUNET_PeerIdentity id; + unsigned int i; + unsigned int max; + + if (GNUNET_YES == clear_cls) + { + switch (queue->type) + { + case GNUNET_MESSAGE_TYPE_MESH_TUNNEL_DESTROY: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, " cancelling TUNNEL_DESTROY\n"); + GNUNET_break (GNUNET_YES == queue->tunnel->destroy); + /* fall through */ + case GNUNET_MESSAGE_TYPE_MESH_UNICAST: + case GNUNET_MESSAGE_TYPE_MESH_MULTICAST: + case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: + case GNUNET_MESSAGE_TYPE_MESH_ACK: + case GNUNET_MESSAGE_TYPE_MESH_POLL: + case GNUNET_MESSAGE_TYPE_MESH_PATH_KEEPALIVE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " prebuilt message\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " type %s\n", + GNUNET_MESH_DEBUG_M2S(queue->type)); + dd = queue->cls; + data_descriptor_decrement_rc (dd->mesh_data); + break; + case GNUNET_MESSAGE_TYPE_MESH_PATH_CREATE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " type create path\n"); + path_info = queue->cls; + path_destroy (path_info->path); + break; + default: + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + " type %s unknown!\n", + GNUNET_MESH_DEBUG_M2S(queue->type)); + } + GNUNET_free_non_null (queue->cls); + } + GNUNET_CONTAINER_DLL_remove (queue->peer->queue_head, + queue->peer->queue_tail, + queue); + + /* Delete from child_fc in the appropiate tunnel */ + max = queue->tunnel->fwd_queue_max; + GNUNET_PEER_resolve (queue->peer->id, &id); + cinfo = tunnel_get_neighbor_fc (queue->tunnel, &id); + if (NULL != cinfo) + { + for (i = 0; i < cinfo->send_buffer_n; i++) + { + unsigned int i2; + i2 = (cinfo->send_buffer_start + i) % max; + if (cinfo->send_buffer[i2] == queue) + { + /* Found corresponding entry in the send_buffer. Move all others back. */ + unsigned int j; + unsigned int j2; + unsigned int j3; + + for (j = i, j2 = 0, j3 = 0; j < cinfo->send_buffer_n - 1; j++) + { + j2 = (cinfo->send_buffer_start + j) % max; + j3 = (cinfo->send_buffer_start + j + 1) % max; + cinfo->send_buffer[j2] = cinfo->send_buffer[j3]; + } + + cinfo->send_buffer[j3] = NULL; + cinfo->send_buffer_n--; + } + } + } + + GNUNET_free (queue); +} + + +/** + * @brief Get the next transmittable message from the queue. + * + * This will be the head, except in the case of being a data packet + * not allowed by the destination peer. + * + * @param peer Destination peer. + * + * @return The next viable MeshPeerQueue element to send to that peer. + * NULL when there are no transmittable messages. + */ +struct MeshPeerQueue * +queue_get_next (const struct MeshPeerInfo *peer) +{ + struct MeshPeerQueue *q; + struct MeshTunnel *t; + struct MeshTransmissionDescriptor *info; + struct MeshTunnelChildInfo *cinfo; + struct GNUNET_MESH_Unicast *ucast; + struct GNUNET_MESH_ToOrigin *to_orig; + struct GNUNET_MESH_Multicast *mcast; + struct GNUNET_PeerIdentity id; + uint32_t pid; + uint32_t ack; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* selecting message\n"); + for (q = peer->queue_head; NULL != q; q = q->next) + { + t = q->tunnel; + info = q->cls; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* %s\n", + GNUNET_MESH_DEBUG_M2S(q->type)); + switch (q->type) + { + case GNUNET_MESSAGE_TYPE_MESH_UNICAST: + ucast = (struct GNUNET_MESH_Unicast *) info->mesh_data->data; + pid = ntohl (ucast->pid); + GNUNET_PEER_resolve (info->peer->id, &id); + cinfo = tunnel_get_neighbor_fc(t, &id); + ack = cinfo->fwd_ack; + break; + case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: + to_orig = (struct GNUNET_MESH_ToOrigin *) info->mesh_data->data; + pid = ntohl (to_orig->pid); + ack = t->bck_ack; + break; + case GNUNET_MESSAGE_TYPE_MESH_MULTICAST: + mcast = (struct GNUNET_MESH_Multicast *) info->mesh_data->data; + if (GNUNET_MESSAGE_TYPE_MESH_MULTICAST != ntohs(mcast->header.type)) + { + // Not a multicast payload: multicast control traffic (destroy, etc) + return q; + } + pid = ntohl (mcast->pid); + GNUNET_PEER_resolve (info->peer->id, &id); + cinfo = tunnel_get_neighbor_fc(t, &id); + ack = cinfo->fwd_ack; + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* OK!\n"); + return q; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* ACK: %u, PID: %u\n", + ack, pid); + if (GNUNET_NO == GMC_is_pid_bigger(pid, ack)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* OK!\n"); + return q; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* NEXT!\n"); + } + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* nothing found\n"); + return NULL; +} + + +/** + * Core callback to write a queued packet to core buffer + * + * @param cls Closure (peer info). + * @param size Number of bytes available in buf. + * @param buf Where the to write the message. + * + * @return number of bytes written to buf + */ +static size_t +queue_send (void *cls, size_t size, void *buf) +{ + struct MeshPeerInfo *peer = cls; + struct GNUNET_MessageHeader *msg; + struct MeshPeerQueue *queue; + struct MeshTunnel *t; + struct MeshTunnelChildInfo *cinfo; + struct GNUNET_PeerIdentity dst_id; + size_t data_size; + + peer->core_transmit = NULL; + cinfo = NULL; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* Queue send\n"); + queue = queue_get_next (peer); + + /* Queue has no internal mesh traffic nor sendable payload */ + if (NULL == queue) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* not ready, return\n"); + if (NULL == peer->queue_head) + GNUNET_break (0); // Should've been canceled + return 0; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* not empty\n"); + + GNUNET_PEER_resolve (peer->id, &dst_id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* towards %s\n", + GNUNET_i2s(&dst_id)); + /* Check if buffer size is enough for the message */ + if (queue->size > size) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* not enough room, reissue\n"); + peer->core_transmit = + GNUNET_CORE_notify_transmit_ready (core_handle, + 0, + 0, + GNUNET_TIME_UNIT_FOREVER_REL, + &dst_id, + queue->size, + &queue_send, + peer); + return 0; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* size ok\n"); + + t = queue->tunnel; + GNUNET_assert (0 < t->pending_messages); + t->pending_messages--; + if (GNUNET_MESSAGE_TYPE_MESH_UNICAST == queue->type) + { + t->fwd_queue_n--; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* unicast: t->q (%u/%u)\n", + t->fwd_queue_n, t->fwd_queue_max); + } + else if (GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN == queue->type) + { + t->bck_queue_n--; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* to origin\n"); + } + + /* Fill buf */ + switch (queue->type) + { + case 0: + case GNUNET_MESSAGE_TYPE_MESH_ACK: + case GNUNET_MESSAGE_TYPE_MESH_POLL: + case GNUNET_MESSAGE_TYPE_MESH_PATH_BROKEN: + case GNUNET_MESSAGE_TYPE_MESH_PATH_DESTROY: + case GNUNET_MESSAGE_TYPE_MESH_TUNNEL_DESTROY: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* raw: %s\n", + GNUNET_MESH_DEBUG_M2S (queue->type)); + /* Fall through */ + case GNUNET_MESSAGE_TYPE_MESH_UNICAST: + case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: + data_size = send_core_data_raw (queue->cls, size, buf); + msg = (struct GNUNET_MessageHeader *) buf; + switch (ntohs (msg->type)) // Type of preconstructed message + { + case GNUNET_MESSAGE_TYPE_MESH_UNICAST: + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_UNICAST); + break; + case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: + tunnel_send_bck_ack (t, GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN); + break; + default: + break; + } + break; + case GNUNET_MESSAGE_TYPE_MESH_MULTICAST: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* multicast\n"); + { + struct MeshTransmissionDescriptor *info = queue->cls; + + if ((1 == info->mesh_data->reference_counter + && GNUNET_YES == t->speed_min) + || + (info->mesh_data->total_out == info->mesh_data->reference_counter + && GNUNET_NO == t->speed_min)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* considered sent\n"); + t->fwd_queue_n--; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "********* NOT considered sent yet\n"); + t->pending_messages++; + } + } + data_size = send_core_data_multicast(queue->cls, size, buf); + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_MULTICAST); + break; + case GNUNET_MESSAGE_TYPE_MESH_PATH_CREATE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* path create\n"); + data_size = send_core_path_create (queue->cls, size, buf); + break; + case GNUNET_MESSAGE_TYPE_MESH_PATH_ACK: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* path ack\n"); + data_size = send_core_path_ack (queue->cls, size, buf); + break; + case GNUNET_MESSAGE_TYPE_MESH_PATH_KEEPALIVE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* path keepalive\n"); + data_size = send_core_data_multicast (queue->cls, size, buf); + break; + default: + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "********* type unknown: %u\n", + queue->type); + data_size = 0; + } + switch (queue->type) + { + case GNUNET_MESSAGE_TYPE_MESH_UNICAST: + case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: + case GNUNET_MESSAGE_TYPE_MESH_MULTICAST: + cinfo = tunnel_get_neighbor_fc (t, &dst_id); + if (cinfo->send_buffer[cinfo->send_buffer_start] != queue) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "at pos %u (%p) != %p\n", + cinfo->send_buffer_start, + cinfo->send_buffer[cinfo->send_buffer_start], + queue); + } + if (cinfo->send_buffer_n > 0) + { + cinfo->send_buffer[cinfo->send_buffer_start] = NULL; + cinfo->send_buffer_n--; + cinfo->send_buffer_start++; + cinfo->send_buffer_start %= t->fwd_queue_max; + } + else + { + GNUNET_break (0); + } + break; + default: + break; + } + + /* Free queue, but cls was freed by send_core_* */ + queue_destroy (queue, GNUNET_NO); + + if (GNUNET_YES == t->destroy && 0 == t->pending_messages) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* destroying tunnel!\n"); + tunnel_destroy (t); + } + + /* If more data in queue, send next */ + queue = queue_get_next(peer); + if (NULL != queue) + { + struct GNUNET_PeerIdentity id; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* more data!\n"); + GNUNET_PEER_resolve (peer->id, &id); + peer->core_transmit = + GNUNET_CORE_notify_transmit_ready(core_handle, + 0, + 0, + GNUNET_TIME_UNIT_FOREVER_REL, + &id, + queue->size, + &queue_send, + peer); + } + else + { + if (NULL != peer->queue_head) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "********* %s stalled\n", + GNUNET_i2s(&my_full_id)); + if (NULL == cinfo) + cinfo = tunnel_get_neighbor_fc (t, &dst_id); + // FIXME unify bck/fwd structures, bck does not have cinfo right now + if (NULL != cinfo && GNUNET_SCHEDULER_NO_TASK == cinfo->fc_poll) + { + cinfo->fc_poll = GNUNET_SCHEDULER_add_delayed (cinfo->fc_poll_time, + &tunnel_poll, cinfo); + } + } + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "********* return %d\n", data_size); + return data_size; +} + + +/** + * @brief Queue and pass message to core when possible. + * + * If type is payload (UNICAST, TO_ORIGIN, MULTICAST) checks for queue status + * and accounts for it. In case the queue is full, the message is dropped and + * a break issued. + * + * Otherwise, message is treated as internal and allowed to go regardless of + * queue status. + * + * @param cls Closure (@c type dependant). It will be used by queue_send to + * build the message to be sent if not already prebuilt. + * @param type Type of the message, 0 for a raw message. + * @param size Size of the message. + * @param dst Neighbor to send message to. + * @param t Tunnel this message belongs to. + */ +static void +queue_add (void *cls, uint16_t type, size_t size, + struct MeshPeerInfo *dst, struct MeshTunnel *t) +{ + struct MeshPeerQueue *queue; + struct MeshTunnelChildInfo *cinfo; + struct GNUNET_PeerIdentity id; + unsigned int *max; + unsigned int *n; + unsigned int i; + + n = NULL; + if (GNUNET_MESSAGE_TYPE_MESH_UNICAST == type || + GNUNET_MESSAGE_TYPE_MESH_MULTICAST == type) + { + n = &t->fwd_queue_n; + max = &t->fwd_queue_max; + } + else if (GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN == type) + { + n = &t->bck_queue_n; + max = &t->bck_queue_max; + } + if (NULL != n) + { + if (*n >= *max) + { + GNUNET_break(0); + GNUNET_STATISTICS_update(stats, + "# messages dropped (buffer full)", + 1, GNUNET_NO); + return; // Drop message + } + (*n)++; + } + queue = GNUNET_malloc (sizeof (struct MeshPeerQueue)); + queue->cls = cls; + queue->type = type; + queue->size = size; + queue->peer = dst; + queue->tunnel = t; + GNUNET_CONTAINER_DLL_insert_tail (dst->queue_head, dst->queue_tail, queue); + GNUNET_PEER_resolve (dst->id, &id); + if (NULL == dst->core_transmit) + { + dst->core_transmit = + GNUNET_CORE_notify_transmit_ready (core_handle, + 0, + 0, + GNUNET_TIME_UNIT_FOREVER_REL, + &id, + size, + &queue_send, + dst); + } + t->pending_messages++; + if (NULL == n) // Is this internal mesh traffic? + return; + + // It's payload, keep track of buffer per peer. + cinfo = tunnel_get_neighbor_fc(t, &id); + i = (cinfo->send_buffer_start + cinfo->send_buffer_n) % t->fwd_queue_max; + if (NULL != cinfo->send_buffer[i]) + { + GNUNET_break (cinfo->send_buffer_n == t->fwd_queue_max); // aka i == start + queue_destroy (cinfo->send_buffer[cinfo->send_buffer_start], GNUNET_YES); + cinfo->send_buffer_start++; + cinfo->send_buffer_start %= t->fwd_queue_max; + } + else + { + cinfo->send_buffer_n++; + } + cinfo->send_buffer[i] = queue; + if (cinfo->send_buffer_n > t->fwd_queue_max) + { + GNUNET_break (0); + cinfo->send_buffer_n = t->fwd_queue_max; + } +} + + /******************************************************************************/ /******************** MESH NETWORK HANDLERS **************************/ /******************************************************************************/ @@ -2840,7 +5088,7 @@ handle_mesh_path_create (void *cls, const struct GNUNET_PeerIdentity *peer, MESH_TunnelNumber tid; struct GNUNET_MESH_ManipulatePath *msg; struct GNUNET_PeerIdentity *pi; - GNUNET_HashCode hash; + struct GNUNET_HashCode hash; struct MeshPeerPath *path; struct MeshPeerInfo *dest_peer_info; struct MeshPeerInfo *orig_peer_info; @@ -2876,27 +5124,42 @@ handle_mesh_path_create (void *cls, const struct GNUNET_PeerIdentity *peer, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " path is for tunnel %s [%X].\n", GNUNET_i2s (pi), tid); t = tunnel_get (pi, tid); - if (NULL == t) + if (NULL == t) // FIXME only for INCOMING tunnels? { + uint32_t opt; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Creating tunnel\n"); - t = GNUNET_malloc (sizeof (struct MeshTunnel)); - t->id.oid = GNUNET_PEER_intern (pi); - t->id.tid = tid; + t = tunnel_new (GNUNET_PEER_intern (pi), tid, NULL, 0); + if (NULL == t) + { + // FIXME notify failure + return GNUNET_OK; + } + opt = ntohl (msg->opt); + t->speed_min = (0 != (opt & MESH_TUNNEL_OPT_SPEED_MIN)) ? + GNUNET_YES : GNUNET_NO; + if (0 != (opt & MESH_TUNNEL_OPT_NOBUFFER)) + { + t->nobuffer = GNUNET_YES; + t->last_fwd_ack = t->fwd_pid + 1; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " speed_min: %d, nobuffer:%d\n", + t->speed_min, t->nobuffer); + + if (GNUNET_YES == t->nobuffer) + { + t->bck_queue_max = 1; + t->fwd_queue_max = 1; + } + + // FIXME only assign a local tid if a local client is interested (on demand) while (NULL != tunnel_get_incoming (next_local_tid)) next_local_tid = (next_local_tid + 1) | GNUNET_MESH_LOCAL_TUNNEL_ID_SERV; t->local_tid_dest = next_local_tid++; next_local_tid = next_local_tid | GNUNET_MESH_LOCAL_TUNNEL_ID_SERV; - t->tree = tree_new (t->id.oid); + // FIXME end - GNUNET_CRYPTO_hash (&t->id, sizeof (struct MESH_TunnelID), &hash); - if (GNUNET_OK != - GNUNET_CONTAINER_multihashmap_put (tunnels, &hash, t, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) - { - tunnel_destroy (t); - GNUNET_break (0); - return GNUNET_OK; - } tunnel_reset_timeout (t); GNUNET_CRYPTO_hash (&t->local_tid_dest, sizeof (MESH_TunnelNumber), &hash); if (GNUNET_OK != @@ -2948,44 +5211,28 @@ handle_mesh_path_create (void *cls, const struct GNUNET_PeerIdentity *peer, /* create path: self not found in path through self */ GNUNET_break_op (0); path_destroy (path); - /* FIXME error. destroy tunnel? leave for timeout? */ - return 0; + tunnel_destroy (t); + return GNUNET_OK; } path_add_to_peers (path, GNUNET_NO); tunnel_add_path (t, path, own_pos); if (own_pos == size - 1) { /* It is for us! Send ack. */ - struct MeshTransmissionDescriptor *info; - unsigned int j; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " It's for us!\n"); peer_info_add_path_to_origin (orig_peer_info, path, GNUNET_NO); if (NULL == t->peers) { - /* New tunnel! Notify clients on data. */ - t->peers = GNUNET_CONTAINER_multihashmap_create (4); + /* New tunnel! Notify clients on first payload message. */ + t->peers = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO); } - GNUNET_break (GNUNET_OK == + GNUNET_break (GNUNET_SYSERR != GNUNET_CONTAINER_multihashmap_put (t->peers, &my_full_id.hashPubKey, peer_info_get (&my_full_id), GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE)); - /* FIXME use send_message */ - info = GNUNET_malloc (sizeof (struct MeshTransmissionDescriptor)); - info->origin = &t->id; - info->peer = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey); - GNUNET_assert (NULL != info->peer); - j = peer_info_transmit_slot (info->peer); - info->handler_n = j; - info->peer->types[j] = GNUNET_MESSAGE_TYPE_MESH_PATH_ACK; - info->peer->infos[j] = info; - info->peer->core_transmit[j] = - GNUNET_CORE_notify_transmit_ready (core_handle, 0, 10, - GNUNET_TIME_UNIT_FOREVER_REL, peer, - sizeof (struct GNUNET_MESH_PathACK), - &send_core_path_ack, info); + send_path_ack (t); } else { @@ -3077,7 +5324,7 @@ handle_mesh_path_destroy (void *cls, const struct GNUNET_PeerIdentity *peer, } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " Own position: %u\n", own_pos); if (own_pos < path->length - 1) - send_message (message, &pi[own_pos + 1]); + send_prebuilt_message (message, &pi[own_pos + 1], t); else send_client_tunnel_disconnect(t, NULL); @@ -3148,25 +5395,43 @@ handle_mesh_tunnel_destroy (void *cls, const struct GNUNET_PeerIdentity *peer, { struct GNUNET_MESH_TunnelDestroy *msg; struct MeshTunnel *t; + GNUNET_PEER_Id parent; + GNUNET_PEER_Id pid; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got a TUNNEL DESTROY packet from %s\n", GNUNET_i2s (peer)); msg = (struct GNUNET_MESH_TunnelDestroy *) message; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " for tunnel %s [%u]\n", + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got a TUNNEL DESTROY packet from %s\n", + GNUNET_i2s (peer)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " for tunnel %s [%u]\n", GNUNET_i2s (&msg->oid), ntohl (msg->tid)); t = tunnel_get (&msg->oid, ntohl (msg->tid)); + /* Check signature */ if (NULL == t) { /* Probably already got the message from another path, * destroyed the tunnel and retransmitted to children. * Safe to ignore. */ + GNUNET_STATISTICS_update (stats, "# control on unknown tunnel", + 1, GNUNET_NO); return GNUNET_OK; } - if (t->id.oid == myid) + parent = tree_get_predecessor (t->tree); + pid = GNUNET_PEER_search (peer); + if (pid != parent) { - GNUNET_break_op (0); - return GNUNET_OK; + unsigned int nc; + + tree_del_peer (t->tree, pid, &tunnel_child_removed, t); + nc = tree_count_children (t->tree); + if (nc > 0 || NULL != t->owner || t->nclients > 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "still in use: %u cl, %u ch\n", + t->nclients, nc); + return GNUNET_OK; + } } if (t->local_tid_dest >= GNUNET_MESH_LOCAL_TUNNEL_ID_SERV) { @@ -3175,8 +5440,9 @@ handle_mesh_tunnel_destroy (void *cls, const struct GNUNET_PeerIdentity *peer, t->local_tid, t->local_tid_dest); send_clients_tunnel_destroy (t); } - tunnel_send_destroy (t); - tunnel_destroy (t); + tunnel_send_destroy (t, parent); + t->destroy = GNUNET_YES; + // TODO: add timeout to destroy the tunnel anyway return GNUNET_OK; } @@ -3199,12 +5465,17 @@ handle_mesh_data_unicast (void *cls, const struct GNUNET_PeerIdentity *peer, unsigned int atsi_count) { struct GNUNET_MESH_Unicast *msg; + struct GNUNET_PeerIdentity *neighbor; + struct MeshTunnelChildInfo *cinfo; struct MeshTunnel *t; - GNUNET_PEER_Id pid; + GNUNET_PEER_Id dest_id; + uint32_t pid; + uint32_t ttl; size_t size; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got a unicast packet from %s\n", GNUNET_i2s (peer)); + /* Check size */ size = ntohs (message->size); if (size < sizeof (struct GNUNET_MESH_Unicast) + @@ -3214,27 +5485,83 @@ handle_mesh_data_unicast (void *cls, const struct GNUNET_PeerIdentity *peer, return GNUNET_OK; } msg = (struct GNUNET_MESH_Unicast *) message; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " of type %u\n", - ntohs (msg[1].header.type)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " of type %s\n", + GNUNET_MESH_DEBUG_M2S (ntohs (msg[1].header.type))); + /* Check tunnel */ t = tunnel_get (&msg->oid, ntohl (msg->tid)); if (NULL == t) { /* TODO notify back: we don't know this tunnel */ + GNUNET_STATISTICS_update (stats, "# data on unknown tunnel", 1, GNUNET_NO); GNUNET_break_op (0); return GNUNET_OK; } + pid = ntohl (msg->pid); + if (t->fwd_pid == pid) + { + GNUNET_STATISTICS_update (stats, "# duplicate PID drops", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + " Already seen pid %u, DROPPING!\n", pid); + return GNUNET_OK; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " pid %u not seen yet, forwarding\n", pid); + } + + t->skip += (pid - t->fwd_pid) - 1; + t->fwd_pid = pid; + + if (GMC_is_pid_bigger (pid, t->last_fwd_ack)) + { + GNUNET_STATISTICS_update (stats, "# unsolicited unicast", 1, GNUNET_NO); + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received PID %u, ACK %u\n", + pid, t->last_fwd_ack); + return GNUNET_OK; + } + tunnel_reset_timeout (t); - pid = GNUNET_PEER_search (&msg->destination); - if (pid == myid) + dest_id = GNUNET_PEER_search (&msg->destination); + if (dest_id == myid) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " it's for us! sending to clients...\n"); - send_subscribed_clients (message, (struct GNUNET_MessageHeader *) &msg[1]); + GNUNET_STATISTICS_update (stats, "# unicast received", 1, GNUNET_NO); + send_subscribed_clients (message, &msg[1].header, t); + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_UNICAST); + return GNUNET_OK; + } + ttl = ntohl (msg->ttl); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " ttl: %u\n", ttl); + if (ttl == 0) + { + GNUNET_STATISTICS_update (stats, "# TTL drops", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, " TTL is 0, DROPPING!\n"); + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_ACK); return GNUNET_OK; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " not for us, retransmitting...\n"); - send_message (message, tree_get_first_hop (t->tree, pid)); + + neighbor = tree_get_first_hop (t->tree, dest_id); + cinfo = tunnel_get_neighbor_fc (t, neighbor); + cinfo->fwd_pid = pid; + GNUNET_CONTAINER_multihashmap_iterate (t->children_fc, + &tunnel_add_skip, + &neighbor); + if (GNUNET_YES == t->nobuffer && + GNUNET_YES == GMC_is_pid_bigger (pid, cinfo->fwd_ack)) + { + GNUNET_STATISTICS_update (stats, "# unsolicited unicast", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, " %u > %u\n", pid, cinfo->fwd_ack); + GNUNET_break_op (0); + return GNUNET_OK; + } + send_prebuilt_message (message, neighbor, t); + GNUNET_STATISTICS_update (stats, "# unicast forwarded", 1, GNUNET_NO); return GNUNET_OK; } @@ -3261,6 +5588,7 @@ handle_mesh_data_multicast (void *cls, const struct GNUNET_PeerIdentity *peer, struct GNUNET_MESH_Multicast *msg; struct MeshTunnel *t; size_t size; + uint32_t pid; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got a multicast packet from %s\n", GNUNET_i2s (peer)); @@ -3277,38 +5605,47 @@ handle_mesh_data_multicast (void *cls, const struct GNUNET_PeerIdentity *peer, if (NULL == t) { /* TODO notify that we dont know that tunnel */ + GNUNET_STATISTICS_update (stats, "# data on unknown tunnel", 1, GNUNET_NO); GNUNET_break_op (0); return GNUNET_OK; } - if (t->mid == ntohl (msg->mid)) + pid = ntohl (msg->pid); + if (t->fwd_pid == pid) { - /* FIXME: already seen this packet, log dropping */ - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - " Already seen mid %u, DROPPING!\n", t->mid); + /* already seen this packet, drop */ + GNUNET_STATISTICS_update (stats, "# duplicate PID drops", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " Already seen pid %u, DROPPING!\n", pid); + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_ACK); return GNUNET_OK; } else { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - " mid %u not seen yet, forwarding\n", ntohl (msg->mid)); + " pid %u not seen yet, forwarding\n", pid); } - t->mid = ntohl (msg->mid); + t->skip += (pid - t->fwd_pid) - 1; + t->fwd_pid = pid; tunnel_reset_timeout (t); /* Transmit to locally interested clients */ if (NULL != t->peers && GNUNET_CONTAINER_multihashmap_contains (t->peers, &my_full_id.hashPubKey)) { - send_subscribed_clients (message, &msg[1].header); + GNUNET_STATISTICS_update (stats, "# multicast received", 1, GNUNET_NO); + send_subscribed_clients (message, &msg[1].header, t); + tunnel_send_fwd_ack(t, GNUNET_MESSAGE_TYPE_MESH_MULTICAST); } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " ttl: %u\n", ntohl (msg->ttl)); if (ntohl (msg->ttl) == 0) { - /* FIXME: ttl is 0, log dropping */ + GNUNET_STATISTICS_update (stats, "# TTL drops", 1, GNUNET_NO); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, " TTL is 0, DROPPING!\n"); + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_ACK); return GNUNET_OK; } - tunnel_send_multicast (t, message, GNUNET_NO); + GNUNET_STATISTICS_update (stats, "# multicast forwarded", 1, GNUNET_NO); + tunnel_send_multicast (t, message); return GNUNET_OK; } @@ -3335,7 +5672,10 @@ handle_mesh_data_to_orig (void *cls, const struct GNUNET_PeerIdentity *peer, struct GNUNET_PeerIdentity id; struct MeshPeerInfo *peer_info; struct MeshTunnel *t; + struct MeshTunnelChildInfo *cinfo; + GNUNET_PEER_Id predecessor; size_t size; + uint32_t pid; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got a ToOrigin packet from %s\n", GNUNET_i2s (peer)); @@ -3347,37 +5687,59 @@ handle_mesh_data_to_orig (void *cls, const struct GNUNET_PeerIdentity *peer, return GNUNET_OK; } msg = (struct GNUNET_MESH_ToOrigin *) message; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " of type %u\n", - ntohs (msg[1].header.type)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " of type %s\n", + GNUNET_MESH_DEBUG_M2S (ntohs (msg[1].header.type))); t = tunnel_get (&msg->oid, ntohl (msg->tid)); + pid = ntohl (msg->pid); if (NULL == t) { /* TODO notify that we dont know this tunnel (whom)? */ - GNUNET_break_op (0); + GNUNET_STATISTICS_update (stats, "# data on unknown tunnel", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received to_origin with PID %u on unknown tunnel %s [%u]\n", + pid, GNUNET_i2s (&msg->oid), ntohl (msg->tid)); + return GNUNET_OK; + } + + cinfo = tunnel_get_neighbor_fc(t, peer); + if (NULL == cinfo) + { + GNUNET_break (0); + return GNUNET_OK; + } + + if (cinfo->bck_pid == pid) + { + /* already seen this packet, drop */ + GNUNET_STATISTICS_update (stats, "# duplicate PID drops BCK", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " Already seen pid %u, DROPPING!\n", pid); + tunnel_send_bck_ack (t, GNUNET_MESSAGE_TYPE_MESH_ACK); return GNUNET_OK; } - if (t->id.oid == myid) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " pid %u not seen yet, forwarding\n", pid); + cinfo->bck_pid = pid; + + if (NULL != t->owner) { char cbuf[size]; struct GNUNET_MESH_ToOrigin *copy; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " it's for us! sending to clients...\n"); - if (NULL == t->owner) - { - /* got data packet for ownerless tunnel */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " no clients!\n"); - GNUNET_break_op (0); - return GNUNET_OK; - } /* TODO signature verification */ memcpy (cbuf, message, size); copy = (struct GNUNET_MESH_ToOrigin *) cbuf; copy->tid = htonl (t->local_tid); + t->bck_pid++; + copy->pid = htonl (t->bck_pid); + GNUNET_STATISTICS_update (stats, "# to origin received", 1, GNUNET_NO); GNUNET_SERVER_notification_context_unicast (nc, t->owner->handle, - ©->header, GNUNET_YES); + ©->header, GNUNET_NO); + tunnel_send_bck_ack (t, GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN); return GNUNET_OK; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -3390,14 +5752,160 @@ handle_mesh_data_to_orig (void *cls, const struct GNUNET_PeerIdentity *peer, GNUNET_break (0); return GNUNET_OK; } - GNUNET_PEER_resolve (tree_get_predecessor (t->tree), &id); - send_message (message, &id); + predecessor = tree_get_predecessor (t->tree); + if (0 == predecessor) + { + if (GNUNET_YES == t->destroy) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "to orig received on a dying tunnel %s [%X]\n", + GNUNET_i2s (&msg->oid), ntohl(msg->tid)); + return GNUNET_OK; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "unknown to origin at %s\n", + GNUNET_i2s (&my_full_id)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "from peer %s\n", + GNUNET_i2s (peer)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "for tunnel %s [%X]\n", + GNUNET_i2s (&msg->oid), ntohl(msg->tid)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "current tree:\n"); + tree_debug (t->tree); + return GNUNET_OK; + } + GNUNET_PEER_resolve (predecessor, &id); + send_prebuilt_message (message, &id, t); + GNUNET_STATISTICS_update (stats, "# to origin forwarded", 1, GNUNET_NO); + + return GNUNET_OK; +} + + +/** + * Core handler for mesh network traffic point-to-point acks. + * + * @param cls closure + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_mesh_ack (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct GNUNET_MESH_ACK *msg; + struct MeshTunnel *t; + uint32_t ack; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got an ACK packet from %s!\n", + GNUNET_i2s (peer)); + msg = (struct GNUNET_MESH_ACK *) message; + + t = tunnel_get (&msg->oid, ntohl (msg->tid)); + + if (NULL == t) + { + /* TODO notify that we dont know this tunnel (whom)? */ + GNUNET_STATISTICS_update (stats, "# ack on unknown tunnel", 1, GNUNET_NO); + return GNUNET_OK; + } + ack = ntohl (msg->pid); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " ACK %u\n", ack); + + /* Is this a forward or backward ACK? */ + if (tree_get_predecessor(t->tree) != GNUNET_PEER_search(peer)) + { + struct MeshTunnelChildInfo *cinfo; + debug_bck_ack++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " FWD ACK\n"); + cinfo = tunnel_get_neighbor_fc (t, peer); + cinfo->fwd_ack = ack; + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_ACK); + tunnel_unlock_fwd_queues (t); + if (GNUNET_SCHEDULER_NO_TASK != cinfo->fc_poll) + { + GNUNET_SCHEDULER_cancel (cinfo->fc_poll); + cinfo->fc_poll = GNUNET_SCHEDULER_NO_TASK; + cinfo->fc_poll_time = GNUNET_TIME_UNIT_SECONDS; + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " BCK ACK\n"); + t->bck_ack = ack; + tunnel_send_bck_ack (t, GNUNET_MESSAGE_TYPE_MESH_ACK); + tunnel_unlock_bck_queue (t); + } return GNUNET_OK; } /** + * Core handler for mesh network traffic point-to-point ack polls. + * + * @param cls closure + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_mesh_poll (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct GNUNET_MESH_Poll *msg; + struct MeshTunnel *t; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got an POLL packet from %s!\n", + GNUNET_i2s (peer)); + + msg = (struct GNUNET_MESH_Poll *) message; + + t = tunnel_get (&msg->oid, ntohl (msg->tid)); + + if (NULL == t) + { + /* TODO notify that we dont know this tunnel (whom)? */ + GNUNET_STATISTICS_update (stats, "# poll on unknown tunnel", 1, GNUNET_NO); + GNUNET_break_op (0); + return GNUNET_OK; + } + + /* Is this a forward or backward ACK? */ + if (tree_get_predecessor(t->tree) != GNUNET_PEER_search(peer)) + { + struct MeshTunnelChildInfo *cinfo; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " from FWD\n"); + cinfo = tunnel_get_neighbor_fc (t, peer); + cinfo->bck_ack = cinfo->fwd_pid; // mark as ready to send + tunnel_send_bck_ack (t, GNUNET_MESSAGE_TYPE_MESH_POLL); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " from BCK\n"); + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_POLL); + } + + return GNUNET_OK; +} + +/** * Core handler for path ACKs * * @param cls closure @@ -3424,14 +5932,31 @@ handle_mesh_path_ack (void *cls, const struct GNUNET_PeerIdentity *peer, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received a path ACK msg [%s]\n", GNUNET_i2s (&my_full_id)); msg = (struct GNUNET_MESH_PathACK *) message; - t = tunnel_get (&msg->oid, msg->tid); + t = tunnel_get (&msg->oid, ntohl(msg->tid)); if (NULL == t) { /* TODO notify that we don't know the tunnel */ + GNUNET_STATISTICS_update (stats, "# control on unknown tunnel", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " don't know the tunnel %s [%X]!\n", + GNUNET_i2s (&msg->oid), ntohl(msg->tid)); return GNUNET_OK; } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " on tunnel %s [%X]\n", + GNUNET_i2s (&msg->oid), ntohl(msg->tid)); peer_info = peer_info_get (&msg->peer_id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by peer %s\n", + GNUNET_i2s (&msg->peer_id)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " via peer %s\n", + GNUNET_i2s (peer)); + + if (NULL != t->regex_search && t->regex_search->peer == peer_info->id) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "connect_by_string completed, stopping search\n"); + regex_cancel_search (t->regex_search); + t->regex_search = NULL; + } /* Add paths to peers? */ p = tree_get_path_to_peer (t->tree, peer_info->id); @@ -3477,12 +6002,57 @@ handle_mesh_path_ack (void *cls, const struct GNUNET_PeerIdentity *peer, GNUNET_break (0); return GNUNET_OK; } - send_message (message, &id); + send_prebuilt_message (message, &id, t); return GNUNET_OK; } /** + * Core handler for mesh keepalives. + * + * @param cls closure + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of records in 'atsi' + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + * + * TODO: Check who we got this from, to validate route. + */ +static int +handle_mesh_keepalive (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct GNUNET_MESH_TunnelKeepAlive *msg; + struct MeshTunnel *t; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got a keepalive packet from %s\n", + GNUNET_i2s (peer)); + + msg = (struct GNUNET_MESH_TunnelKeepAlive *) message; + t = tunnel_get (&msg->oid, ntohl (msg->tid)); + + if (NULL == t) + { + /* TODO notify that we dont know that tunnel */ + GNUNET_STATISTICS_update (stats, "# keepalive on unknown tunnel", 1, + GNUNET_NO); + return GNUNET_OK; + } + + tunnel_reset_timeout (t); + + GNUNET_STATISTICS_update (stats, "# keepalives forwarded", 1, GNUNET_NO); + tunnel_send_multicast (t, message); + return GNUNET_OK; + } + + + +/** * Functions to handle messages from core */ static struct GNUNET_CORE_MessageHandler core_handlers[] = { @@ -3490,10 +6060,17 @@ static struct GNUNET_CORE_MessageHandler core_handlers[] = { {&handle_mesh_path_destroy, GNUNET_MESSAGE_TYPE_MESH_PATH_DESTROY, 0}, {&handle_mesh_path_broken, GNUNET_MESSAGE_TYPE_MESH_PATH_BROKEN, sizeof (struct GNUNET_MESH_PathBroken)}, - {&handle_mesh_tunnel_destroy, GNUNET_MESSAGE_TYPE_MESH_TUNNEL_DESTROY, 0}, + {&handle_mesh_tunnel_destroy, GNUNET_MESSAGE_TYPE_MESH_TUNNEL_DESTROY, + sizeof (struct GNUNET_MESH_TunnelDestroy)}, {&handle_mesh_data_unicast, GNUNET_MESSAGE_TYPE_MESH_UNICAST, 0}, {&handle_mesh_data_multicast, GNUNET_MESSAGE_TYPE_MESH_MULTICAST, 0}, + {&handle_mesh_keepalive, GNUNET_MESSAGE_TYPE_MESH_PATH_KEEPALIVE, + sizeof (struct GNUNET_MESH_TunnelKeepAlive)}, {&handle_mesh_data_to_orig, GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN, 0}, + {&handle_mesh_ack, GNUNET_MESSAGE_TYPE_MESH_ACK, + sizeof (struct GNUNET_MESH_ACK)}, + {&handle_mesh_poll, GNUNET_MESSAGE_TYPE_MESH_POLL, + sizeof (struct GNUNET_MESH_Poll)}, {&handle_mesh_path_ack, GNUNET_MESSAGE_TYPE_MESH_PATH_ACK, sizeof (struct GNUNET_MESH_PathACK)}, {NULL, 0, 0} @@ -3515,11 +6092,11 @@ static struct GNUNET_CORE_MessageHandler core_handlers[] = { * @return GNUNET_OK on success */ static int -deregister_app (void *cls, const GNUNET_HashCode * key, void *value) +deregister_app (void *cls, const struct GNUNET_HashCode * key, void *value) { + struct GNUNET_CONTAINER_MultiHashMap *h = cls; GNUNET_break (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (applications, key, - value)); + GNUNET_CONTAINER_multihashmap_remove (h, key, value)); return GNUNET_OK; } @@ -3568,45 +6145,34 @@ notify_client_connection_failure (void *cls, size_t size, void *buf) * * @param cls Closure (tunnel for which to send the keepalive). * @param tc Notification context. - * - * TODO: implement explicit multicast keepalive? */ static void path_refresh (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct MeshTunnel *t = cls; - struct GNUNET_MessageHeader *payload; - struct GNUNET_MESH_Multicast *msg; - size_t size = - sizeof (struct GNUNET_MESH_Multicast) + - sizeof (struct GNUNET_MessageHeader); + struct GNUNET_MESH_TunnelKeepAlive *msg; + size_t size = sizeof (struct GNUNET_MESH_TunnelKeepAlive); char cbuf[size]; - if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN) + t->path_refresh_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) { return; } - t->path_refresh_task = GNUNET_SCHEDULER_NO_TASK; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "sending keepalive for tunnel %d\n", t->id.tid); - msg = (struct GNUNET_MESH_Multicast *) cbuf; + msg = (struct GNUNET_MESH_TunnelKeepAlive *) cbuf; msg->header.size = htons (size); - msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_MULTICAST); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_PATH_KEEPALIVE); msg->oid = my_full_id; msg->tid = htonl (t->id.tid); - msg->ttl = htonl (DEFAULT_TTL); - msg->mid = htonl (t->mid + 1); - t->mid++; - payload = (struct GNUNET_MessageHeader *) &msg[1]; - payload->size = htons (sizeof (struct GNUNET_MessageHeader)); - payload->type = htons (GNUNET_MESSAGE_TYPE_MESH_PATH_KEEPALIVE); - tunnel_send_multicast (t, &msg->header, GNUNET_YES); + tunnel_send_multicast (t, &msg->header); t->path_refresh_task = - GNUNET_SCHEDULER_add_delayed (REFRESH_PATH_TIME, &path_refresh, t); - return; + GNUNET_SCHEDULER_add_delayed (refresh_path_time, &path_refresh, t); + tunnel_reset_timeout(t); } @@ -3630,7 +6196,7 @@ path_refresh (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) */ static void dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp, - const GNUNET_HashCode * key, + const struct GNUNET_HashCode * key, const struct GNUNET_PeerIdentity *get_path, unsigned int get_path_length, const struct GNUNET_PeerIdentity *put_path, @@ -3678,20 +6244,26 @@ dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp, */ static void dht_get_type_handler (void *cls, struct GNUNET_TIME_Absolute exp, - const GNUNET_HashCode * key, + const struct GNUNET_HashCode * key, const struct GNUNET_PeerIdentity *get_path, unsigned int get_path_length, const struct GNUNET_PeerIdentity *put_path, unsigned int put_path_length, enum GNUNET_BLOCK_Type type, size_t size, const void *data) { - const struct GNUNET_PeerIdentity *pi = data; + const struct PBlock *pb = data; + const struct GNUNET_PeerIdentity *pi = &pb->id; struct MeshTunnel *t = cls; struct MeshPeerInfo *peer_info; struct MeshPeerPath *p; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got type DHT result!\n"); - if (size != sizeof (struct GNUNET_PeerIdentity)) + if (size != sizeof (struct PBlock)) + { + GNUNET_break_op (0); + return; + } + if (ntohl(pb->type) != t->type) { GNUNET_break_op (0); return; @@ -3728,6 +6300,7 @@ handle_local_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client) { struct MeshClient *c; struct MeshClient *next; + unsigned int i; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disconnected\n"); if (client == NULL) @@ -3735,6 +6308,7 @@ handle_local_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " (SERVER DOWN)\n"); return; } + c = clients; while (NULL != c) { @@ -3763,7 +6337,7 @@ handle_local_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client) /* deregister clients applications */ if (NULL != c->apps) { - GNUNET_CONTAINER_multihashmap_iterate (c->apps, &deregister_app, NULL); + GNUNET_CONTAINER_multihashmap_iterate (c->apps, &deregister_app, c->apps); GNUNET_CONTAINER_multihashmap_destroy (c->apps); } if (0 == GNUNET_CONTAINER_multihashmap_size (applications) && @@ -3774,10 +6348,20 @@ handle_local_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client) } if (NULL != c->types) GNUNET_CONTAINER_multihashmap_destroy (c->types); + for (i = 0; i < c->n_regex; i++) + { + GNUNET_free (c->regexes[i].regex); + if (NULL != c->regexes[i].h) + GNUNET_REGEX_announce_cancel (c->regexes[i].h); + } + GNUNET_free_non_null (c->regexes); + if (GNUNET_SCHEDULER_NO_TASK != c->regex_announce_task) + GNUNET_SCHEDULER_cancel (c->regex_announce_task); next = c->next; GNUNET_CONTAINER_DLL_remove (clients, clients_tail, c); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " CLIENT FREE at %p\n", c); GNUNET_free (c); + GNUNET_STATISTICS_update (stats, "# clients", -1, GNUNET_NO); c = next; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " done!\n"); @@ -3829,16 +6413,16 @@ handle_local_new_client (void *cls, struct GNUNET_SERVER_Client *client, if (napps > 0) { GNUNET_MESH_ApplicationType at; - GNUNET_HashCode hc; + struct GNUNET_HashCode hc; - c->apps = GNUNET_CONTAINER_multihashmap_create (napps); + c->apps = GNUNET_CONTAINER_multihashmap_create (napps, GNUNET_NO); for (i = 0; i < napps; i++) { at = ntohl (a[i]); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " app type: %u\n", at); GNUNET_CRYPTO_hash (&at, sizeof (at), &hc); /* store in clients hashmap */ - GNUNET_CONTAINER_multihashmap_put (c->apps, &hc, c, + GNUNET_CONTAINER_multihashmap_put (c->apps, &hc, (void *) (long) at, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); /* store in global hashmap, for announcements */ GNUNET_CONTAINER_multihashmap_put (applications, &hc, c, @@ -3852,10 +6436,10 @@ handle_local_new_client (void *cls, struct GNUNET_SERVER_Client *client, if (ntypes > 0) { uint16_t u16; - GNUNET_HashCode hc; + struct GNUNET_HashCode hc; t = (uint16_t *) & a[napps]; - c->types = GNUNET_CONTAINER_multihashmap_create (ntypes); + c->types = GNUNET_CONTAINER_multihashmap_create (ntypes, GNUNET_NO); for (i = 0; i < ntypes; i++) { u16 = ntohs (t[i]); @@ -3874,10 +6458,11 @@ handle_local_new_client (void *cls, struct GNUNET_SERVER_Client *client, " client has %u+%u subscriptions\n", napps, ntypes); GNUNET_CONTAINER_DLL_insert (clients, clients_tail, c); - c->own_tunnels = GNUNET_CONTAINER_multihashmap_create (32); - c->incoming_tunnels = GNUNET_CONTAINER_multihashmap_create (32); - c->ignore_tunnels = GNUNET_CONTAINER_multihashmap_create (32); + c->own_tunnels = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); + c->incoming_tunnels = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); + c->ignore_tunnels = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); GNUNET_SERVER_notification_context_add (nc, client); + GNUNET_STATISTICS_update (stats, "# clients", 1, GNUNET_NO); GNUNET_SERVER_receive_done (client, GNUNET_OK); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "new client processed\n"); @@ -3885,6 +6470,85 @@ handle_local_new_client (void *cls, struct GNUNET_SERVER_Client *client, /** + * Handler for clients announcing available services by a regular expression. + * + * @param cls closure + * @param client identification of the client + * @param message the actual message, which includes messages the client wants + */ +static void +handle_local_announce_regex (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_MESH_RegexAnnounce *msg; + struct MeshRegexDescriptor rd; + struct MeshClient *c; + char *regex; + size_t len; + size_t offset; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "announce regex started\n"); + + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + msg = (const struct GNUNET_MESH_RegexAnnounce *) message; + + len = ntohs (message->size) - sizeof(struct GNUNET_MESH_RegexAnnounce); + if (NULL != c->partial_regex) + { + regex = c->partial_regex; + offset = strlen (c->partial_regex); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " continuation, already have %u bytes\n", + offset); + } + else + { + regex = NULL; + offset = 0; + } + + regex = GNUNET_realloc (regex, offset + len + 1); + memcpy (®ex[offset], &msg[1], len); + regex[offset + len] = '\0'; + if (0 == ntohs (msg->last)) + { + c->partial_regex = regex; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " not ended, stored %u bytes for later\n", + len); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + rd.regex = regex; + rd.compression = ntohs (msg->compression_characters); + rd.h = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " length %u\n", len); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " regex %s\n", regex); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " compr %u\n", ntohs (rd.compression)); + GNUNET_array_append (c->regexes, c->n_regex, rd); + c->partial_regex = NULL; + if (GNUNET_SCHEDULER_NO_TASK == c->regex_announce_task) + { + c->regex_announce_task = GNUNET_SCHEDULER_add_now (®ex_announce, c); + } + else + { + regex_put (&rd); + } + GNUNET_SERVER_receive_done (client, GNUNET_OK); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "announce regex processed\n"); +} + + +/** * Handler for requests of new tunnels * * @param cls closure @@ -3898,7 +6562,7 @@ handle_local_tunnel_create (void *cls, struct GNUNET_SERVER_Client *client, struct GNUNET_MESH_TunnelMessage *t_msg; struct MeshTunnel *t; struct MeshClient *c; - GNUNET_HashCode hash; + MESH_TunnelNumber tid; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "new tunnel requested\n"); @@ -3921,52 +6585,35 @@ handle_local_tunnel_create (void *cls, struct GNUNET_SERVER_Client *client, t_msg = (struct GNUNET_MESH_TunnelMessage *) message; /* Sanity check for tunnel numbering */ - if (0 == (ntohl (t_msg->tunnel_id) & GNUNET_MESH_LOCAL_TUNNEL_ID_CLI)) + tid = ntohl (t_msg->tunnel_id); + if (0 == (tid & GNUNET_MESH_LOCAL_TUNNEL_ID_CLI)) { GNUNET_break (0); GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } /* Sanity check for duplicate tunnel IDs */ - if (NULL != tunnel_get_by_local_id (c, ntohl (t_msg->tunnel_id))) + if (NULL != tunnel_get_by_local_id (c, tid)) { GNUNET_break (0); GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } - t = GNUNET_malloc (sizeof (struct MeshTunnel)); while (NULL != tunnel_get_by_pi (myid, next_tid)) next_tid = (next_tid + 1) & ~GNUNET_MESH_LOCAL_TUNNEL_ID_CLI; - t->id.tid = next_tid++; - next_tid = next_tid & ~GNUNET_MESH_LOCAL_TUNNEL_ID_CLI; - t->id.oid = myid; - t->local_tid = ntohl (t_msg->tunnel_id); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CREATED TUNNEL %s [%x] (%x)\n", - GNUNET_i2s (&my_full_id), t->id.tid, t->local_tid); - t->owner = c; - t->peers = GNUNET_CONTAINER_multihashmap_create (32); - - GNUNET_CRYPTO_hash (&t->local_tid, sizeof (MESH_TunnelNumber), &hash); - if (GNUNET_OK != - GNUNET_CONTAINER_multihashmap_put (c->own_tunnels, &hash, t, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - - GNUNET_CRYPTO_hash (&t->id, sizeof (struct MESH_TunnelID), &hash); - if (GNUNET_OK != - GNUNET_CONTAINER_multihashmap_put (tunnels, &hash, t, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + t = tunnel_new (myid, next_tid++, c, tid); + if (NULL == t) { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Tunnel creation failed.\n"); GNUNET_break (0); GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } - t->tree = tree_new (myid); + next_tid = next_tid & ~GNUNET_MESH_LOCAL_TUNNEL_ID_CLI; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CREATED TUNNEL %s [%x] (%x)\n", + GNUNET_i2s (&my_full_id), t->id.tid, t->local_tid); + t->peers = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "new tunnel created\n"); GNUNET_SERVER_receive_done (client, GNUNET_OK); @@ -4000,6 +6647,8 @@ handle_local_tunnel_destroy (void *cls, struct GNUNET_SERVER_Client *client, GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + /* Message sanity check */ if (sizeof (struct GNUNET_MESH_TunnelMessage) != ntohs (message->size)) { @@ -4007,7 +6656,7 @@ handle_local_tunnel_destroy (void *cls, struct GNUNET_SERVER_Client *client, GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + tunnel_msg = (struct GNUNET_MESH_TunnelMessage *) message; /* Retrieve tunnel */ @@ -4015,43 +6664,146 @@ handle_local_tunnel_destroy (void *cls, struct GNUNET_SERVER_Client *client, t = tunnel_get_by_local_id(c, tid); if (NULL == t) { - GNUNET_break (0); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, " tunnel %X not found\n", tid); + GNUNET_break (0); GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } if (c != t->owner || tid >= GNUNET_MESH_LOCAL_TUNNEL_ID_SERV) { client_ignore_tunnel (c, t); -#if 0 - // TODO: when to destroy incoming tunnel? - if (t->nclients == 0) - { - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (incoming_tunnels, - &hash, t)); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (t->peers, - &my_full_id.hashPubKey, - t)); - } -#endif + tunnel_destroy_empty (t); GNUNET_SERVER_receive_done (client, GNUNET_OK); return; } - send_client_tunnel_disconnect(t, c); - client_delete_tunnel(c, t); + send_client_tunnel_disconnect (t, c); + client_delete_tunnel (c, t); /* Don't try to ACK the client about the tunnel_destroy multicast packet */ t->owner = NULL; - tunnel_send_destroy (t); - tunnel_destroy (t); + tunnel_send_destroy (t, 0); + t->destroy = GNUNET_YES; + /* The tunnel will be destroyed when the last message is transmitted. */ GNUNET_SERVER_receive_done (client, GNUNET_OK); return; } /** + * Handler for requests of seeting tunnel's speed. + * + * @param cls Closure (unused). + * @param client Identification of the client. + * @param message The actual message. + */ +static void +handle_local_tunnel_speed (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_TunnelMessage *tunnel_msg; + struct MeshClient *c; + struct MeshTunnel *t; + MESH_TunnelNumber tid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got a SPEED request from client!\n"); + + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + tunnel_msg = (struct GNUNET_MESH_TunnelMessage *) message; + + /* Retrieve tunnel */ + tid = ntohl (tunnel_msg->tunnel_id); + t = tunnel_get_by_local_id(c, tid); + if (NULL == t) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, " tunnel %X not found\n", tid); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + switch (ntohs(message->type)) + { + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_MIN: + t->speed_min = GNUNET_YES; + break; + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_MAX: + t->speed_min = GNUNET_NO; + break; + default: + GNUNET_break (0); + } + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Handler for requests of seeting tunnel's buffering policy. + * + * @param cls Closure (unused). + * @param client Identification of the client. + * @param message The actual message. + */ +static void +handle_local_tunnel_buffer (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_TunnelMessage *tunnel_msg; + struct MeshClient *c; + struct MeshTunnel *t; + MESH_TunnelNumber tid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got a BUFFER request from client!\n"); + + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + tunnel_msg = (struct GNUNET_MESH_TunnelMessage *) message; + + /* Retrieve tunnel */ + tid = ntohl (tunnel_msg->tunnel_id); + t = tunnel_get_by_local_id(c, tid); + if (NULL == t) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, " tunnel %X not found\n", tid); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + switch (ntohs(message->type)) + { + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_BUFFER: + t->nobuffer = GNUNET_NO; + break; + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_NOBUFFER: + t->nobuffer = GNUNET_YES; + break; + default: + GNUNET_break (0); + } + + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** * Handler for connection requests to new peers * * @param cls closure @@ -4076,8 +6828,10 @@ handle_local_connect_add (void *cls, struct GNUNET_SERVER_Client *client, GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); peer_msg = (struct GNUNET_MESH_PeerControl *) message; + /* Sanity check for message size */ if (sizeof (struct GNUNET_MESH_PeerControl) != ntohs (peer_msg->header.size)) { @@ -4140,7 +6894,10 @@ handle_local_connect_del (void *cls, struct GNUNET_SERVER_Client *client, GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + peer_msg = (struct GNUNET_MESH_PeerControl *) message; + /* Sanity check for message size */ if (sizeof (struct GNUNET_MESH_PeerControl) != ntohs (peer_msg->header.size)) { @@ -4190,6 +6947,132 @@ handle_local_connect_del (void *cls, struct GNUNET_SERVER_Client *client, return; } +/** + * Handler for blacklist requests of peers in a tunnel + * + * @param cls closure + * @param client identification of the client + * @param message the actual message (PeerControl) + * + * FIXME implement DHT block bloomfilter + */ +static void +handle_local_blacklist (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_PeerControl *peer_msg; + struct MeshClient *c; + struct MeshTunnel *t; + MESH_TunnelNumber tid; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a PEER BLACKLIST request\n"); + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + peer_msg = (struct GNUNET_MESH_PeerControl *) message; + + /* Sanity check for message size */ + if (sizeof (struct GNUNET_MESH_PeerControl) != ntohs (peer_msg->header.size)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Tunnel exists? */ + tid = ntohl (peer_msg->tunnel_id); + t = tunnel_get_by_local_id (c, tid); + if (NULL == t) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " on tunnel %X\n", t->id.tid); + + GNUNET_array_append(t->blacklisted, t->nblacklisted, + GNUNET_PEER_intern(&peer_msg->peer)); +} + + +/** + * Handler for unblacklist requests of peers in a tunnel + * + * @param cls closure + * @param client identification of the client + * @param message the actual message (PeerControl) + */ +static void +handle_local_unblacklist (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_PeerControl *peer_msg; + struct MeshClient *c; + struct MeshTunnel *t; + MESH_TunnelNumber tid; + GNUNET_PEER_Id pid; + unsigned int i; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a PEER UNBLACKLIST request\n"); + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + peer_msg = (struct GNUNET_MESH_PeerControl *) message; + + /* Sanity check for message size */ + if (sizeof (struct GNUNET_MESH_PeerControl) != ntohs (peer_msg->header.size)) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Tunnel exists? */ + tid = ntohl (peer_msg->tunnel_id); + t = tunnel_get_by_local_id (c, tid); + if (NULL == t) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " on tunnel %X\n", t->id.tid); + + /* if peer is not known, complain */ + pid = GNUNET_PEER_search (&peer_msg->peer); + if (0 == pid) + { + GNUNET_break (0); + return; + } + + /* search and remove from list */ + for (i = 0; i < t->nblacklisted; i++) + { + if (t->blacklisted[i] == pid) + { + t->blacklisted[i] = t->blacklisted[t->nblacklisted - 1]; + GNUNET_array_grow (t->blacklisted, t->nblacklisted, t->nblacklisted - 1); + return; + } + } + + /* if peer hasn't been blacklisted, complain */ + GNUNET_break (0); +} + /** * Handler for connection requests to new peers by type @@ -4205,7 +7088,7 @@ handle_local_connect_by_type (void *cls, struct GNUNET_SERVER_Client *client, struct GNUNET_MESH_ConnectPeerByType *connect_msg; struct MeshClient *c; struct MeshTunnel *t; - GNUNET_HashCode hash; + struct GNUNET_HashCode hash; MESH_TunnelNumber tid; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "got connect by type request\n"); @@ -4216,8 +7099,10 @@ handle_local_connect_by_type (void *cls, struct GNUNET_SERVER_Client *client, GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); connect_msg = (struct GNUNET_MESH_ConnectPeerByType *) message; + /* Sanity check for message size */ if (sizeof (struct GNUNET_MESH_ConnectPeerByType) != ntohs (connect_msg->header.size)) @@ -4284,9 +7169,12 @@ handle_local_connect_by_type (void *cls, struct GNUNET_SERVER_Client *client, GNUNET_h2s (&hash)); t->dht_get_type = GNUNET_DHT_get_start (dht_handle, - GNUNET_BLOCK_TYPE_TEST, &hash, 10, + GNUNET_BLOCK_TYPE_MESH_PEER_BY_TYPE, + &hash, + dht_replication_level, GNUNET_DHT_RO_RECORD_ROUTE | - GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, NULL, 0, + GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, + NULL, 0, &dht_get_type_handler, t); GNUNET_SERVER_receive_done (client, GNUNET_OK); @@ -4295,6 +7183,101 @@ handle_local_connect_by_type (void *cls, struct GNUNET_SERVER_Client *client, /** + * Handler for connection requests to new peers by a string service description. + * + * @param cls closure + * @param client identification of the client + * @param message the actual message, which includes messages the client wants + */ +static void +handle_local_connect_by_string (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_ConnectPeerByString *msg; + struct MeshRegexSearchInfo *info; + struct MeshTunnel *t; + struct MeshClient *c; + MESH_TunnelNumber tid; + const char *string; + size_t size; + size_t len; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connect by string started\n"); + msg = (struct GNUNET_MESH_ConnectPeerByString *) message; + size = htons (message->size); + + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + /* Message size sanity check */ + if (sizeof(struct GNUNET_MESH_ConnectPeerByString) >= size) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Tunnel exists? */ + tid = ntohl (msg->tunnel_id); + t = tunnel_get_by_local_id (c, tid); + if (NULL == t) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Does client own tunnel? */ + if (t->owner->handle != client) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " on tunnel %s [%u]\n", + GNUNET_i2s(&my_full_id), + t->id.tid); + + /* Only one connect_by_string allowed at the same time! */ + /* FIXME: allow more, return handle at api level to cancel, document */ + if (NULL != t->regex_search) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + /* Find string itself */ + len = size - sizeof(struct GNUNET_MESH_ConnectPeerByString); + string = (const char *) &msg[1]; + + info = GNUNET_malloc (sizeof (struct MeshRegexSearchInfo)); + info->t = t; + info->description = GNUNET_strndup (string, len); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " string: %s\n", info->description); + + t->regex_search = info; + + info->search_handle = GNUNET_REGEX_search (dht_handle, + info->description, + ®ex_found_handler, info, + stats); + + GNUNET_SERVER_receive_done (client, GNUNET_OK); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "connect by string processed\n"); +} + + +/** * Handler for client traffic directed to one peer * * @param cls closure @@ -4322,7 +7305,10 @@ handle_local_unicast (void *cls, struct GNUNET_SERVER_Client *client, GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + data_msg = (struct GNUNET_MESH_Unicast *) message; + /* Sanity check for message size */ size = ntohs (message->size); if (sizeof (struct GNUNET_MESH_Unicast) + @@ -4361,23 +7347,37 @@ handle_local_unicast (void *cls, struct GNUNET_SERVER_Client *client, return; } + /* PID should be as expected */ + if (ntohl (data_msg->pid) != t->fwd_pid + 1) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unicast PID, expected %u, got %u\n", + t->fwd_pid + 1, ntohl (data_msg->pid)); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + /* Ok, everything is correct, send the message * (pretend we got it from a mesh peer) */ { + /* Work around const limitation */ char buf[ntohs (message->size)] GNUNET_ALIGN; struct GNUNET_MESH_Unicast *copy; - /* Work around const limitation */ copy = (struct GNUNET_MESH_Unicast *) buf; memcpy (buf, data_msg, size); copy->oid = my_full_id; copy->tid = htonl (t->id.tid); + copy->ttl = htonl (default_ttl); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " calling generic handler...\n"); handle_mesh_data_unicast (NULL, &my_full_id, ©->header, NULL, 0); } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "receive done OK\n"); GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; } @@ -4394,12 +7394,14 @@ handle_local_to_origin (void *cls, struct GNUNET_SERVER_Client *client, const struct GNUNET_MessageHeader *message) { struct GNUNET_MESH_ToOrigin *data_msg; - struct GNUNET_PeerIdentity id; + struct MeshTunnelClientInfo *clinfo; struct MeshClient *c; struct MeshTunnel *t; MESH_TunnelNumber tid; size_t size; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got a ToOrigin request from a client!\n"); /* Sanity check for client registration */ if (NULL == (c = client_get (client))) { @@ -4407,7 +7409,10 @@ handle_local_to_origin (void *cls, struct GNUNET_SERVER_Client *client, GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + data_msg = (struct GNUNET_MESH_ToOrigin *) message; + /* Sanity check for message size */ size = ntohs (message->size); if (sizeof (struct GNUNET_MESH_ToOrigin) + @@ -4420,8 +7425,7 @@ handle_local_to_origin (void *cls, struct GNUNET_SERVER_Client *client, /* Tunnel exists? */ tid = ntohl (data_msg->tid); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got a ToOrigin request from a client! Tunnel %X\n", tid); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " on tunnel %X\n", tid); if (tid < GNUNET_MESH_LOCAL_TUNNEL_ID_SERV) { GNUNET_break (0); @@ -4431,19 +7435,34 @@ handle_local_to_origin (void *cls, struct GNUNET_SERVER_Client *client, t = tunnel_get_by_local_id (c, tid); if (NULL == t) { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Tunnel %X unknown.\n", tid); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, " for client %u.\n", c->id); GNUNET_break (0); GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } /* It should be sent by someone who has this as incoming tunnel. */ - if (-1 == client_knows_tunnel (c, t)) + if (GNUNET_NO == client_knows_tunnel (c, t)) { GNUNET_break (0); GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } - GNUNET_PEER_resolve (t->id.oid, &id); + + /* PID should be as expected */ + clinfo = tunnel_get_client_fc (t, c); + if (ntohl (data_msg->pid) != clinfo->bck_pid + 1) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "To Origin PID, expected %u, got %u\n", + clinfo->bck_pid + 1, + ntohl (data_msg->pid)); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + clinfo->bck_pid++; /* Ok, everything is correct, send the message * (pretend we got it from a mesh peer) @@ -4455,14 +7474,18 @@ handle_local_to_origin (void *cls, struct GNUNET_SERVER_Client *client, /* Work around const limitation */ copy = (struct GNUNET_MESH_ToOrigin *) buf; memcpy (buf, data_msg, size); - copy->oid = id; + GNUNET_PEER_resolve (t->id.oid, ©->oid); copy->tid = htonl (t->id.tid); + copy->ttl = htonl (default_ttl); + copy->pid = htonl (t->bck_pid + 1); + copy->sender = my_full_id; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " calling generic handler...\n"); handle_mesh_data_to_orig (NULL, &my_full_id, ©->header, NULL, 0); } GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; } @@ -4493,7 +7516,10 @@ handle_local_multicast (void *cls, struct GNUNET_SERVER_Client *client, GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + data_msg = (struct GNUNET_MESH_Multicast *) message; + /* Sanity check for message size */ if (sizeof (struct GNUNET_MESH_Multicast) + sizeof (struct GNUNET_MessageHeader) > ntohs (data_msg->header.size)) @@ -4509,6 +7535,9 @@ handle_local_multicast (void *cls, struct GNUNET_SERVER_Client *client, if (NULL == t) { GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Tunnel %X unknown.\n", tid); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, " for client %u.\n", c->id); + GNUNET_break (0); GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } @@ -4521,6 +7550,17 @@ handle_local_multicast (void *cls, struct GNUNET_SERVER_Client *client, return; } + /* PID should be as expected */ + if (ntohl (data_msg->pid) != t->fwd_pid + 1) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Multicast PID, expected %u, got %u\n", + t->fwd_pid + 1, ntohl (data_msg->pid)); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + { char buf[ntohs (message->size)] GNUNET_ALIGN; struct GNUNET_MESH_Multicast *copy; @@ -4529,44 +7569,428 @@ handle_local_multicast (void *cls, struct GNUNET_SERVER_Client *client, memcpy (buf, message, ntohs (message->size)); copy->oid = my_full_id; copy->tid = htonl (t->id.tid); - copy->ttl = htonl (DEFAULT_TTL); - copy->mid = htonl (t->mid + 1); + copy->ttl = htonl (default_ttl); + GNUNET_assert (ntohl (copy->pid) == (t->fwd_pid + 1)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " calling generic handler...\n"); handle_mesh_data_multicast (client, &my_full_id, ©->header, NULL, 0); } - /* receive done gets called when last copy is sent to a neighbor */ + GNUNET_SERVER_receive_done (t->owner->handle, GNUNET_OK); + return; +} + + +/** + * Handler for client's ACKs for payload traffic. + * + * @param cls Closure (unused). + * @param client Identification of the client. + * @param message The actual message. + */ +static void +handle_local_ack (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_LocalAck *msg; + struct MeshTunnel *t; + struct MeshClient *c; + MESH_TunnelNumber tid; + uint32_t ack; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a local ACK\n"); + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); + + msg = (struct GNUNET_MESH_LocalAck *) message; + + /* Tunnel exists? */ + tid = ntohl (msg->tunnel_id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " on tunnel %X\n", tid); + t = tunnel_get_by_local_id (c, tid); + if (NULL == t) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Tunnel %X unknown.\n", tid); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, " for client %u.\n", c->id); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + ack = ntohl (msg->max_pid); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " ack %u\n", ack); + + /* Does client own tunnel? I.E: Is this an ACK for BCK traffic? */ + if (NULL != t->owner && t->owner->handle == client) + { + /* The client owns the tunnel, ACK is for data to_origin, send BCK ACK. */ + t->bck_ack = ack; + tunnel_send_bck_ack(t, GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK); + } + else + { + /* The client doesn't own the tunnel, this ACK is for FWD traffic. */ + tunnel_set_client_fwd_ack (t, c, ack); + tunnel_send_fwd_ack (t, GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK); + } + + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; } + +/** + * Iterator over all peers to send a monitoring client info about a tunnel. + * + * @param cls Closure (message being built). + * @param key Key (hashed tunnel ID, unused). + * @param value Peer info. + * + * @return GNUNET_YES, to keep iterating. + */ +static int +monitor_peers_iterator (void *cls, + const struct GNUNET_HashCode * key, + void *value) +{ + struct GNUNET_MESH_LocalMonitor *msg = cls; + struct GNUNET_PeerIdentity *id; + struct MeshPeerInfo *info = value; + + id = (struct GNUNET_PeerIdentity *) &msg[1]; + GNUNET_PEER_resolve (info->id, &id[msg->npeers]); + msg->npeers++; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "* sending info about peer %s [%u]\n", + GNUNET_i2s (&id[msg->npeers - 1]), msg->npeers); + + return GNUNET_YES; +} + + + +/** + * Iterator over all tunnels to send a monitoring client info about each tunnel. + * + * @param cls Closure (client handle). + * @param key Key (hashed tunnel ID, unused). + * @param value Tunnel info. + * + * @return GNUNET_YES, to keep iterating. + */ +static int +monitor_all_tunnels_iterator (void *cls, + const struct GNUNET_HashCode * key, + void *value) +{ + struct GNUNET_SERVER_Client *client = cls; + struct MeshTunnel *t = value; + struct GNUNET_MESH_LocalMonitor *msg; + uint32_t npeers; + + npeers = GNUNET_CONTAINER_multihashmap_size (t->peers); + msg = GNUNET_malloc (sizeof(struct GNUNET_MESH_LocalMonitor) + + npeers * sizeof (struct GNUNET_PeerIdentity)); + GNUNET_PEER_resolve(t->id.oid, &msg->owner); + msg->tunnel_id = htonl (t->id.tid); + msg->header.size = htons (sizeof (struct GNUNET_MESH_LocalMonitor) + + npeers * sizeof (struct GNUNET_PeerIdentity)); + msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_INFO_TUNNELS); + msg->npeers = 0; + (void) GNUNET_CONTAINER_multihashmap_iterate (t->peers, + monitor_peers_iterator, + msg); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "* sending info about tunnel %s [%u] (%u peers)\n", + GNUNET_i2s (&msg->owner), t->id.tid, npeers); + + if (msg->npeers != npeers) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Get tunnels fail: size %u - iter %u\n", + npeers, msg->npeers); + } + + msg->npeers = htonl (npeers); + GNUNET_SERVER_notification_context_unicast (nc, client, + &msg->header, GNUNET_NO); + return GNUNET_YES; +} + + +/** + * Handler for client's MONITOR request. + * + * @param cls Closure (unused). + * @param client Identification of the client. + * @param message The actual message. + */ +static void +handle_local_get_tunnels (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + struct MeshClient *c; + + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received get tunnels request from client %u\n", + c->id); + GNUNET_CONTAINER_multihashmap_iterate (tunnels, + monitor_all_tunnels_iterator, + client); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Get tunnels request from client %u completed\n", + c->id); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Data needed to build a Monitor_Tunnel message. + */ +struct MeshMonitorTunnelContext +{ + /** + * Partial message, including peer count. + */ + struct GNUNET_MESH_LocalMonitor *msg; + + /** + * Hashmap with positions: peer->position. + */ + struct GNUNET_CONTAINER_MultiHashMap *lookup; + + /** + * Index of the parent of each peer in the message, realtive to the absolute + * order in the array (can be in a previous message). + */ + uint32_t parents[1024]; + + /** + * Peers visited so far in the tree, aka position of the current peer. + */ + unsigned int npeers; + + /** + * Client requesting the info. + */ + struct MeshClient *c; +}; + + +/** + * Send a client a message about the structure of a tunnel. + * + * @param ctx Context of the tunnel iteration, with info regarding the state + * of the execution and the number of peers visited for this message. + */ +static void +send_client_tunnel_info (struct MeshMonitorTunnelContext *ctx) +{ + struct GNUNET_MESH_LocalMonitor *resp = ctx->msg; + struct GNUNET_PeerIdentity *pid; + unsigned int *parent; + size_t size; + + size = sizeof (struct GNUNET_MESH_LocalMonitor); + size += (sizeof (struct GNUNET_PeerIdentity) + sizeof (int)) * resp->npeers; + resp->header.size = htons (size); + pid = (struct GNUNET_PeerIdentity *) &resp[1]; + parent = (unsigned int *) &pid[resp->npeers]; + memcpy (parent, ctx->parents, sizeof(uint32_t) * resp->npeers); + GNUNET_SERVER_notification_context_unicast (nc, ctx->c->handle, + &resp->header, GNUNET_NO); +} + +/** + * Iterator over a tunnel tree to build a message containing all peers + * the in the tunnel, including relay nodes. + * + * @param cls Closure (pointer to pointer of message being built). + * @param peer Short ID of a peer. + * @param parent Short ID of the @c peer 's parent. + */ +static void +tunnel_tree_iterator (void *cls, + GNUNET_PEER_Id peer, + GNUNET_PEER_Id parent) +{ + struct MeshMonitorTunnelContext *ctx = cls; + struct GNUNET_MESH_LocalMonitor *msg; + struct GNUNET_PeerIdentity *pid; + struct GNUNET_PeerIdentity ppid; + + msg = ctx->msg; + pid = (struct GNUNET_PeerIdentity *) &msg[1]; + GNUNET_PEER_resolve (peer, &pid[msg->npeers]); + GNUNET_CONTAINER_multihashmap_put (ctx->lookup, + &pid[msg->npeers].hashPubKey, + (void *) (long) ctx->npeers, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + GNUNET_PEER_resolve (parent, &ppid); + ctx->parents[msg->npeers] = + htonl ((long) GNUNET_CONTAINER_multihashmap_get (ctx->lookup, + &ppid.hashPubKey)); + + ctx->npeers++; + msg->npeers++; + + if (sizeof (struct GNUNET_MESH_LocalMonitor) + + (msg->npeers + 1) * + (sizeof (struct GNUNET_PeerIdentity) + sizeof (uint32_t)) + > USHRT_MAX) + { + send_client_tunnel_info (ctx); + msg->npeers = 0; + } +} + + +/** + * Handler for client's MONITOR_TUNNEL request. + * + * @param cls Closure (unused). + * @param client Identification of the client. + * @param message The actual message. + */ +static void +handle_local_show_tunnel (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct GNUNET_MESH_LocalMonitor *msg; + struct GNUNET_MESH_LocalMonitor *resp; + struct MeshMonitorTunnelContext ctx; + struct MeshClient *c; + struct MeshTunnel *t; + + /* Sanity check for client registration */ + if (NULL == (c = client_get (client))) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + + msg = (struct GNUNET_MESH_LocalMonitor *) message; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received tunnel info request from client %u for tunnel %s[%X]\n", + c->id, + &msg->owner, + ntohl (msg->tunnel_id)); + t = tunnel_get (&msg->owner, ntohl (msg->tunnel_id)); + if (NULL == t) + { + /* We don't know the tunnel */ + struct GNUNET_MESH_LocalMonitor warn; + + warn = *msg; + warn.npeers = htonl (UINT_MAX); + GNUNET_SERVER_notification_context_unicast (nc, client, + &warn.header, + GNUNET_NO); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } + + /* Initialize context */ + resp = GNUNET_malloc (USHRT_MAX); /* avoid realloc'ing on each step */ + *resp = *msg; + resp->npeers = 0; + ctx.msg = resp; + ctx.lookup = GNUNET_CONTAINER_multihashmap_create (4 * t->peers_total, + GNUNET_YES); + ctx.c = c; + + /* Collect and send information */ + tree_iterate_all (t->tree, &tunnel_tree_iterator, &ctx); + send_client_tunnel_info (&ctx); + + /* Free context */ + GNUNET_CONTAINER_multihashmap_destroy (ctx.lookup); + GNUNET_free (resp); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Monitor tunnel request from client %u completed\n", + c->id); + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + /** * Functions to handle messages from clients */ static struct GNUNET_SERVER_MessageHandler client_handlers[] = { {&handle_local_new_client, NULL, GNUNET_MESSAGE_TYPE_MESH_LOCAL_CONNECT, 0}, + {&handle_local_announce_regex, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_ANNOUNCE_REGEX, 0}, {&handle_local_tunnel_create, NULL, GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_CREATE, sizeof (struct GNUNET_MESH_TunnelMessage)}, {&handle_local_tunnel_destroy, NULL, GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_DESTROY, sizeof (struct GNUNET_MESH_TunnelMessage)}, + {&handle_local_tunnel_speed, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_MIN, + sizeof (struct GNUNET_MESH_TunnelMessage)}, + {&handle_local_tunnel_speed, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_MAX, + sizeof (struct GNUNET_MESH_TunnelMessage)}, + {&handle_local_tunnel_buffer, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_BUFFER, + sizeof (struct GNUNET_MESH_TunnelMessage)}, + {&handle_local_tunnel_buffer, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_NOBUFFER, + sizeof (struct GNUNET_MESH_TunnelMessage)}, {&handle_local_connect_add, NULL, GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_ADD, sizeof (struct GNUNET_MESH_PeerControl)}, {&handle_local_connect_del, NULL, GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_DEL, sizeof (struct GNUNET_MESH_PeerControl)}, + {&handle_local_blacklist, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_BLACKLIST, + sizeof (struct GNUNET_MESH_PeerControl)}, + {&handle_local_unblacklist, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_UNBLACKLIST, + sizeof (struct GNUNET_MESH_PeerControl)}, {&handle_local_connect_by_type, NULL, GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_ADD_BY_TYPE, sizeof (struct GNUNET_MESH_ConnectPeerByType)}, + {&handle_local_connect_by_string, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_ADD_BY_STRING, 0}, {&handle_local_unicast, NULL, GNUNET_MESSAGE_TYPE_MESH_UNICAST, 0}, {&handle_local_to_origin, NULL, GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN, 0}, {&handle_local_multicast, NULL, GNUNET_MESSAGE_TYPE_MESH_MULTICAST, 0}, + {&handle_local_ack, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK, + sizeof (struct GNUNET_MESH_LocalAck)}, + {&handle_local_get_tunnels, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_INFO_TUNNELS, + sizeof (struct GNUNET_MessageHeader)}, + {&handle_local_show_tunnel, NULL, + GNUNET_MESSAGE_TYPE_MESH_LOCAL_INFO_TUNNEL, + sizeof (struct GNUNET_MESH_LocalMonitor)}, {NULL, NULL, 0, 0} }; @@ -4582,17 +8006,27 @@ static void core_init (void *cls, struct GNUNET_CORE_Handle *server, const struct GNUNET_PeerIdentity *identity) { + static int i = 0; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Core init\n"); core_handle = server; if (0 != memcmp (identity, &my_full_id, sizeof (my_full_id)) || NULL == server) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Wrong CORE service\n")); - GNUNET_SCHEDULER_shutdown (); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + " core id %s\n", + GNUNET_i2s (identity)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + " my id %s\n", + GNUNET_i2s (&my_full_id)); + GNUNET_SCHEDULER_shutdown (); // Try gracefully + if (10 < i++) + GNUNET_abort(); // Try harder } return; } + /** * Method called whenever a given peer connects. * @@ -4627,9 +8061,11 @@ core_connect (void *cls, const struct GNUNET_PeerIdentity *peer, GNUNET_PEER_change_rc (myid, 1); GNUNET_PEER_change_rc (peer_info->id, 1); peer_info_add_path (peer_info, path, GNUNET_YES); + GNUNET_STATISTICS_update (stats, "# peers", 1, GNUNET_NO); return; } + /** * Method called whenever a peer disconnects. * @@ -4640,7 +8076,8 @@ static void core_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer) { struct MeshPeerInfo *pi; - unsigned int i; + struct MeshPeerQueue *q; + struct MeshPeerQueue *n; DEBUG_CONN ("Peer disconnected\n"); pi = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey); @@ -4649,16 +8086,25 @@ core_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer) GNUNET_break (0); return; } - for (i = 0; i < CORE_QUEUE_SIZE; i++) + q = pi->queue_head; + while (NULL != q) { - /* TODO: notify that the transmission failed */ - peer_info_cancel_transmission (pi, i); + n = q->next; + /* TODO try to reroute this traffic instead */ + queue_destroy(q, GNUNET_YES); + q = n; + } + if (NULL != pi->core_transmit) + { + GNUNET_CORE_notify_transmit_ready_cancel(pi->core_transmit); + pi->core_transmit = NULL; } peer_info_remove_path (pi, pi->id, myid); if (myid == pi->id) { DEBUG_CONN (" (self)\n"); } + GNUNET_STATISTICS_update (stats, "# peers", -1, GNUNET_NO); return; } @@ -4677,7 +8123,7 @@ core_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer) * GNUNET_NO if not. */ static int -shutdown_tunnel (void *cls, const GNUNET_HashCode * key, void *value) +shutdown_tunnel (void *cls, const struct GNUNET_HashCode * key, void *value) { struct MeshTunnel *t = value; @@ -4695,14 +8141,27 @@ shutdown_tunnel (void *cls, const GNUNET_HashCode * key, void *value) * GNUNET_NO if not. */ static int -shutdown_peer (void *cls, const GNUNET_HashCode * key, void *value) +shutdown_peer (void *cls, const struct GNUNET_HashCode * key, void *value) { struct MeshPeerInfo *p = value; + struct MeshPeerQueue *q; + struct MeshPeerQueue *n; + q = p->queue_head; + while (NULL != q) + { + n = q->next; + if (q->peer == p) + { + queue_destroy(q, GNUNET_YES); + } + q = n; + } peer_info_destroy (p); return GNUNET_YES; } + /** * Task run during shutdown. * @@ -4719,6 +8178,11 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) GNUNET_CORE_disconnect (core_handle); core_handle = NULL; } + if (NULL != keygen) + { + GNUNET_CRYPTO_rsa_key_create_stop (keygen); + keygen = NULL; + } GNUNET_CONTAINER_multihashmap_iterate (tunnels, &shutdown_tunnel, NULL); GNUNET_CONTAINER_multihashmap_iterate (peers, &shutdown_peer, NULL); if (dht_handle != NULL) @@ -4736,28 +8200,50 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) GNUNET_SCHEDULER_cancel (announce_id_task); announce_id_task = GNUNET_SCHEDULER_NO_TASK; } + if (GNUNET_SCHEDULER_NO_TASK != announce_applications_task) + { + GNUNET_SCHEDULER_cancel (announce_applications_task); + announce_applications_task = GNUNET_SCHEDULER_NO_TASK; + } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "shut down\n"); } + /** - * Process mesh requests. + * Callback for hostkey read/generation * - * @param cls closure - * @param server the initialized server - * @param c configuration to use + * @param cls Closure (Configuration handle). + * @param pk the private key + * @param emsg error message */ static void -run (void *cls, struct GNUNET_SERVER_Handle *server, - const struct GNUNET_CONFIGURATION_Handle *c) +key_generation_cb (void *cls, + struct GNUNET_CRYPTO_RsaPrivateKey *pk, + const char *emsg) { + const struct GNUNET_CONFIGURATION_Handle *c = cls; struct MeshPeerInfo *peer; struct MeshPeerPath *p; - char *keyfile; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "starting to run\n"); - server_handle = server; + keygen = NULL; + if (NULL == pk) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Mesh service could not access hostkey: %s. Exiting.\n"), + emsg); + GNUNET_SCHEDULER_shutdown (); + return; + } + my_private_key = pk; + GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key); + GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), + &my_full_id.hashPubKey); + myid = GNUNET_PEER_intern (&my_full_id); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Mesh for peer [%s] starting\n", + GNUNET_i2s(&my_full_id)); + core_handle = GNUNET_CORE_connect (c, /* Main configuration */ - CORE_QUEUE_SIZE, /* queue size */ NULL, /* Closure passed to MESH functions */ &core_init, /* Call core_init once connected */ &core_connect, /* Handle connects */ @@ -4767,7 +8253,7 @@ run (void *cls, struct GNUNET_SERVER_Handle *server, NULL, /* Don't notify about all outbound messages */ GNUNET_NO, /* For header-only out notification */ core_handlers); /* Register these handlers */ - + if (core_handle == NULL) { GNUNET_break (0); @@ -4775,80 +8261,192 @@ run (void *cls, struct GNUNET_SERVER_Handle *server, return; } + next_tid = 0; + next_local_tid = GNUNET_MESH_LOCAL_TUNNEL_ID_SERV; + + + GNUNET_SERVER_add_handlers (server_handle, client_handlers); + nc = GNUNET_SERVER_notification_context_create (server_handle, 1); + GNUNET_SERVER_disconnect_notify (server_handle, + &handle_local_client_disconnect, NULL); + + + clients = NULL; + clients_tail = NULL; + next_client_id = 0; + + announce_applications_task = GNUNET_SCHEDULER_NO_TASK; + announce_id_task = GNUNET_SCHEDULER_add_now (&announce_id, cls); + + /* Create a peer_info for the local peer */ + peer = peer_info_get (&my_full_id); + p = path_new (1); + p->peers[0] = myid; + GNUNET_PEER_change_rc (myid, 1); + peer_info_add_path (peer, p, GNUNET_YES); + GNUNET_SERVER_resume (server_handle); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Mesh service running\n"); +} + + +/** + * Process mesh requests. + * + * @param cls closure + * @param server the initialized server + * @param c configuration to use + */ +static void +run (void *cls, struct GNUNET_SERVER_Handle *server, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + char *keyfile; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "starting to run\n"); + server_handle = server; + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (c, "GNUNETD", "HOSTKEY", &keyfile)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _ - ("Mesh service is lacking key configuration settings. Exiting.\n")); + ("%s service is lacking key configuration settings (%s). Exiting.\n"), + "mesh", "hostkey"); GNUNET_SCHEDULER_shutdown (); return; } - my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); - GNUNET_free (keyfile); - if (my_private_key == NULL) + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (c, "MESH", "REFRESH_PATH_TIME", + &refresh_path_time)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Mesh service could not access hostkey. Exiting.\n")); + _ + ("%s service is lacking key configuration settings (%s). Exiting.\n"), + "mesh", "refresh path time"); GNUNET_SCHEDULER_shutdown (); return; } - GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key); - GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), - &my_full_id.hashPubKey); - myid = GNUNET_PEER_intern (&my_full_id); -// // transport_handle = GNUNET_TRANSPORT_connect(c, -// // &my_full_id, -// // NULL, -// // NULL, -// // NULL, -// // NULL); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (c, "MESH", "APP_ANNOUNCE_TIME", + &app_announce_time)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("%s service is lacking key configuration settings (%s). Exiting.\n"), + "mesh", "app announce time"); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "APP_ANNOUNCE_TIME %llu ms\n", + app_announce_time.rel_value); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (c, "MESH", "ID_ANNOUNCE_TIME", + &id_announce_time)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("%s service is lacking key configuration settings (%s). Exiting.\n"), + "mesh", "id announce time"); + GNUNET_SCHEDULER_shutdown (); + return; + } - dht_handle = GNUNET_DHT_connect (c, 64); - if (dht_handle == NULL) + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (c, "MESH", "CONNECT_TIMEOUT", + &connect_timeout)) { - GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("%s service is lacking key configuration settings (%s). Exiting.\n"), + "mesh", "connect timeout"); + GNUNET_SCHEDULER_shutdown (); + return; } - next_tid = 0; - next_local_tid = GNUNET_MESH_LOCAL_TUNNEL_ID_SERV; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, "MESH", "MAX_MSGS_QUEUE", + &max_msgs_queue)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("%s service is lacking key configuration settings (%s). Exiting.\n"), + "mesh", "max msgs queue"); + GNUNET_SCHEDULER_shutdown (); + return; + } - tunnels = GNUNET_CONTAINER_multihashmap_create (32); - incoming_tunnels = GNUNET_CONTAINER_multihashmap_create (32); - peers = GNUNET_CONTAINER_multihashmap_create (32); - applications = GNUNET_CONTAINER_multihashmap_create (32); - types = GNUNET_CONTAINER_multihashmap_create (32); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, "MESH", "MAX_TUNNELS", + &max_tunnels)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("%s service is lacking key configuration settings (%s). Exiting.\n"), + "mesh", "max tunnels"); + GNUNET_SCHEDULER_shutdown (); + return; + } - GNUNET_SERVER_add_handlers (server_handle, client_handlers); - nc = GNUNET_SERVER_notification_context_create (server_handle, - LOCAL_QUEUE_SIZE); - GNUNET_SERVER_disconnect_notify (server_handle, - &handle_local_client_disconnect, NULL); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, "MESH", "DEFAULT_TTL", + &default_ttl)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("%s service is lacking key configuration settings (%s). Using default (%u).\n"), + "mesh", "default ttl", 64); + default_ttl = 64; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, "MESH", "MAX_PEERS", + &max_peers)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("%s service is lacking key configuration settings (%s). Using default (%u).\n"), + "mesh", "max peers", 1000); + max_peers = 1000; + } - clients = NULL; - clients_tail = NULL; - next_client_id = 0; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, "MESH", "DHT_REPLICATION_LEVEL", + &dht_replication_level)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ + ("%s service is lacking key configuration settings (%s). Using default (%u).\n"), + "mesh", "dht replication level", 3); + dht_replication_level = 3; + } - announce_applications_task = GNUNET_SCHEDULER_NO_TASK; - announce_id_task = GNUNET_SCHEDULER_add_now (&announce_id, cls); + tunnels = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); + incoming_tunnels = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); + peers = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); + applications = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); + types = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); - /* Create a peer_info for the local peer */ - peer = peer_info_get (&my_full_id); - p = path_new (1); - p->peers[0] = myid; - GNUNET_PEER_change_rc (myid, 1); - peer_info_add_path (peer, p, GNUNET_YES); + dht_handle = GNUNET_DHT_connect (c, 64); + if (NULL == dht_handle) + { + GNUNET_break (0); + } + stats = GNUNET_STATISTICS_create ("mesh", c); + GNUNET_SERVER_suspend (server_handle); /* Scheduled the task to clean up when shutdown is called */ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, NULL); - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "end of run()\n"); + keygen = GNUNET_CRYPTO_rsa_key_create_start (keyfile, + &key_generation_cb, + (void *) c); + GNUNET_free (keyfile); } + /** * The main function for the mesh service. * @@ -4860,13 +8458,19 @@ int main (int argc, char *const *argv) { int ret; + int r; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "main()\n"); - ret = - (GNUNET_OK == - GNUNET_SERVICE_run (argc, argv, "mesh", GNUNET_SERVICE_OPTION_NONE, &run, - NULL)) ? 0 : 1; + r = GNUNET_SERVICE_run (argc, argv, "mesh", GNUNET_SERVICE_OPTION_NONE, &run, + NULL); + ret = (GNUNET_OK == r) ? 0 : 1; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "main() END\n"); + INTERVAL_SHOW; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Mesh for peer [%s] FWD ACKs %u, BCK ACKs %u\n", + GNUNET_i2s(&my_full_id), debug_fwd_ack, debug_bck_ack); + return ret; } diff --git a/src/mesh/mesh.conf.in b/src/mesh/mesh.conf.in index 83a8938..9aa8b89 100644 --- a/src/mesh/mesh.conf.in +++ b/src/mesh/mesh.conf.in @@ -1,13 +1,20 @@ [mesh] AUTOSTART = YES -@UNIXONLY@ PORT = 2096 +@JAVAPORT@PORT = 2096 HOSTNAME = localhost HOME = $SERVICEHOME -CONFIG = $DEFAULTCONFIG BINARY = gnunet-service-mesh ACCEPT_FROM = 127.0.0.1; ACCEPT_FROM6 = ::1; UNIXPATH = /tmp/gnunet-service-mesh.sock UNIX_MATCH_UID = YES UNIX_MATCH_GID = YES - +REFRESH_PATH_TIME = 30 min +APP_ANNOUNCE_TIME = 1 h +ID_ANNOUNCE_TIME = 1 h +CONNECT_TIMEOUT = 30 s +DEFAULT_TTL = 64 +DHT_REPLICATION_LEVEL = 3 +MAX_TUNNELS = 1000 +MAX_MSGS_QUEUE = 10000 +MAX_PEERS=1000
\ No newline at end of file diff --git a/src/mesh/mesh.h b/src/mesh/mesh.h index d8fc404..a5a817c 100644 --- a/src/mesh/mesh.h +++ b/src/mesh/mesh.h @@ -25,10 +25,21 @@ #ifndef MESH_H_ #define MESH_H_ + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + #include <stdint.h> #define MESH_DEBUG GNUNET_YES +#define INITIAL_WINDOW_SIZE 8 +#define ACK_THRESHOLD INITIAL_WINDOW_SIZE / 2 #include "platform.h" #include "gnunet_common.h" @@ -52,20 +63,29 @@ * * tunnel_create GNUNET_MESH_TunnelMessage * tunnel_destroy GNUNET_MESH_TunnelMessage + * tunnel_speed_max GNUNET_MESH_TunnelMessage + * tunnel_speed_min GNUNET_MESH_TunnelMessage + * tunnel_buffer GNUNET_MESH_TunnelMessage * * peer_request_connect_add GNUNET_MESH_PeerControl * peer_request_connect_del GNUNET_MESH_PeerControl * peer_request_connect_by_type GNUNET_MESH_ConnectPeerByType + * peer_request_connect_by_string GNUNET_MESH_ConnectPeerByString + * + * peer_blacklist GNUNET_MESH_PeerControl + * peer_unblacklist GNUNET_MESH_PeerControl * - * notify_transmit_ready *GNUNET_MESH_TransmitReady?* + * notify_transmit_ready None (queue / GNUNET_CLIENT_ntf_tmt_rdy) * notify_transmit_ready_cancel None (clear of internal data structures) * - * - * + * * EVENT MESSAGE USED * ----- ------------ - * data GNUNET_MESH_Data OR - * GNUNET_MESH_DataBroadcast + * data GNUNET_MESH_Unicast OR + * GNUNET_MESH_Multicast OR + * GNUNET_MESH_ToOrigin + * data ack GNUNET_MESH_LocalAck + * * new incoming tunnel GNUNET_MESH_PeerControl * peer connects to a tunnel GNUNET_MESH_PeerControl * peer disconnects from a tunnel GNUNET_MESH_PeerControl @@ -75,11 +95,13 @@ /************************** CONSTANTS ******************************/ /******************************************************************************/ -#define GNUNET_MESH_LOCAL_TUNNEL_ID_CLI 0x80000000 -#define GNUNET_MESH_LOCAL_TUNNEL_ID_SERV 0xB0000000 +#define GNUNET_MESH_LOCAL_TUNNEL_ID_CLI 0x80000000 +#define GNUNET_MESH_LOCAL_TUNNEL_ID_SERV 0xB0000000 + +#define HIGH_PID 0xFFFF0000 +#define LOW_PID 0x0000FFFF -#define CORE_QUEUE_SIZE 10 -#define LOCAL_QUEUE_SIZE 100 +#define PID_OVERFLOW(pid, max) (pid > HIGH_PID && max < LOW_PID) /******************************************************************************/ /************************** MESSAGES ******************************/ @@ -102,7 +124,7 @@ struct GNUNET_MESH_ClientConnect struct GNUNET_MessageHeader header; uint16_t applications GNUNET_PACKED; uint16_t types GNUNET_PACKED; - /* uint16_t list_apps[applications] */ + /* uint32_t list_apps[applications] */ /* uint16_t list_types[types] */ }; @@ -122,6 +144,7 @@ struct GNUNET_MESH_TunnelMessage { /** * Type: GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_[CREATE|DESTROY] + * GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_[MAX|MIN] * * Size: sizeof(struct GNUNET_MESH_TunnelMessage) */ @@ -155,8 +178,39 @@ struct GNUNET_MESH_TunnelNotification * Peer at the other end, if any */ struct GNUNET_PeerIdentity peer; + + /** + * Tunnel options (speed, buffering) + */ + uint32_t opt; +}; + +/** + * Message for announce of regular expressions. + */ +struct GNUNET_MESH_RegexAnnounce +{ + /** + * Type: GNUNET_MESSAGE_TYPE_MESH_LOCAL_ANNOUNCE_REGEX + * + * Size: sizeof(struct GNUNET_MESH_RegexAnnounce) + strlen (regex) + */ + struct GNUNET_MessageHeader header; + + /** + * How many characters do we want to put in an edge label. + */ + uint16_t compression_characters; + + /** + * Is this the last message for this regex? (for regex > 65k) + */ + int16_t last; + + /* regex payload */ }; + /** * Message for: * - request adding and deleting peers from a tunnel @@ -168,49 +222,131 @@ struct GNUNET_MESH_TunnelNotification struct GNUNET_MESH_PeerControl { - /** - * Type: GNUNET_MESSAGE_TYPE_MESH_LOCAL_CONNECT_PEER_[ADD|DEL] - * (client to service, client created tunnel) - * GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_[CONNECTED|DISCONNECTED] - * (service to client) - * - * Size: sizeof(struct GNUNET_MESH_PeerControl) - */ + /** + * Type: GNUNET_MESSAGE_TYPE_MESH_LOCAL_CONNECT_PEER_[ADD|DEL|[UN]BLACKLIST] + * (client to service, client created tunnel) + * GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_[CONNECTED|DISCONNECTED] + * (service to client) + * + * Size: sizeof(struct GNUNET_MESH_PeerControl) + */ struct GNUNET_MessageHeader header; - /** - * ID of a tunnel controlled by this client. - */ + /** + * ID of a tunnel controlled by this client. + */ MESH_TunnelNumber tunnel_id GNUNET_PACKED; - /** - * Peer to connect/disconnect. - */ + /** + * Peer to connect/disconnect. + */ struct GNUNET_PeerIdentity peer; }; /** - * Message for connecting to peers offering a certain service. + * Message for connecting to peers offering a service, by service number. */ struct GNUNET_MESH_ConnectPeerByType { /** * Type: GNUNET_MESSAGE_TYPE_MESH_LOCAL_CONNECT_PEER_BY_TYPE | * GNUNET_MESSAGE_TYPE_MESH_LOCAL_DISCONNECT_PEER_BY_TYPE + * + * Size: sizeof(struct GNUNET_MESH_ConnectPeerByType) */ struct GNUNET_MessageHeader header; + /** + * ID of a tunnel controlled by this client. + */ + MESH_TunnelNumber tunnel_id GNUNET_PACKED; + + /** + * Type specification + */ + GNUNET_MESH_ApplicationType type GNUNET_PACKED; +}; + + +/** + * Message for connecting to peers offering a service, by service string. + */ +struct GNUNET_MESH_ConnectPeerByString +{ + /** + * Type: GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_ADD_BY_STRING + * + * Size: sizeof(struct GNUNET_MESH_ConnectPeerByString) + strlen (string) + */ + struct GNUNET_MessageHeader header; + + /** + * ID of a tunnel controlled by this client. + */ + MESH_TunnelNumber tunnel_id GNUNET_PACKED; + + /* String describing the service */ +}; + + +/** + * Message to allow the client send more data to the service + * (always service -> client). + */ +struct GNUNET_MESH_LocalAck +{ + /** + * Type: GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK + */ + struct GNUNET_MessageHeader header; + + /** + * ID of the tunnel allowed to send more data. + */ + MESH_TunnelNumber tunnel_id GNUNET_PACKED; + + /** + * ID of the last packet allowed. + */ + uint32_t max_pid GNUNET_PACKED; +}; + + +/** + * Message to inform the client about tunnels in the service. + */ +struct GNUNET_MESH_LocalMonitor +{ /** - * ID of a tunnel controlled by this client. + * Type: GNUNET_MESSAGE_TYPE_MESH_LOCAL_MONITOR[_TUNNEL] + */ + struct GNUNET_MessageHeader header; + + /** + * ID of the tunnel allowed to send more data. */ MESH_TunnelNumber tunnel_id GNUNET_PACKED; /** - * Type specification + * Number of peers in the tunnel. */ - GNUNET_MESH_ApplicationType type GNUNET_PACKED; + uint32_t npeers GNUNET_PACKED; + + /** + * Alignment. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * ID of the owner of the tunnel (can be local peer). + */ + struct GNUNET_PeerIdentity owner; + + /* struct GNUNET_PeerIdentity peers[npeers] */ }; + + GNUNET_NETWORK_STRUCT_END /******************************************************************************/ @@ -259,5 +395,60 @@ enum MeshPeerState }; +/** + * Check if one pid is bigger than other, accounting for overflow. + * + * @param bigger Argument that should be bigger. + * @param smaller Argument that should be smaller. + * + * @return True if bigger (arg1) has a higher value than smaller (arg 2). + */ +int +GMC_is_pid_bigger (uint32_t bigger, uint32_t smaller); + + +/** + * Get the higher ACK value out of two values, taking in account overflow. + * + * @param a First ACK value. + * @param b Second ACK value. + * + * @return Highest ACK value from the two. + */ +uint32_t +GMC_max_pid (uint32_t a, uint32_t b); + + +/** + * Get the lower ACK value out of two values, taking in account overflow. + * + * @param a First ACK value. + * @param b Second ACK value. + * + * @return Lowest ACK value from the two. + */ +uint32_t +GMC_min_pid (uint32_t a, uint32_t b); + + +/** + * Convert a message type into a string to help debug + * Generated with: + * FIND: "#define ([^ ]+)[ ]*([0-9]+)" + * REPLACE: " case \2: return "\1"; break;" + * + * @param m Message type. + * + * @return Human readable string description. + */ +const char * +GNUNET_MESH_DEBUG_M2S (uint16_t m); + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif #endif diff --git a/src/mesh/mesh_api.c b/src/mesh/mesh_api.c index de931db..be2ec27 100644 --- a/src/mesh/mesh_api.c +++ b/src/mesh/mesh_api.c @@ -21,12 +21,14 @@ * @author Bartlomiej Polot * * STRUCTURE: - * - CONSTANTS * - DATA STRUCTURES + * - DECLARATIONS * - AUXILIARY FUNCTIONS * - RECEIVE HANDLERS * - SEND FUNCTIONS * - API CALL DEFINITIONS + * + * TODO: add regex to reconnect */ #include "platform.h" #include "gnunet_common.h" @@ -39,6 +41,7 @@ #define LOG(kind,...) GNUNET_log_from (kind, "mesh-api",__VA_ARGS__) +#define DEBUG_ACK GNUNET_YES /******************************************************************************/ /************************ DATA STRUCTURES ****************************/ @@ -92,12 +95,6 @@ struct GNUNET_MESH_TransmitHandle GNUNET_SCHEDULER_TaskIdentifier timeout_task; /** - * Priority of the message. The queue is sorted by priority, - * control messages have the maximum priority (UINT32_MAX). - */ - uint32_t priority; - - /** * Target of the message, 0 for multicast. This field * is only valid if 'notify' is non-NULL. */ @@ -135,9 +132,13 @@ struct GNUNET_MESH_Handle const GNUNET_MESH_ApplicationType *applications; /** - * Double linked list of the tunnels this client is connected to. + * Double linked list of the tunnels this client is connected to, head. */ struct GNUNET_MESH_Tunnel *tunnels_head; + + /** + * Double linked list of the tunnels this client is connected to, tail. + */ struct GNUNET_MESH_Tunnel *tunnels_tail; /** @@ -161,18 +162,29 @@ struct GNUNET_MESH_Handle void *cls; /** - * Messages to send to the service + * Messages to send to the service, head. */ struct GNUNET_MESH_TransmitHandle *th_head; + + /** + * Messages to send to the service, tail. + */ struct GNUNET_MESH_TransmitHandle *th_tail; /** * tid of the next tunnel to create (to avoid reusing IDs often) */ MESH_TunnelNumber next_tid; + + /** + * Number of handlers in the handlers array. + */ unsigned int n_handlers; + + /** + * Number of applications in the applications array. + */ unsigned int n_applications; - unsigned int max_queue_size; /** * Have we started the task to receive messages from the service @@ -180,11 +192,6 @@ struct GNUNET_MESH_Handle */ int in_receive; - /** - * Number of packets queued - */ - unsigned int npackets; - /** * Configuration given by the client, in case of reconnection */ @@ -199,6 +206,41 @@ struct GNUNET_MESH_Handle * Task for trying to reconnect. */ GNUNET_SCHEDULER_TaskIdentifier reconnect_task; + + /** + * Monitor callback + */ + GNUNET_MESH_TunnelsCB tunnels_cb; + + /** + * Monitor callback closure. + */ + void *tunnels_cls; + + /** + * Tunnel callback. + */ + GNUNET_MESH_TunnelCB tunnel_cb; + + /** + * Tunnel callback closure. + */ + void *tunnel_cls; + + /** + * All the peer in the tunnel so far. + */ + struct GNUNET_PeerIdentity *peers; + + /** + * How many peers we have in this tunnel so far. + */ + unsigned int tunnel_npeers; + +#if DEBUG_ACK + unsigned int acks_sent; + unsigned int acks_recv; +#endif }; @@ -232,9 +274,13 @@ struct GNUNET_MESH_Tunnel { /** - * DLL + * DLL next */ struct GNUNET_MESH_Tunnel *next; + + /** + * DLL prev + */ struct GNUNET_MESH_Tunnel *prev; /** @@ -288,23 +334,118 @@ struct GNUNET_MESH_Tunnel unsigned int npeers; /** - * Number of packets queued in this tunnel + * Size of packet queued in this tunnel */ - unsigned int npackets; + unsigned int packet_size; /** * Number of applications requested this tunnel */ unsigned int napps; + /** + * Is the tunnel throttled to the slowest peer? + */ + int speed_min; + + /** + * Is the tunnel allowed to buffer? + */ + int buffering; + + /** + * Next packet ID to send. + */ + uint32_t next_send_pid; + + /** + * Maximum allowed PID to send (ACK recevied). + */ + uint32_t max_send_pid; + + /** + * Last pid received from the service. + */ + uint32_t last_recv_pid; + + /** + * Which ACK value have we last sent to the service? + */ + uint32_t max_recv_pid; }; /******************************************************************************/ +/*********************** DECLARATIONS *************************/ +/******************************************************************************/ + +/** + * Function called to send a message to the service. + * "buf" will be NULL and "size" zero if the socket was closed for writing in + * the meantime. + * + * @param cls closure, the mesh handle + * @param size number of bytes available in buf + * @param buf where the callee should write the connect message + * @return number of bytes written to buf + */ +static size_t +send_callback (void *cls, size_t size, void *buf); + + +/******************************************************************************/ /*********************** AUXILIARY FUNCTIONS *************************/ /******************************************************************************/ /** + * Check if transmission is a payload packet. + * + * @param th Transmission handle. + * + * @return GNUNET_YES if it is a payload packet, + * GNUNET_NO if it is a mesh management packet. + */ +static int +th_is_payload (struct GNUNET_MESH_TransmitHandle *th) +{ + return (th->notify != NULL) ? GNUNET_YES : GNUNET_NO; +} + + +/** + * Check whether there is any message ready in the queue and find the size. + * + * @param h Mesh handle. + * + * @return The size of the first ready message in the queue, + * 0 if there is none. + */ +static size_t +message_ready_size (struct GNUNET_MESH_Handle *h) +{ + struct GNUNET_MESH_TransmitHandle *th; + struct GNUNET_MESH_Tunnel *t; + + for (th = h->th_head; NULL != th; th = th->next) + { + t = th->tunnel; + if (GNUNET_NO == th_is_payload (th)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, " message internal\n"); + return th->size; + } + if (GNUNET_NO == GMC_is_pid_bigger(t->next_send_pid, t->max_send_pid)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, " message payload ok (%u <= %u)\n", + t->next_send_pid, t->max_send_pid); + return th->size; + } + } + return 0; +} + + +/** * Get the tunnel handler for the tunnel specified by id from the given handle * @param h Mesh handle * @param tid ID of the wanted tunnel @@ -354,6 +495,9 @@ create_tunnel (struct GNUNET_MESH_Handle *h, MESH_TunnelNumber tid) { t->tid = tid; } + t->max_send_pid = INITIAL_WINDOW_SIZE - 1; + t->last_recv_pid = (uint32_t) -1; + t->buffering = GNUNET_YES; return t; } @@ -379,6 +523,8 @@ destroy_tunnel (struct GNUNET_MESH_Tunnel *t, int call_cleaner) struct GNUNET_MESH_TransmitHandle *next; unsigned int i; + LOG (GNUNET_ERROR_TYPE_DEBUG, "destroy_tunnel %X\n", t->tid); + if (NULL == t) { GNUNET_break (0); @@ -411,7 +557,7 @@ destroy_tunnel (struct GNUNET_MESH_Tunnel *t, int call_cleaner) continue; /* Clients should have aborted their requests already. * Management traffic should be ok, as clients can't cancel that */ - GNUNET_break (NULL == th->notify); + GNUNET_break (GNUNET_NO == th_is_payload(th)); GNUNET_CONTAINER_DLL_remove (h->th_head, h->th_tail, th); /* clean up request */ @@ -422,7 +568,7 @@ destroy_tunnel (struct GNUNET_MESH_Tunnel *t, int call_cleaner) /* if there are no more pending requests with mesh service, cancel active request */ /* Note: this should be unnecessary... */ - if ( (NULL == h->th_head) && (NULL != h->th)) + if ((0 == message_ready_size (h)) && (NULL != h->th)) { GNUNET_CLIENT_notify_transmit_ready_cancel (h->th); h->th = NULL; @@ -512,6 +658,7 @@ remove_peer_from_tunnel (struct GNUNET_MESH_Peer *p) /** * Notify client that the transmission has timed out + * * @param cls closure * @param tc task context */ @@ -523,12 +670,13 @@ timeout_transmission (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) mesh = th->tunnel->mesh; GNUNET_CONTAINER_DLL_remove (mesh->th_head, mesh->th_tail, th); - if (th->notify != NULL) + th->tunnel->packet_size = 0; + if (GNUNET_YES == th_is_payload (th)) th->notify (th->notify_cls, 0, NULL); GNUNET_free (th); - if ((NULL == mesh->th_head) && (NULL != mesh->th)) + if ((0 == message_ready_size (mesh)) && (NULL != mesh->th)) { - /* queue empty, no point in asking for transmission */ + /* nothing ready to transmit, no point in asking for transmission */ GNUNET_CLIENT_notify_transmit_ready_cancel (mesh->th); mesh->th = NULL; } @@ -536,7 +684,7 @@ timeout_transmission (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) /** - * Add a transmit handle to the transmission queue by priority and set the + * Add a transmit handle to the transmission queue and set the * timeout if needed. * * @param h mesh handle with the queue head and tail @@ -546,16 +694,7 @@ static void add_to_queue (struct GNUNET_MESH_Handle *h, struct GNUNET_MESH_TransmitHandle *th) { - struct GNUNET_MESH_TransmitHandle *p; - - p = h->th_head; - while ((NULL != p) && (th->priority <= p->priority)) - p = p->next; - if (NULL == p) - p = h->th_tail; - else - p = p->prev; - GNUNET_CONTAINER_DLL_insert_after (h->th_head, h->th_tail, p, th); + GNUNET_CONTAINER_DLL_insert_tail (h->th_head, h->th_tail, th); if (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value == th->timeout.abs_value) return; th->timeout_task = @@ -580,6 +719,48 @@ send_packet (struct GNUNET_MESH_Handle *h, /** + * Send an ack on the tunnel to confirm the processing of a message. + * + * @param h Mesh handle. + * @param t Tunnel on which to send the ACK. + */ +static void +send_ack (struct GNUNET_MESH_Handle *h, struct GNUNET_MESH_Tunnel *t) +{ + struct GNUNET_MESH_LocalAck msg; + uint32_t delta; + + delta = t->max_recv_pid - t->last_recv_pid; + if (delta > ACK_THRESHOLD) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Not sending ACK on tunnel %X: ACK: %u, PID: %u, buffer %u\n", + t->tid, t->max_recv_pid, t->last_recv_pid, delta); + return; + } + if (GNUNET_YES == t->buffering) + t->max_recv_pid = t->last_recv_pid + INITIAL_WINDOW_SIZE; + else + t->max_recv_pid = t->last_recv_pid + 1; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending ACK on tunnel %X: %u\n", + t->tid, t->max_recv_pid); + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK); + msg.header.size = htons (sizeof (msg)); + msg.tunnel_id = htonl (t->tid); + msg.max_pid = htonl (t->max_recv_pid); + +#if DEBUG_ACK + t->mesh->acks_sent++; +#endif + + send_packet (h, &msg.header, t); + return; +} + + + +/** * Reconnect callback: tries to reconnect again after a failer previous * reconnecttion * @param cls closure (mesh handle) @@ -620,11 +801,16 @@ send_connect (struct GNUNET_MESH_Handle *h) for (napps = 0; napps < h->n_applications; napps++) { apps[napps] = htonl (h->applications[napps]); - LOG (GNUNET_ERROR_TYPE_DEBUG, " app %u\n", h->applications[napps]); + LOG (GNUNET_ERROR_TYPE_DEBUG, " app %u\n", + h->applications[napps]); } types = (uint16_t *) & apps[napps]; for (ntypes = 0; ntypes < h->n_handlers; ntypes++) + { types[ntypes] = htons (h->message_handlers[ntypes].type); + LOG (GNUNET_ERROR_TYPE_DEBUG, " type %u\n", + h->message_handlers[ntypes].type); + } msg->applications = htons (napps); msg->types = htons (ntypes); LOG (GNUNET_ERROR_TYPE_DEBUG, @@ -652,8 +838,9 @@ do_reconnect (struct GNUNET_MESH_Handle *h) LOG (GNUNET_ERROR_TYPE_DEBUG, "*****************************\n"); LOG (GNUNET_ERROR_TYPE_DEBUG, "******* RECONNECT *******\n"); LOG (GNUNET_ERROR_TYPE_DEBUG, "*****************************\n"); + LOG (GNUNET_ERROR_TYPE_DEBUG, "******** on %p *******\n", h); + LOG (GNUNET_ERROR_TYPE_DEBUG, "*****************************\n"); - h->in_receive = GNUNET_NO; /* disconnect */ if (NULL != h->th) { @@ -675,8 +862,10 @@ do_reconnect (struct GNUNET_MESH_Handle *h) GNUNET_TIME_relative_min (GNUNET_TIME_UNIT_SECONDS, GNUNET_TIME_relative_multiply (h->reconnect_time, 2)); - LOG (GNUNET_ERROR_TYPE_DEBUG, " Next retry in %sms\n", - GNUNET_TIME_relative_to_string (h->reconnect_time)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Next retry in %s\n", + GNUNET_STRINGS_relative_time_to_string (h->reconnect_time, + GNUNET_NO)); GNUNET_break (0); return GNUNET_NO; } @@ -699,6 +888,9 @@ do_reconnect (struct GNUNET_MESH_Handle *h) */ continue; } + t->next_send_pid = 0; + t->max_send_pid = INITIAL_WINDOW_SIZE - 1; + t->last_recv_pid = (uint32_t) -1; tmsg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_CREATE); tmsg.header.size = htons (sizeof (struct GNUNET_MESH_TunnelMessage)); tmsg.tunnel_id = htonl (t->tid); @@ -709,14 +901,13 @@ do_reconnect (struct GNUNET_MESH_Handle *h) pmsg.tunnel_id = htonl (t->tid); /* Reconnect all peers */ - for (i = 0; i < t->npeers; i++) + /* If the tunnel was "by type", dont connect individual peers */ + for (i = 0; i < t->npeers && 0 == t->napps; i++) { GNUNET_PEER_resolve (t->peers[i]->id, &pmsg.peer); if (NULL != t->disconnect_handler && t->peers[i]->connected) t->disconnect_handler (t->cls, &pmsg.peer); - /* If the tunnel was "by type", dont connect individual peers */ - if (0 == t->napps) - send_packet (t->mesh, &pmsg.header, t); + send_packet (t->mesh, &pmsg.header, t); } /* Reconnect all types, if any */ for (i = 0; i < t->napps; i++) @@ -729,6 +920,10 @@ do_reconnect (struct GNUNET_MESH_Handle *h) msg.type = htonl (t->apps[i]); send_packet (t->mesh, &msg.header, t); } + if (GNUNET_NO == t->buffering) + GNUNET_MESH_tunnel_buffer (t, GNUNET_NO); + if (GNUNET_YES == t->speed_min) + GNUNET_MESH_tunnel_speed_min (t); } return GNUNET_YES; } @@ -763,6 +958,7 @@ static void reconnect (struct GNUNET_MESH_Handle *h) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Requested RECONNECT\n"); + h->in_receive = GNUNET_NO; if (GNUNET_SCHEDULER_NO_TASK == h->reconnect_task) h->reconnect_task = GNUNET_SCHEDULER_add_delayed (h->reconnect_time, &reconnect_cbk, h); @@ -787,6 +983,7 @@ process_tunnel_created (struct GNUNET_MESH_Handle *h, MESH_TunnelNumber tid; tid = ntohl (msg->tunnel_id); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Creating incoming tunnel %X\n", tid); if (tid < GNUNET_MESH_LOCAL_TUNNEL_ID_SERV) { GNUNET_break (0); @@ -807,10 +1004,17 @@ process_tunnel_created (struct GNUNET_MESH_Handle *h, GNUNET_PEER_change_rc (t->owner, 1); t->mesh = h; t->tid = tid; + if ((msg->opt & MESH_TUNNEL_OPT_NOBUFFER) != 0) + t->buffering = GNUNET_NO; + else + t->buffering = GNUNET_YES; + if ((msg->opt & MESH_TUNNEL_OPT_SPEED_MIN) != 0) + t->speed_min = GNUNET_YES; atsi.type = 0; atsi.value = 0; + LOG (GNUNET_ERROR_TYPE_DEBUG, " created tunnel %p\n", t); t->ctx = h->new_tunnel (h->cls, t, &msg->peer, &atsi); - LOG (GNUNET_ERROR_TYPE_DEBUG, "new incoming tunnel %X\n", t->tid); + LOG (GNUNET_ERROR_TYPE_DEBUG, "User notified\n"); } else { @@ -936,6 +1140,7 @@ process_incoming_data (struct GNUNET_MESH_Handle *h, struct GNUNET_MESH_ToOrigin *to_orig; struct GNUNET_MESH_Tunnel *t; unsigned int i; + uint32_t pid; uint16_t type; LOG (GNUNET_ERROR_TYPE_DEBUG, "Got a data message!\n"); @@ -948,7 +1153,8 @@ process_incoming_data (struct GNUNET_MESH_Handle *h, t = retrieve_tunnel (h, ntohl (ucast->tid)); payload = (struct GNUNET_MessageHeader *) &ucast[1]; peer = &ucast->oid; - LOG (GNUNET_ERROR_TYPE_DEBUG, " ucast on tunnel %s [%x]\n", + pid = ntohl (ucast->pid); + LOG (GNUNET_ERROR_TYPE_DEBUG, " ucast on tunnel %s [%X]\n", GNUNET_i2s (peer), ntohl (ucast->tid)); break; case GNUNET_MESSAGE_TYPE_MESH_MULTICAST: @@ -956,7 +1162,8 @@ process_incoming_data (struct GNUNET_MESH_Handle *h, t = retrieve_tunnel (h, ntohl (mcast->tid)); payload = (struct GNUNET_MessageHeader *) &mcast[1]; peer = &mcast->oid; - LOG (GNUNET_ERROR_TYPE_DEBUG, " mcast on tunnel %s [%x]\n", + pid = ntohl (mcast->pid); + LOG (GNUNET_ERROR_TYPE_DEBUG, " mcast on tunnel %s [%X]\n", GNUNET_i2s (peer), ntohl (mcast->tid)); break; case GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN: @@ -964,19 +1171,34 @@ process_incoming_data (struct GNUNET_MESH_Handle *h, t = retrieve_tunnel (h, ntohl (to_orig->tid)); payload = (struct GNUNET_MessageHeader *) &to_orig[1]; peer = &to_orig->sender; - LOG (GNUNET_ERROR_TYPE_DEBUG, " torig on tunnel %s [%x]\n", + pid = ntohl (to_orig->pid); + LOG (GNUNET_ERROR_TYPE_DEBUG, " torig on tunnel %s [%X]\n", GNUNET_i2s (peer), ntohl (to_orig->tid)); break; default: GNUNET_break (0); return GNUNET_YES; } + LOG (GNUNET_ERROR_TYPE_DEBUG, " pid %u\n", pid); if (NULL == t) { - /* Tunnel was ignored, probably service didn't get it yet */ + /* Tunnel was ignored/destroyed, probably service didn't get it yet */ + LOG (GNUNET_ERROR_TYPE_DEBUG, " ignored!\n"); return GNUNET_YES; } + if (GNUNET_YES == + GMC_is_pid_bigger(pid, t->max_recv_pid)) + { + GNUNET_break (0); + LOG (GNUNET_ERROR_TYPE_WARNING, + " unauthorized message! (%u, max %u)\n", + pid, t->max_recv_pid); + // FIXME fc what now? accept? reject? + return GNUNET_YES; + } + t->last_recv_pid = pid; type = ntohs (payload->type); + send_ack (h, t); for (i = 0; i < h->n_handlers; i++) { handler = &h->message_handlers[i]; @@ -989,15 +1211,14 @@ process_incoming_data (struct GNUNET_MESH_Handle *h, if (GNUNET_OK != handler->callback (h->cls, t, &t->ctx, peer, payload, &atsi)) { - LOG (GNUNET_ERROR_TYPE_DEBUG, "MESH: callback caused disconnection\n"); + LOG (GNUNET_ERROR_TYPE_DEBUG, "callback caused disconnection\n"); GNUNET_MESH_disconnect (h); return GNUNET_NO; } else { LOG (GNUNET_ERROR_TYPE_DEBUG, - "MESH: callback completed successfully\n"); - + "callback completed successfully\n"); } } } @@ -1006,6 +1227,160 @@ process_incoming_data (struct GNUNET_MESH_Handle *h, /** + * Process a local ACK message, enabling the client to send + * more data to the service. + * + * @param h Mesh handle. + * @param message Message itself. + */ +static void +process_ack (struct GNUNET_MESH_Handle *h, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_LocalAck *msg; + struct GNUNET_MESH_Tunnel *t; + uint32_t ack; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "Got an ACK!\n"); + h->acks_recv++; + msg = (struct GNUNET_MESH_LocalAck *) message; + + t = retrieve_tunnel (h, ntohl (msg->tunnel_id)); + + if (NULL == t) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "ACK on unknown tunnel %X\n", + ntohl (msg->tunnel_id)); + return; + } + ack = ntohl (msg->max_pid); + LOG (GNUNET_ERROR_TYPE_DEBUG, " on tunnel %X, ack %u!\n", t->tid, ack); + if (GNUNET_YES == GMC_is_pid_bigger(ack, t->max_send_pid)) + t->max_send_pid = ack; + else + return; + if (NULL == h->th && 0 < t->packet_size) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, " tmt rdy was NULL, requesting!\n", t->tid, ack); + h->th = + GNUNET_CLIENT_notify_transmit_ready (h->client, t->packet_size, + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_YES, &send_callback, h); + } +} + + +/** + * Process a local reply about info on all tunnels, pass info to the user. + * + * @param h Mesh handle. + * @param message Message itself. + */ +static void +process_get_tunnels (struct GNUNET_MESH_Handle *h, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_LocalMonitor *msg; + uint32_t npeers; + + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Get Tunnels messasge received\n"); + + if (NULL == h->tunnels_cb) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, " ignored\n"); + return; + } + + msg = (struct GNUNET_MESH_LocalMonitor *) message; + npeers = ntohl (msg->npeers); + if (ntohs (message->size) != + (sizeof (struct GNUNET_MESH_LocalMonitor) + + npeers * sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Get tunnels message: size %hu - expected %u (%u peers)\n", + ntohs (message->size), + sizeof (struct GNUNET_MESH_LocalMonitor) + + npeers * sizeof (struct GNUNET_PeerIdentity), + npeers); + return; + } + h->tunnels_cb (h->tunnels_cls, + &msg->owner, + ntohl (msg->tunnel_id), + (struct GNUNET_PeerIdentity *) &msg[1], + npeers); +} + + + +/** + * Process a local monitor_tunnel reply, pass info to the user. + * + * @param h Mesh handle. + * @param message Message itself. + */ +static void +process_show_tunnel (struct GNUNET_MESH_Handle *h, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_MESH_LocalMonitor *msg; + struct GNUNET_PeerIdentity *new_peers; + uint32_t *new_parents; + size_t esize; + uint32_t npeers; + unsigned int i; + + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Show Tunnel messasge received\n"); + + if (NULL == h->tunnel_cb) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, " ignored\n"); + return; + } + + /* Verify message sanity */ + msg = (struct GNUNET_MESH_LocalMonitor *) message; + npeers = ntohl (msg->npeers); + esize = sizeof (struct GNUNET_MESH_LocalMonitor); + esize += npeers * (sizeof (struct GNUNET_PeerIdentity) + sizeof (uint32_t)); + if (ntohs (message->size) != esize) + { + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Show tunnel message: size %hu - expected %u (%u peers)\n", + ntohs (message->size), + esize, + npeers); + + h->tunnel_cb (h->tunnel_cls, NULL, NULL); + h->tunnel_cb = NULL; + h->tunnel_cls = NULL; + h->tunnel_npeers = 0; + GNUNET_free_non_null (h->peers); + h->peers = NULL; + + return; + } + + new_peers = (struct GNUNET_PeerIdentity *) &msg[1]; + new_parents = (uint32_t *) &new_peers[npeers]; + + h->peers = GNUNET_realloc (h->peers, h->tunnel_npeers + npeers); + memcpy (&h->peers[h->tunnel_npeers], + new_peers, + npeers * sizeof (struct GNUNET_PeerIdentity)); + h->tunnel_npeers += npeers; + for (i = 0; i < npeers; i++) + h->tunnel_cb (h->tunnel_cls, + &new_peers[i], + &h->peers[new_parents[i]]); +} + + +/** * Function to process all messages received from the service * * @param cls closure @@ -1018,12 +1393,15 @@ msg_received (void *cls, const struct GNUNET_MessageHeader *msg) if (msg == NULL) { - LOG (GNUNET_ERROR_TYPE_DEBUG, "Received NULL msg\n"); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Mesh service disconnected, reconnecting\n", h); reconnect (h); return; } - LOG (GNUNET_ERROR_TYPE_DEBUG, "received a message type %hu from MESH\n", - ntohs (msg->type)); + LOG (GNUNET_ERROR_TYPE_DEBUG, "\n", + GNUNET_MESH_DEBUG_M2S (ntohs (msg->type))); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Received a message: %s\n", + GNUNET_MESH_DEBUG_M2S (ntohs (msg->type))); switch (ntohs (msg->type)) { /* Notify of a new incoming tunnel */ @@ -1046,15 +1424,32 @@ msg_received (void *cls, const struct GNUNET_MessageHeader *msg) if (GNUNET_NO == process_incoming_data (h, msg)) return; break; - /* We shouldn't get any other packages, log and ignore */ + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK: + process_ack (h, msg); + break; + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_INFO_TUNNELS: + process_get_tunnels (h, msg); + break; + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_INFO_TUNNEL: + process_show_tunnel (h, msg); + break; default: + /* We shouldn't get any other packages, log and ignore */ LOG (GNUNET_ERROR_TYPE_WARNING, - "MESH: unsolicited message form service (type %d)\n", - ntohs (msg->type)); + "unsolicited message form service (type %s)\n", + GNUNET_MESH_DEBUG_M2S (ntohs (msg->type))); } LOG (GNUNET_ERROR_TYPE_DEBUG, "message processed\n"); - GNUNET_CLIENT_receive (h->client, &msg_received, h, - GNUNET_TIME_UNIT_FOREVER_REL); + if (GNUNET_YES == h->in_receive) + { + GNUNET_CLIENT_receive (h->client, &msg_received, h, + GNUNET_TIME_UNIT_FOREVER_REL); + } + else + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "in receive off, not calling CLIENT_receive\n"); + } } @@ -1077,24 +1472,39 @@ send_callback (void *cls, size_t size, void *buf) { struct GNUNET_MESH_Handle *h = cls; struct GNUNET_MESH_TransmitHandle *th; + struct GNUNET_MESH_TransmitHandle *next; + struct GNUNET_MESH_Tunnel *t; char *cbuf = buf; size_t tsize; size_t psize; + size_t nsize; + LOG (GNUNET_ERROR_TYPE_DEBUG, "\n"); LOG (GNUNET_ERROR_TYPE_DEBUG, "Send packet() Buffer %u\n", size); - h->th = NULL; if ((0 == size) || (NULL == buf)) { - LOG (GNUNET_ERROR_TYPE_DEBUG, "Received NULL callback\n"); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Received NULL send callback on %p\n", h); reconnect (h); + h->th = NULL; return 0; } tsize = 0; - while ((NULL != (th = h->th_head)) && (size >= th->size)) + next = h->th_head; + nsize = message_ready_size (h); + while ((NULL != (th = next)) && (0 < nsize) && (size >= nsize)) { - if (NULL != th->notify) + t = th->tunnel; + if (GNUNET_YES == th_is_payload (th)) { - if (th->tunnel->tid >= GNUNET_MESH_LOCAL_TUNNEL_ID_SERV) + LOG (GNUNET_ERROR_TYPE_DEBUG, " payload\n"); + if (GNUNET_YES == GMC_is_pid_bigger(t->next_send_pid, t->max_send_pid)) + { + /* This tunnel is not ready to transmit yet, try next message */ + next = th->next; + continue; + } + t->packet_size = 0; + if (t->tid >= GNUNET_MESH_LOCAL_TUNNEL_ID_SERV) { /* traffic to origin */ struct GNUNET_MESH_ToOrigin to; @@ -1103,15 +1513,17 @@ send_callback (void *cls, size_t size, void *buf) GNUNET_assert (size >= th->size); mh = (struct GNUNET_MessageHeader *) &cbuf[sizeof (to)]; psize = th->notify (th->notify_cls, size - sizeof (to), mh); - LOG (GNUNET_ERROR_TYPE_DEBUG, " to origin, type %u\n", - ntohs (mh->type)); + LOG (GNUNET_ERROR_TYPE_DEBUG, " to origin, type %s\n", + GNUNET_MESH_DEBUG_M2S (ntohs (mh->type))); if (psize > 0) { psize += sizeof (to); GNUNET_assert (size >= psize); to.header.size = htons (psize); to.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN); - to.tid = htonl (th->tunnel->tid); + to.tid = htonl (t->tid); + to.pid = htonl (t->next_send_pid); + to.ttl = 0; memset (&to.oid, 0, sizeof (struct GNUNET_PeerIdentity)); memset (&to.sender, 0, sizeof (struct GNUNET_PeerIdentity)); memcpy (cbuf, &to, sizeof (to)); @@ -1126,16 +1538,16 @@ send_callback (void *cls, size_t size, void *buf) GNUNET_assert (size >= th->size); mh = (struct GNUNET_MessageHeader *) &cbuf[sizeof (mc)]; psize = th->notify (th->notify_cls, size - sizeof (mc), mh); - LOG (GNUNET_ERROR_TYPE_DEBUG, " multicast, type %u\n", - ntohs (mh->type)); + LOG (GNUNET_ERROR_TYPE_DEBUG, " multicast, type %s\n", + GNUNET_MESH_DEBUG_M2S (ntohs (mh->type))); if (psize > 0) { psize += sizeof (mc); GNUNET_assert (size >= psize); mc.header.size = htons (psize); mc.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_MULTICAST); - mc.tid = htonl (th->tunnel->tid); - mc.mid = 0; + mc.tid = htonl (t->tid); + mc.pid = htonl (t->next_send_pid); mc.ttl = 0; memset (&mc.oid, 0, sizeof (struct GNUNET_PeerIdentity)); memcpy (cbuf, &mc, sizeof (mc)); @@ -1150,56 +1562,69 @@ send_callback (void *cls, size_t size, void *buf) GNUNET_assert (size >= th->size); mh = (struct GNUNET_MessageHeader *) &cbuf[sizeof (uc)]; psize = th->notify (th->notify_cls, size - sizeof (uc), mh); - LOG (GNUNET_ERROR_TYPE_DEBUG, " unicast, type %u\n", - ntohs (mh->type)); + LOG (GNUNET_ERROR_TYPE_DEBUG, " unicast, type %s\n", + GNUNET_MESH_DEBUG_M2S (ntohs (mh->type))); if (psize > 0) { psize += sizeof (uc); GNUNET_assert (size >= psize); uc.header.size = htons (psize); uc.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_UNICAST); - uc.tid = htonl (th->tunnel->tid); + uc.tid = htonl (t->tid); + uc.pid = htonl (t->next_send_pid); + uc.ttl = 0; memset (&uc.oid, 0, sizeof (struct GNUNET_PeerIdentity)); GNUNET_PEER_resolve (th->target, &uc.destination); memcpy (cbuf, &uc, sizeof (uc)); } } + t->next_send_pid++; } else { + struct GNUNET_MessageHeader *mh = (struct GNUNET_MessageHeader *) &th[1]; + + LOG (GNUNET_ERROR_TYPE_DEBUG, " mesh traffic, type %s\n", + GNUNET_MESH_DEBUG_M2S (ntohs (mh->type))); memcpy (cbuf, &th[1], th->size); psize = th->size; } if (th->timeout_task != GNUNET_SCHEDULER_NO_TASK) GNUNET_SCHEDULER_cancel (th->timeout_task); - if (NULL != th->notify) - { - th->tunnel->mesh->npackets--; - th->tunnel->npackets--; - } GNUNET_CONTAINER_DLL_remove (h->th_head, h->th_tail, th); GNUNET_free (th); + next = h->th_head; + nsize = message_ready_size (h); cbuf += psize; size -= psize; tsize += psize; } LOG (GNUNET_ERROR_TYPE_DEBUG, " total size: %u\n", tsize); - if (NULL != (th = h->th_head)) + h->th = NULL; + size = message_ready_size (h); + if (0 != size) { - LOG (GNUNET_ERROR_TYPE_DEBUG, " next size: %u\n", th->size); - if (NULL == h->th) - h->th = - GNUNET_CLIENT_notify_transmit_ready (h->client, th->size, - GNUNET_TIME_UNIT_FOREVER_REL, - GNUNET_YES, &send_callback, h); + LOG (GNUNET_ERROR_TYPE_DEBUG, " next size: %u\n", size); + h->th = + GNUNET_CLIENT_notify_transmit_ready (h->client, size, + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_YES, &send_callback, h); + } + else + { + if (NULL != h->th_head) + LOG (GNUNET_ERROR_TYPE_DEBUG, " can't transmit any more\n"); + else + LOG (GNUNET_ERROR_TYPE_DEBUG, " nothing left to transmit\n"); } - LOG (GNUNET_ERROR_TYPE_DEBUG, "Send packet() END\n"); if (GNUNET_NO == h->in_receive) { + LOG (GNUNET_ERROR_TYPE_DEBUG, " start receiving from service\n"); h->in_receive = GNUNET_YES; GNUNET_CLIENT_receive (h->client, &msg_received, h, GNUNET_TIME_UNIT_FOREVER_REL); } + LOG (GNUNET_ERROR_TYPE_DEBUG, "Send packet() END\n"); return tsize; } @@ -1221,16 +1646,19 @@ send_packet (struct GNUNET_MESH_Handle *h, struct GNUNET_MESH_TransmitHandle *th; size_t msize; + LOG (GNUNET_ERROR_TYPE_DEBUG, " Sending message to service: %s\n", + GNUNET_MESH_DEBUG_M2S(ntohs(msg->type))); msize = ntohs (msg->size); th = GNUNET_malloc (sizeof (struct GNUNET_MESH_TransmitHandle) + msize); - th->priority = UINT32_MAX; th->timeout = GNUNET_TIME_UNIT_FOREVER_ABS; th->size = msize; th->tunnel = tunnel; memcpy (&th[1], msg, msize); add_to_queue (h, th); + LOG (GNUNET_ERROR_TYPE_DEBUG, " queued\n"); if (NULL != h->th) return; + LOG (GNUNET_ERROR_TYPE_DEBUG, " calling ntfy tmt rdy for %u bytes\n", msize); h->th = GNUNET_CLIENT_notify_transmit_ready (h->client, msize, GNUNET_TIME_UNIT_FOREVER_REL, @@ -1246,9 +1674,6 @@ send_packet (struct GNUNET_MESH_Handle *h, * Connect to the mesh service. * * @param cfg configuration to use - * @param queue_size size of the data message queue, shared among all tunnels - * (each tunnel is guaranteed to accept at least one message, - * no matter what is the status of other tunnels) * @param cls closure for the various callbacks that follow * (including handlers in the handlers array) * @param new_tunnel function called when an *inbound* tunnel is created @@ -1264,8 +1689,7 @@ send_packet (struct GNUNET_MESH_Handle *h, * (in this case, init is never called) */ struct GNUNET_MESH_Handle * -GNUNET_MESH_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, - unsigned int queue_size, void *cls, +GNUNET_MESH_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, void *cls, GNUNET_MESH_InboundTunnelNotificationHandler new_tunnel, GNUNET_MESH_TunnelEndHandler cleaner, const struct GNUNET_MESH_MessageHandler *handlers, @@ -1275,8 +1699,8 @@ GNUNET_MESH_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, LOG (GNUNET_ERROR_TYPE_DEBUG, "GNUNET_MESH_connect()\n"); h = GNUNET_malloc (sizeof (struct GNUNET_MESH_Handle)); + LOG (GNUNET_ERROR_TYPE_DEBUG, " addr %p\n", h); h->cfg = cfg; - h->max_queue_size = queue_size; h->new_tunnel = new_tunnel; h->cleaner = cleaner; h->client = GNUNET_CLIENT_connect ("mesh", cfg); @@ -1295,8 +1719,12 @@ GNUNET_MESH_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, h->reconnect_task = GNUNET_SCHEDULER_NO_TASK; /* count handlers and apps, calculate size */ - for (h->n_applications = 0; stypes[h->n_applications]; h->n_applications++) ; - for (h->n_handlers = 0; handlers[h->n_handlers].type; h->n_handlers++) ; + for (h->n_applications = 0; + stypes && stypes[h->n_applications]; + h->n_applications++) ; + for (h->n_handlers = 0; + handlers && handlers[h->n_handlers].type; + h->n_handlers++) ; send_connect (h); LOG (GNUNET_ERROR_TYPE_DEBUG, "GNUNET_MESH_connect() END\n"); return h; @@ -1318,6 +1746,13 @@ GNUNET_MESH_disconnect (struct GNUNET_MESH_Handle *handle) struct GNUNET_MESH_Tunnel *aux; struct GNUNET_MESH_TransmitHandle *th; + LOG (GNUNET_ERROR_TYPE_DEBUG, "MESH DISCONNECT\n"); + +#if DEBUG_ACK + LOG (GNUNET_ERROR_TYPE_INFO, "Sent %d ACKs\n", handle->acks_sent); + LOG (GNUNET_ERROR_TYPE_INFO, "Recv %d ACKs\n\n", handle->acks_recv); +#endif + t = handle->tunnels_head; while (NULL != t) { @@ -1337,17 +1772,18 @@ GNUNET_MESH_disconnect (struct GNUNET_MESH_Handle *handle) /* Make sure it is an allowed packet (everything else should have been * already canceled). */ - GNUNET_break (UINT32_MAX == th->priority); - GNUNET_break (NULL == th->notify); + GNUNET_break (GNUNET_NO == th_is_payload (th)); msg = (struct GNUNET_MessageHeader *) &th[1]; switch (ntohs(msg->type)) { case GNUNET_MESSAGE_TYPE_MESH_LOCAL_CONNECT: case GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_DESTROY: + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_INFO_TUNNELS: + case GNUNET_MESSAGE_TYPE_MESH_LOCAL_INFO_TUNNEL: break; default: GNUNET_break (0); - LOG (GNUNET_ERROR_TYPE_DEBUG, "unexpected msg %u\n", + LOG (GNUNET_ERROR_TYPE_ERROR, "unexpected msg %u\n", ntohs(msg->type)); } @@ -1375,6 +1811,55 @@ GNUNET_MESH_disconnect (struct GNUNET_MESH_Handle *handle) /** + * Announce to ther peer the availability of services described by the regex, + * in order to be reachable to other peers via connect_by_string. + * + * Note that the first 8 characters are considered to be part of a prefix, + * (for instance 'gnunet://'). If you put a variable part in there (*, +. ()), + * all matching strings will be stored in the DHT. + * + * @param h Handle to mesh. + * @param regex String with the regular expression describing local services. + * @param compression_characters How many characters can be assigned to one + * edge of the graph. The bigger the variability + * of the data, the smaller this parameter should + * be (down to 1). + * For maximum compression, use strlen (regex) + * or 0 (special value). Use with care! + */ +void +GNUNET_MESH_announce_regex (struct GNUNET_MESH_Handle *h, + const char *regex, + unsigned int compression_characters) +{ + struct GNUNET_MESH_RegexAnnounce *msg; + size_t payload; + size_t len; + size_t msgsize; + size_t offset; + char buffer[UINT16_MAX]; + + len = strlen (regex); + payload = UINT16_MAX - sizeof(struct GNUNET_MESH_RegexAnnounce); + msg = (struct GNUNET_MESH_RegexAnnounce *) buffer; + msg->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_ANNOUNCE_REGEX); + msg->compression_characters = htons (compression_characters); + offset = 0; + do + { + msgsize = (len - offset > payload) ? payload : len - offset; + memcpy (&msg[1], ®ex[offset], msgsize); + offset += msgsize; + msgsize += sizeof(struct GNUNET_MESH_RegexAnnounce); + + msg->header.size = htons (msgsize); + msg->last = htons (offset >= len); + + send_packet (h, &msg->header, NULL); + } while (len > offset); +} + +/** * Create a new tunnel (we're initiator and will be allowed to add/remove peers * and to broadcast). * @@ -1395,6 +1880,8 @@ GNUNET_MESH_tunnel_create (struct GNUNET_MESH_Handle *h, void *tunnel_ctx, LOG (GNUNET_ERROR_TYPE_DEBUG, "Creating new tunnel\n"); t = create_tunnel (h, 0); + LOG (GNUNET_ERROR_TYPE_DEBUG, " at %p\n", t); + LOG (GNUNET_ERROR_TYPE_DEBUG, " number %X\n", t->tid); t->connect_handler = connect_handler; t->disconnect_handler = disconnect_handler; t->cls = handler_cls; @@ -1434,7 +1921,7 @@ GNUNET_MESH_tunnel_destroy (struct GNUNET_MESH_Tunnel *tunnel) { aux = th->next; /* FIXME call the handler? */ - if (NULL != th->notify) + if (GNUNET_YES == th_is_payload (th)) th->notify (th->notify_cls, 0, NULL); GNUNET_CONTAINER_DLL_remove (h->th_head, h->th_tail, th); GNUNET_free (th); @@ -1448,12 +1935,90 @@ GNUNET_MESH_tunnel_destroy (struct GNUNET_MESH_Tunnel *tunnel) send_packet (h, &msg.header, NULL); } +/** + * Request that the tunnel data rate is limited to the speed of the slowest + * receiver. + * + * @param tunnel Tunnel affected. + */ +void +GNUNET_MESH_tunnel_speed_min (struct GNUNET_MESH_Tunnel *tunnel) +{ + struct GNUNET_MESH_TunnelMessage msg; + struct GNUNET_MESH_Handle *h; + + h = tunnel->mesh; + tunnel->speed_min = GNUNET_YES; + + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_MIN); + msg.header.size = htons (sizeof (struct GNUNET_MESH_TunnelMessage)); + msg.tunnel_id = htonl (tunnel->tid); + + send_packet (h, &msg.header, NULL); +} + + +/** + * Request that the tunnel data rate is limited to the speed of the fastest + * receiver. This is the default behavior. + * + * @param tunnel Tunnel affected. + */ +void +GNUNET_MESH_tunnel_speed_max (struct GNUNET_MESH_Tunnel *tunnel) +{ + struct GNUNET_MESH_TunnelMessage msg; + struct GNUNET_MESH_Handle *h; + + h = tunnel->mesh; + tunnel->speed_min = GNUNET_NO; + + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_MAX); + msg.header.size = htons (sizeof (struct GNUNET_MESH_TunnelMessage)); + msg.tunnel_id = htonl (tunnel->tid); + + send_packet (h, &msg.header, NULL); +} + +/** + * Turn on/off the buffering status of the tunnel. + * + * @param tunnel Tunnel affected. + * @param buffer GNUNET_YES to turn buffering on (default), + * GNUNET_NO otherwise. + */ +void +GNUNET_MESH_tunnel_buffer (struct GNUNET_MESH_Tunnel *tunnel, int buffer) +{ + struct GNUNET_MESH_TunnelMessage msg; + struct GNUNET_MESH_Handle *h; + + h = tunnel->mesh; + tunnel->buffering = buffer; + tunnel->max_send_pid = tunnel->next_send_pid; + + if (GNUNET_YES == buffer) + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_BUFFER); + else + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_NOBUFFER); + msg.header.size = htons (sizeof (struct GNUNET_MESH_TunnelMessage)); + msg.tunnel_id = htonl (tunnel->tid); + + send_packet (h, &msg.header, NULL); +} + /** * Request that a peer should be added to the tunnel. The existing * connect handler will be called ONCE with either success or failure. * This function should NOT be called again with the same peer before the * connect handler is called. + * FIXME: I think the above documentation is false. I think it should + * read: "The connect handler will be called once the peer was actually + * successfully added to the multicast group. This function should + * not be called twice for the same peer (unless, of course, + * the peer was removed using GNUNET_MESH_peer_Request_connect_del in + * the meantime). * * @param tunnel handle to existing tunnel * @param peer peer to add @@ -1485,8 +2050,6 @@ GNUNET_MESH_peer_request_connect_add (struct GNUNET_MESH_Tunnel *tunnel, msg.tunnel_id = htonl (tunnel->tid); msg.peer = *peer; send_packet (tunnel->mesh, &msg.header, tunnel); - - return; } @@ -1560,16 +2123,98 @@ GNUNET_MESH_peer_request_connect_by_type (struct GNUNET_MESH_Tunnel *tunnel, /** + * Request that the mesh should try to connect to a peer matching the + * description given in the service string. + * + * FIXME: allow multiple? how to deal with reconnect? + * + * @param tunnel handle to existing tunnel + * @param description string describing the destination node requirements + */ +void +GNUNET_MESH_peer_request_connect_by_string (struct GNUNET_MESH_Tunnel *tunnel, + const char *description) +{ + struct GNUNET_MESH_ConnectPeerByString *m; + size_t len; + size_t msgsize; + + len = strlen (description); + msgsize = sizeof(struct GNUNET_MESH_ConnectPeerByString) + len; + GNUNET_assert (UINT16_MAX > msgsize); + { + char buffer[msgsize]; + + m = (struct GNUNET_MESH_ConnectPeerByString *) buffer; + m->header.size = htons (msgsize); + m->header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_ADD_BY_STRING); + m->tunnel_id = htonl (tunnel->tid); + memcpy(&m[1], description, len); + + send_packet (tunnel->mesh, &m->header, tunnel); + } +} + + +/** + * Request that the given peer isn't added to this tunnel in calls to + * connect_by_* calls, (due to misbehaviour, bad performance, ...). + * + * @param tunnel handle to existing tunnel. + * @param peer peer identity of the peer which should be blacklisted + * for the tunnel. + */ +void +GNUNET_MESH_peer_blacklist (struct GNUNET_MESH_Tunnel *tunnel, + const struct GNUNET_PeerIdentity *peer) +{ + struct GNUNET_MESH_PeerControl msg; + + msg.header.size = htons (sizeof (struct GNUNET_MESH_PeerControl)); + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_BLACKLIST); + msg.tunnel_id = htonl (tunnel->tid); + msg.peer = *peer; + send_packet (tunnel->mesh, &msg.header, tunnel); + + return; +} + + +/** + * Request that the given peer isn't blacklisted anymore from this tunnel, + * and therefore can be added in future calls to connect_by_*. + * The peer must have been previously blacklisted for this tunnel. + * + * @param tunnel handle to existing tunnel. + * @param peer peer identity of the peer which shouldn't be blacklisted + * for the tunnel anymore. + */ +void +GNUNET_MESH_peer_unblacklist (struct GNUNET_MESH_Tunnel *tunnel, + const struct GNUNET_PeerIdentity *peer) +{ + struct GNUNET_MESH_PeerControl msg; + + msg.header.size = htons (sizeof (struct GNUNET_MESH_PeerControl)); + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_UNBLACKLIST); + msg.tunnel_id = htonl (tunnel->tid); + msg.peer = *peer; + send_packet (tunnel->mesh, &msg.header, tunnel); + + return; +} + + +/** * Ask the mesh to call "notify" once it is ready to transmit the - * given number of bytes to the specified "target". If we are not yet - * connected to the specified peer, a call to this function will cause - * us to try to establish a connection. + * given number of bytes to the specified tunnel or target. + * Only one call can be active at any time, to issue another request, + * wait for the callback or cancel the current request. * * @param tunnel tunnel to use for transmission * @param cork is corking allowed for this transmission? - * @param priority how important is the message? * @param maxdelay how long can the message wait? - * @param target destination for the message, + * @param target destination for the message * NULL for multicast to all tunnel targets * @param notify_size how many bytes of buffer space does notify want? * @param notify function to call when buffer space is available; @@ -1583,7 +2228,6 @@ GNUNET_MESH_peer_request_connect_by_type (struct GNUNET_MESH_Tunnel *tunnel, */ struct GNUNET_MESH_TransmitHandle * GNUNET_MESH_notify_transmit_ready (struct GNUNET_MESH_Tunnel *tunnel, int cork, - uint32_t priority, struct GNUNET_TIME_Relative maxdelay, const struct GNUNET_PeerIdentity *target, size_t notify_size, @@ -1591,53 +2235,22 @@ GNUNET_MESH_notify_transmit_ready (struct GNUNET_MESH_Tunnel *tunnel, int cork, void *notify_cls) { struct GNUNET_MESH_TransmitHandle *th; - struct GNUNET_MESH_TransmitHandle *least_priority_th; - uint32_t least_priority; size_t overhead; GNUNET_assert (NULL != tunnel); - LOG (GNUNET_ERROR_TYPE_DEBUG, "mesh notify transmit ready called\n"); - if (NULL != target) + LOG (GNUNET_ERROR_TYPE_DEBUG, "MESH NOTIFY TRANSMIT READY\n"); + LOG (GNUNET_ERROR_TYPE_DEBUG, " on tunnel %X\n", tunnel->tid); + if (tunnel->tid >= GNUNET_MESH_LOCAL_TUNNEL_ID_SERV) + LOG (GNUNET_ERROR_TYPE_DEBUG, " to origin\n"); + else if (NULL != target) LOG (GNUNET_ERROR_TYPE_DEBUG, " target %s\n", GNUNET_i2s (target)); else LOG (GNUNET_ERROR_TYPE_DEBUG, " target multicast\n"); + LOG (GNUNET_ERROR_TYPE_DEBUG, " payload size %u\n", notify_size); GNUNET_assert (NULL != notify); - if (tunnel->mesh->npackets >= tunnel->mesh->max_queue_size && - tunnel->npackets > 0) - { - /* queue full */ - if (0 == priority) - return NULL; - th = tunnel->mesh->th_tail; - least_priority = priority; - least_priority_th = NULL; - while (NULL != th) - { - if (th->priority < least_priority && th->tunnel->npackets > 1) - { - least_priority_th = th; - least_priority = th->priority; - } - th = th->prev; - } - if (NULL == least_priority_th) - return NULL; - /* Can't be a control message */ - GNUNET_assert (NULL != least_priority_th->notify); - least_priority_th->notify (notify_cls, 0, NULL); - least_priority_th->tunnel->npackets--; - tunnel->mesh->npackets--; - GNUNET_CONTAINER_DLL_remove (tunnel->mesh->th_head, tunnel->mesh->th_tail, - least_priority_th); - if (GNUNET_SCHEDULER_NO_TASK != least_priority_th->timeout_task) - GNUNET_SCHEDULER_cancel (least_priority_th->timeout_task); - GNUNET_free (least_priority_th); - } - tunnel->npackets++; - tunnel->mesh->npackets++; + GNUNET_assert (0 == tunnel->packet_size); // Only one data packet allowed th = GNUNET_malloc (sizeof (struct GNUNET_MESH_TransmitHandle)); th->tunnel = tunnel; - th->priority = priority; th->timeout = GNUNET_TIME_relative_to_absolute (maxdelay); th->target = GNUNET_PEER_intern (target); if (tunnel->tid >= GNUNET_MESH_LOCAL_TUNNEL_ID_SERV) @@ -1646,17 +2259,22 @@ GNUNET_MESH_notify_transmit_ready (struct GNUNET_MESH_Tunnel *tunnel, int cork, overhead = sizeof (struct GNUNET_MESH_Multicast); else overhead = sizeof (struct GNUNET_MESH_Unicast); - th->size = notify_size + overhead; + tunnel->packet_size = th->size = notify_size + overhead; + LOG (GNUNET_ERROR_TYPE_DEBUG, " total size %u\n", th->size); th->notify = notify; th->notify_cls = notify_cls; add_to_queue (tunnel->mesh, th); if (NULL != tunnel->mesh->th) return th; + if (GMC_is_pid_bigger(tunnel->next_send_pid, tunnel->max_send_pid)) + return th; + LOG (GNUNET_ERROR_TYPE_DEBUG, " call notify tmt rdy\n"); tunnel->mesh->th = GNUNET_CLIENT_notify_transmit_ready (tunnel->mesh->client, th->size, GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES, &send_callback, tunnel->mesh); + LOG (GNUNET_ERROR_TYPE_DEBUG, "MESH NOTIFY TRANSMIT READY END\n"); return th; } @@ -1671,12 +2289,13 @@ GNUNET_MESH_notify_transmit_ready_cancel (struct GNUNET_MESH_TransmitHandle *th) { struct GNUNET_MESH_Handle *mesh; + th->tunnel->packet_size = 0; mesh = th->tunnel->mesh; if (th->timeout_task != GNUNET_SCHEDULER_NO_TASK) GNUNET_SCHEDULER_cancel (th->timeout_task); GNUNET_CONTAINER_DLL_remove (mesh->th_head, mesh->th_tail, th); GNUNET_free (th); - if ((NULL == mesh->th_head) && (NULL != mesh->th)) + if ((0 == message_ready_size (mesh)) && (NULL != mesh->th)) { /* queue empty, no point in asking for transmission */ GNUNET_CLIENT_notify_transmit_ready_cancel (mesh->th); @@ -1686,6 +2305,91 @@ GNUNET_MESH_notify_transmit_ready_cancel (struct GNUNET_MESH_TransmitHandle *th) /** + * Request information about the running mesh peer. + * The callback will be called for every tunnel known to the service, + * listing all active peers that blong to the tunnel. + * + * If called again on the same handle, it will overwrite the previous + * callback and cls. To retrieve the cls, monitor_cancel must be + * called first. + * + * WARNING: unstable API, likely to change in the future! + * + * @param h Handle to the mesh peer. + * @param callback Function to call with the requested data. + * @param callback_cls Closure for @c callback. + */ +void +GNUNET_MESH_get_tunnels (struct GNUNET_MESH_Handle *h, + GNUNET_MESH_TunnelsCB callback, + void *callback_cls) +{ + struct GNUNET_MessageHeader msg; + + msg.size = htons (sizeof (msg)); + msg.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_INFO_TUNNELS); + send_packet (h, &msg, NULL); + h->tunnels_cb = callback; + h->tunnels_cls = callback_cls; + + return; +} + + +/** + * Cancel a monitor request. The monitor callback will not be called. + * + * @param h Mesh handle. + * + * @return Closure given to GNUNET_MESH_monitor, if any. + */ +void * +GNUNET_MESH_get_tunnels_cancel (struct GNUNET_MESH_Handle *h) +{ + void *cls; + + cls = h->tunnels_cls; + h->tunnels_cb = NULL; + h->tunnels_cls = NULL; + return cls; +} + + +/** + * Request information about a specific tunnel of the running mesh peer. + * + * WARNING: unstable API, likely to change in the future! + * + * @param h Handle to the mesh peer. + * @param initiator ID of the owner of the tunnel. + * @param tunnel_number Tunnel number. + * @param callback Function to call with the requested data. + * @param callback_cls Closure for @c callback. + */ +void +GNUNET_MESH_show_tunnel (struct GNUNET_MESH_Handle *h, + struct GNUNET_PeerIdentity *initiator, + unsigned int tunnel_number, + GNUNET_MESH_TunnelCB callback, + void *callback_cls) +{ + struct GNUNET_MESH_LocalMonitor msg; + + msg.header.size = htons (sizeof (msg)); + msg.header.type = htons (GNUNET_MESSAGE_TYPE_MESH_LOCAL_INFO_TUNNEL); + msg.npeers = htonl (0); + msg.owner = *initiator; + msg.tunnel_id = htonl (tunnel_number); + msg.reserved = 0; + send_packet (h, &msg.header, NULL); + h->tunnel_cb = callback; + h->tunnel_cls = callback_cls; + + return; +} + + +/** * Transition API for tunnel ctx management */ void diff --git a/src/mesh/mesh_common.c b/src/mesh/mesh_common.c new file mode 100644 index 0000000..c9af35c --- /dev/null +++ b/src/mesh/mesh_common.c @@ -0,0 +1,250 @@ +/* + 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 mesh/mesh_common.c + * @brief MESH helper functions + * @author Bartlomiej Polot + */ + +#include "mesh.h" + + +/** + * Check if one pid is bigger than other, accounting for overflow. + * + * @param bigger Argument that should be bigger. + * @param smaller Argument that should be smaller. + * + * @return True if bigger (arg1) has a higher value than smaller (arg 2). + */ +int +GMC_is_pid_bigger (uint32_t bigger, uint32_t smaller) +{ + return (GNUNET_YES == PID_OVERFLOW(smaller, bigger) || + (bigger > smaller && GNUNET_NO == PID_OVERFLOW(bigger, smaller))); +} + +/** + * Get the higher ACK value out of two values, taking in account overflow. + * + * @param a First ACK value. + * @param b Second ACK value. + * + * @return Highest ACK value from the two. + */ +uint32_t +GMC_max_pid (uint32_t a, uint32_t b) +{ + if (GMC_is_pid_bigger(a, b)) + return a; + return b; +} + + +/** + * Get the lower ACK value out of two values, taking in account overflow. + * + * @param a First ACK value. + * @param b Second ACK value. + * + * @return Lowest ACK value from the two. + */ +uint32_t +GMC_min_pid (uint32_t a, uint32_t b) +{ + if (GMC_is_pid_bigger(a, b)) + return b; + return a; +} + + +#if !defined(GNUNET_CULL_LOGGING) +const char * +GNUNET_MESH_DEBUG_M2S (uint16_t m) +{ + static char buf[32]; + switch (m) + { + /** + * Request the creation of a path + */ + case 256: return "GNUNET_MESSAGE_TYPE_MESH_PATH_CREATE"; + + /** + * Request the modification of an existing path + */ + case 257: return "GNUNET_MESSAGE_TYPE_MESH_PATH_CHANGE"; + + /** + * Notify that a connection of a path is no longer valid + */ + case 258: return "GNUNET_MESSAGE_TYPE_MESH_PATH_BROKEN"; + + /** + * At some point, the route will spontaneously change + */ + case 259: return "GNUNET_MESSAGE_TYPE_MESH_PATH_CHANGED"; + + /** + * Transport data in the mesh (origin->end) unicast + */ + case 260: return "GNUNET_MESSAGE_TYPE_MESH_UNICAST"; + + /** + * Transport data to all peers in a tunnel + */ + case 261: return "GNUNET_MESSAGE_TYPE_MESH_MULTICAST"; + + /** + * Transport data back in the mesh (end->origin) + */ + case 262: return "GNUNET_MESSAGE_TYPE_MESH_TO_ORIGIN"; + + /** + * Send origin an ACK that the path is complete + */ + case 263: return "GNUNET_MESSAGE_TYPE_MESH_PATH_ACK"; + + /** + * Avoid path timeouts + */ + case 264: return "GNUNET_MESSAGE_TYPE_MESH_PATH_KEEPALIVE"; + + /** + * Request the destuction of a path + */ + case 265: return "GNUNET_MESSAGE_TYPE_MESH_PATH_DESTROY"; + + /** + * Request the destruction of a whole tunnel + */ + case 266: return "GNUNET_MESSAGE_TYPE_MESH_TUNNEL_DESTROY"; + + /** + * ACK for a data packet. + */ + case 267: return "GNUNET_MESSAGE_TYPE_MESH_ACK"; + + /** + * POLL for ACK. + */ + case 268: return "GNUNET_MESSAGE_TYPE_MESH_POLL"; + + /** + * Connect to the mesh service, specifying subscriptions + */ + case 272: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_CONNECT"; + + /** + * Ask the mesh service to create a new tunnel + */ + case 273: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_CREATE"; + + /** + * Ask the mesh service to destroy a tunnel + */ + case 274: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_DESTROY"; + + /** + * Ask the mesh service to add a peer to an existing tunnel + */ + case 275: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_ADD"; + + /** + * Ask the mesh service to remove a peer from a tunnel + */ + case 276: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_DEL"; + + /** + * Ask the mesh service to add a peer offering a service to an existing tunnel + */ + case 277: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_ADD_BY_TYPE"; + + /** + * Ask the mesh service to add a peer described by a service string + */ + case 278: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_ANNOUNCE_REGEX"; + + /** + * Ask the mesh service to add a peer described by a service string + */ + case 279: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_ADD_BY_STRING"; + + /** + * Ask the mesh service to add a peer to the blacklist of an existing tunnel + */ + case 280: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_BLACKLIST"; + + /** + * Ask the mesh service to remove a peer from the blacklist of a tunnel + */ + case 281: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_PEER_UNBLACKLIST"; + + /** + * Set tunnel speed to slowest peer + */ + case 282: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_MIN"; + + /** + * Set tunnel speed to fastest peer + */ + case 283: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_MAX"; + + /** + * Set tunnel buffering on. + */ + case 284: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_BUFFER"; + + /** + * Set tunnel buffering off. + */ + case 285: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_NOBUFFER"; + + /** + * Local ACK for data. + */ + case 286: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_ACK"; + + /** + * Local monitoring of service. + */ + case 287: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_MONITOR"; + + /** + * Local monitoring of service of a specific tunnel. + */ + case 288: return "GNUNET_MESSAGE_TYPE_MESH_LOCAL_MONITOR_TUNNEL"; + + /** + * 640kb should be enough for everybody + */ + case 299: return "GNUNET_MESSAGE_TYPE_MESH_RESERVE_END"; + } + sprintf(buf, "%u (UNKNOWN TYPE)", m); + return buf; +} +#else +const char * +GNUNET_MESH_DEBUG_M2S (uint16_t m) +{ + return ""; +} +#endif diff --git a/src/mesh/mesh_protocol.h b/src/mesh/mesh_protocol.h index 885f1f3..01f7f34 100644 --- a/src/mesh/mesh_protocol.h +++ b/src/mesh/mesh_protocol.h @@ -35,6 +35,10 @@ extern "C" #endif #endif +#define MESH_TUNNEL_OPT_SPEED_MIN 0x1 +#define MESH_TUNNEL_OPT_NOBUFFER 0x2 + + /******************************************************************************/ /******************** MESH NETWORK MESSAGES **************************/ /******************************************************************************/ @@ -47,7 +51,7 @@ GNUNET_NETWORK_STRUCT_BEGIN struct GNUNET_MESH_ManipulatePath { /** - * Type: GNUNET_MESSAGE_TYPE_MESH_PATH_[CREATE|CHANGE|ADD|DEL] + * Type: GNUNET_MESSAGE_TYPE_MESH_PATH_[CREATE|CHANGE|ADD|DESTROY] * * Size: sizeof(struct GNUNET_MESH_ManipulatePath) + * path_length * sizeof (struct GNUNET_PeerIdentity) @@ -61,6 +65,16 @@ struct GNUNET_MESH_ManipulatePath uint32_t tid GNUNET_PACKED; /** + * Tunnel options (MESH_TUNNEL_OPT_*). + */ + uint32_t opt GNUNET_PACKED; + + /** + * 64 bit alignment padding. + */ + uint32_t reserved GNUNET_PACKED; + + /** * path_length structs defining the *whole* path from the origin [0] to the * final destination [path_length-1]. */ @@ -90,7 +104,7 @@ struct GNUNET_MESH_Multicast /** * Unique ID of the packet */ - uint32_t mid GNUNET_PACKED; + uint32_t pid GNUNET_PACKED; /** * OID of the tunnel @@ -119,6 +133,16 @@ struct GNUNET_MESH_Unicast uint32_t tid GNUNET_PACKED; /** + * Number of hops to live + */ + uint32_t ttl GNUNET_PACKED; + + /** + * Unique ID of the packet + */ + uint32_t pid GNUNET_PACKED; + + /** * OID of the tunnel */ struct GNUNET_PeerIdentity oid; @@ -150,6 +174,16 @@ struct GNUNET_MESH_ToOrigin uint32_t tid GNUNET_PACKED; /** + * Number of hops to live + */ + uint32_t ttl GNUNET_PACKED; + + /** + * Unique ID of the packet + */ + uint32_t pid GNUNET_PACKED; + + /** * OID of the tunnel */ struct GNUNET_PeerIdentity oid; @@ -166,6 +200,59 @@ struct GNUNET_MESH_ToOrigin /** + * Message to acknowledge mesh data traffic. + */ +struct GNUNET_MESH_ACK +{ + /** + * Type: GNUNET_MESSAGE_TYPE_MESH_ACK + */ + struct GNUNET_MessageHeader header; + + /** + * TID of the tunnel + */ + uint32_t tid GNUNET_PACKED; + + /** + * OID of the tunnel + */ + struct GNUNET_PeerIdentity oid; + + /** + * Maximum packet ID authorized. + */ + uint32_t pid; + +}; + +/** + * Message to query a peer about its Flow Control status regarding a tunnel. + */ +struct GNUNET_MESH_Poll +{ + /** + * Type: GNUNET_MESSAGE_TYPE_MESH_POLL + */ + struct GNUNET_MessageHeader header; + + /** + * TID of the tunnel + */ + uint32_t tid GNUNET_PACKED; + + /** + * OID of the tunnel + */ + struct GNUNET_PeerIdentity oid; + + /** + * Last ACK received. + */ + uint32_t last_ack; +}; + +/** * Message for ack'ing a path */ struct GNUNET_MESH_PathACK @@ -253,31 +340,28 @@ struct GNUNET_MESH_TunnelDestroy /** - * Message for mesh flow control + * Message to destroy a tunnel */ -struct GNUNET_MESH_SpeedNotify +struct GNUNET_MESH_TunnelKeepAlive { - /** - * Type: GNUNET_MESSAGE_TYPE_DATA_SPEED_NOTIFY - */ + /** + * Type: GNUNET_MESSAGE_TYPE_MESH_PATH_KEEPALIVE + */ struct GNUNET_MessageHeader header; - - /** - * TID of the tunnel - */ + + /** + * TID of the tunnel + */ uint32_t tid GNUNET_PACKED; - - /** - * OID of the tunnel - */ + + /** + * OID of the tunnel + */ struct GNUNET_PeerIdentity oid; +}; + - /** - * Slowest link down the path (above minimum speed requirement). - */ - uint32_t speed_min; -}; GNUNET_NETWORK_STRUCT_END #if 0 /* keep Emacsens' auto-indent happy */ @@ -287,6 +371,6 @@ GNUNET_NETWORK_STRUCT_END } #endif -/* ifndef MES_PROTOCOL_H */ +/* ifndef MESH_PROTOCOL_H */ #endif /* end of mesh_protocol.h */ diff --git a/src/mesh/mesh_test_lib.c b/src/mesh/mesh_test_lib.c new file mode 100644 index 0000000..cec74f2 --- /dev/null +++ b/src/mesh/mesh_test_lib.c @@ -0,0 +1,297 @@ +/* + 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 mesh/mesh_test_lib.c + * @author Bartlomiej Polot + * @brief library for writing MESH tests + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "mesh_test_lib.h" +#include "gnunet_mesh_service.h" + +/** + * Test context for a MESH Test. + */ +struct GNUNET_MESH_TEST_Context +{ + /** + * Array of running peers. + */ + struct GNUNET_TESTBED_Peer **peers; + + /** + * Array of handles to the MESH for each peer. + */ + struct GNUNET_MESH_Handle **meshes; + + /** + * Operation associated with the connection to the MESH. + */ + struct GNUNET_TESTBED_Operation **ops; + + /** + * Main function of the test to run once all MESHs are available. + */ + GNUNET_MESH_TEST_AppMain app_main; + + /** + * Closure for 'app_main'. + */ + void *app_main_cls; + + /** + * Number of peers running, size of the arrays above. + */ + unsigned int num_peers; + + /** + * Handler for incoming tunnels. + */ + GNUNET_MESH_InboundTunnelNotificationHandler *new_tunnel; + + /** + * Cleaner for destroyed incoming tunnels. + */ + GNUNET_MESH_TunnelEndHandler *cleaner; + + /** + * Message handlers. + */ + struct GNUNET_MESH_MessageHandler* handlers; + + /** + * Application types. + */ + const GNUNET_MESH_ApplicationType* stypes; + +}; + + +/** + * Context for a mesh adapter callback. + */ +struct GNUNET_MESH_TEST_AdapterContext +{ + /** + * Peer number for the particular peer. + */ + unsigned int peer; + + /** + * General context. + */ + struct GNUNET_MESH_TEST_Context *ctx; +}; + + +/** + * Adapter function called to establish a connection to + * the MESH service. + * + * @param cls closure + * @param cfg configuration of the peer to connect to; will be available until + * GNUNET_TESTBED_operation_done() is called on the operation returned + * from GNUNET_TESTBED_service_connect() + * @return service handle to return in 'op_result', NULL on error + */ +static void * +mesh_connect_adapter (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_MESH_TEST_AdapterContext *actx = cls; + struct GNUNET_MESH_TEST_Context *ctx = actx->ctx; + struct GNUNET_MESH_Handle *h; + + h = GNUNET_MESH_connect (cfg, + (void *) (long) actx->peer, + ctx->new_tunnel, + ctx->cleaner, + ctx->handlers, + ctx->stypes); + return h; +} + + +/** + * Adapter function called to destroy a connection to + * the MESH service. + * + * @param cls closure + * @param op_result service handle returned from the connect adapter + */ +static void +mesh_disconnect_adapter (void *cls, + void *op_result) +{ + struct GNUNET_MESH_Handle *mesh = op_result; + struct GNUNET_MESH_TEST_AdapterContext *actx = cls; + + GNUNET_free (actx); + GNUNET_MESH_disconnect (mesh); +} + + +/** + * Callback to be called when a service connect operation is completed. + * + * @param cls The callback closure from functions generating an operation. + * @param op The operation that has been finished. + * @param ca_result The service handle returned from + * GNUNET_TESTBED_ConnectAdapter() (mesh handle). + * @param emsg Error message in case the operation has failed. + * NULL if operation has executed successfully. + */ +static void +mesh_connect_cb (void *cls, + struct GNUNET_TESTBED_Operation *op, + void *ca_result, + const char *emsg) +{ + struct GNUNET_MESH_TEST_Context *ctx = cls; + unsigned int i; + + if (NULL != emsg) + { + fprintf (stderr, "Failed to connect to MESH service: %s\n", + emsg); + GNUNET_SCHEDULER_shutdown (); + return; + } + for (i = 0; i < ctx->num_peers; i++) + if (op == ctx->ops[i]) + ctx->meshes[i] = ca_result; + for (i = 0; i < ctx->num_peers; i++) + if (NULL == ctx->meshes[i]) + return; /* still some MESH connections missing */ + /* all MESH connections ready! */ + ctx->app_main (ctx->app_main_cls, + ctx, + ctx->num_peers, + ctx->peers, + ctx->meshes); +} + + +/** + * Clean up the testbed. + * + * @param ctx handle for the testbed + */ +void +GNUNET_MESH_TEST_cleanup (struct GNUNET_MESH_TEST_Context *ctx) +{ + unsigned int i; + + for (i = 0; i < ctx->num_peers; i++) + { + GNUNET_assert (NULL != ctx->ops[i]); + GNUNET_TESTBED_operation_done (ctx->ops[i]); + ctx->ops[i] = NULL; + } + GNUNET_free (ctx->ops); + GNUNET_free (ctx->meshes); + GNUNET_free (ctx); + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Callback run when the testbed is ready (peers running and connected to + * each other) + * + * @param cls Closure (context). + * @param num_peers Number of peers that are running. + * @param peers Handles to each one of the @c num_peers peers. + */ +static void +mesh_test_run (void *cls, + unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers) +{ + struct GNUNET_MESH_TEST_Context *ctx = cls; + unsigned int i; + + GNUNET_assert (num_peers == ctx->num_peers); + ctx->peers = peers; + for (i = 0; i < num_peers; i++) + { + struct GNUNET_MESH_TEST_AdapterContext *newctx; + newctx = GNUNET_malloc (sizeof (struct GNUNET_MESH_TEST_AdapterContext)); + newctx->peer = i; + newctx->ctx = ctx; + ctx->ops[i] = GNUNET_TESTBED_service_connect (ctx, + peers[i], + "mesh", + &mesh_connect_cb, + ctx, + &mesh_connect_adapter, + &mesh_disconnect_adapter, + newctx); + } +} + + +/** + * Run a test using the given name, configuration file and number of + * peers. + * All mesh callbacks will receive the peer number as the closure. + * + * @param testname Name of the test (for logging). + * @param cfgname Name of the configuration file. + * @param num_peers Number of peers to start. + * @param tmain Main function to run once the testbed is ready. + * @param tmain_cls Closure for 'tmain'. + * @param new_tunnel Handler for incoming tunnels. + * @param cleaner Cleaner for destroyed incoming tunnels. + * @param handlers Message handlers. + * @param stypes Application types. + */ +void +GNUNET_MESH_TEST_run (const char *testname, + const char *cfgname, + unsigned int num_peers, + GNUNET_MESH_TEST_AppMain tmain, + void *tmain_cls, + GNUNET_MESH_InboundTunnelNotificationHandler new_tunnel, + GNUNET_MESH_TunnelEndHandler cleaner, + struct GNUNET_MESH_MessageHandler* handlers, + const GNUNET_MESH_ApplicationType* stypes) +{ + struct GNUNET_MESH_TEST_Context *ctx; + + ctx = GNUNET_malloc (sizeof (struct GNUNET_MESH_TEST_Context)); + ctx->num_peers = num_peers; + ctx->ops = GNUNET_malloc (num_peers * sizeof (struct GNUNET_TESTBED_Operation *)); + ctx->meshes = GNUNET_malloc (num_peers * sizeof (struct GNUNET_MESH_Handle *)); + ctx->app_main = tmain; + ctx->app_main_cls = tmain_cls; + ctx->new_tunnel = new_tunnel; + ctx->cleaner = cleaner; + ctx->handlers = handlers; + ctx->stypes = stypes; + GNUNET_TESTBED_test_run (testname, + cfgname, + num_peers, + 0LL, NULL, NULL, + &mesh_test_run, ctx); +} + +/* end of mesh_test_lib.c */ diff --git a/src/mesh/mesh_test_lib.h b/src/mesh/mesh_test_lib.h new file mode 100644 index 0000000..554d065 --- /dev/null +++ b/src/mesh/mesh_test_lib.h @@ -0,0 +1,106 @@ +/* + 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 mesh/mesh_test_lib.h + * @author Bartlomiej Polot + * @brief library for writing MESH tests + */ +#ifndef MESH_TEST_LIB_H +#define MESH_TEST_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_testbed_service.h" +#include "gnunet_mesh_service.h" + +/** + * Test context for a MESH Test. + */ +struct GNUNET_MESH_TEST_Context; + + +/** + * Main function of a MESH test. + * + * @param cls Closure. + * @param ctx Argument to give to GNUNET_MESH_TEST_cleanup on test end. + * @param num_peers Number of peers that are running. + * @param peers Array of peers. + * @param meshes Handle to each of the MESHs of the peers. + */ +typedef void (*GNUNET_MESH_TEST_AppMain) (void *cls, + struct GNUNET_MESH_TEST_Context *ctx, + unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers, + struct GNUNET_MESH_Handle **meshes); + + +/** + * Run a test using the given name, configuration file and number of + * peers. + * All mesh callbacks will receive the peer number as the closure. + * + * @param testname Name of the test (for logging). + * @param cfgname Name of the configuration file. + * @param num_peers Number of peers to start. + * @param tmain Main function to run once the testbed is ready. + * @param tmain_cls Closure for 'tmain'. + * @param new_tunnel Handler for incoming tunnels. + * @param cleaner Cleaner for destroyed incoming tunnels. + * @param handlers Message handlers. + * @param stypes Application types. + */ +void +GNUNET_MESH_TEST_run (const char *testname, + const char *cfgname, + unsigned int num_peers, + GNUNET_MESH_TEST_AppMain tmain, + void *tmain_cls, + GNUNET_MESH_InboundTunnelNotificationHandler new_tunnel, + GNUNET_MESH_TunnelEndHandler cleaner, + struct GNUNET_MESH_MessageHandler* handlers, + const GNUNET_MESH_ApplicationType* stypes); + + +/** + * Clean up the testbed. + * + * @param ctx handle for the testbed + */ +void +GNUNET_MESH_TEST_cleanup (struct GNUNET_MESH_TEST_Context *ctx); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + + +/* ifndef MESH_TEST_LIB_H */ +#endif diff --git a/src/mesh/mesh_tunnel_tree.c b/src/mesh/mesh_tunnel_tree.c index 287eefc..41b25c3 100644 --- a/src/mesh/mesh_tunnel_tree.c +++ b/src/mesh/mesh_tunnel_tree.c @@ -379,6 +379,8 @@ tree_node_destroy (struct MeshTunnelTreeNode *parent) struct MeshTunnelTreeNode *n; struct MeshTunnelTreeNode *next; + if (NULL == parent) + return; #if MESH_TREE_DEBUG struct GNUNET_PeerIdentity id; @@ -416,7 +418,7 @@ tree_new (GNUNET_PEER_Id peer) struct MeshTunnelTree *tree; tree = GNUNET_malloc (sizeof (struct MeshTunnelTree)); - tree->first_hops = GNUNET_CONTAINER_multihashmap_create (32); + tree->first_hops = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_NO); tree->root = tree_node_new (NULL, peer); tree->root->status = MESH_PEER_ROOT; @@ -494,6 +496,8 @@ tree_get_predecessor (struct MeshTunnelTree *tree) * * @return peerinfo of the peer who is the first hop in the tunnel * NULL on error + * + * FIXME use PEER_Id */ struct GNUNET_PeerIdentity * tree_get_first_hop (struct MeshTunnelTree *t, GNUNET_PEER_Id peer) @@ -583,27 +587,118 @@ tree_mark_peers_disconnected (struct MeshTunnelTree *tree, * * @param tree Tree to use. Must have "me" set. * @param cb Callback to call over each child. - * @param cls Closure. + * @param cb_cls Closure for @c cb. */ void tree_iterate_children (struct MeshTunnelTree *tree, MeshTreeCallback cb, - void *cls) + void *cb_cls) { struct MeshTunnelTreeNode *n; if (NULL == tree->me) - { - GNUNET_break (0); return; - } for (n = tree->me->children_head; NULL != n; n = n->next) { - cb (cls, n->peer); + cb (cb_cls, n->peer); + } +} + + +/** + * Struct to contain a list of pending nodes when iterating a tree. + */ +struct MeshTreePendingNode { + + /** + * DLL next. + */ + struct MeshTreePendingNode *next; + + /** + * DLL prev. + */ + struct MeshTreePendingNode *prev; + + /** + * Pending node. + */ + struct MeshTunnelTreeNode *node; +}; + + +/** + * Iterate over all nodes in the tree. + * + * @param tree Tree to use.. + * @param cb Callback to call over each child. + * @param cb_cls Closure for @c cb. + * + * TODO: recursive implementation? (s/heap/stack/g) + */ +void +tree_iterate_all (struct MeshTunnelTree *tree, + MeshWholeTreeCallback cb, + void *cb_cls) +{ + struct MeshTunnelTreeNode *parent; + struct MeshTunnelTreeNode *n; + struct MeshTreePendingNode *head; + struct MeshTreePendingNode *tail; + struct MeshTreePendingNode *pending; + + cb (cb_cls, tree->root->peer, 0); + pending = GNUNET_malloc (sizeof (struct MeshTreePendingNode)); + pending->node = tree->root; + head = tail = NULL; + GNUNET_CONTAINER_DLL_insert (head, tail, pending); + + while (NULL != head) + { + pending = head; + parent = pending->node; + GNUNET_CONTAINER_DLL_remove (head, tail, pending); + GNUNET_free (pending); + for (n = parent->children_head; NULL != n; n = n->next) + { + cb (cb_cls, n->peer, parent->peer); + pending = GNUNET_malloc (sizeof (struct MeshTreePendingNode)); + pending->node = n; + /* Insert_tail: breadth first, Insert: depth first */ + GNUNET_CONTAINER_DLL_insert (head, tail, pending); + } } } /** + * Iterator to count the children in a tree. + */ +static void +count_children_cb (void *cls, GNUNET_PEER_Id peer) +{ + unsigned int *i = cls; + + (*i)++; +} + + +/** + * Count how many children does the local node have in the tree. + * + * @param tree Tree to use. Must have "me" set. + */ +unsigned int +tree_count_children (struct MeshTunnelTree *tree) +{ + unsigned int i; + + i = 0; + tree_iterate_children(tree, &count_children_cb, &i); + return i; +} + + +/** * Recusively update the info about what is the first hop to reach the node * * @param tree Tree this nodes belongs to. @@ -647,7 +742,7 @@ tree_del_path (struct MeshTunnelTree *t, GNUNET_PEER_Id peer_id, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "tree: Deleting path to %s.\n", GNUNET_i2s (&id)); #endif - if (peer_id == t->root->peer) + if (NULL == t->root || peer_id == t->root->peer) return NULL; for (n = t->disconnected_head; NULL != n; n = n->next) @@ -669,7 +764,8 @@ tree_del_path (struct MeshTunnelTree *t, GNUNET_PEER_Id peer_id, GNUNET_CONTAINER_DLL_remove (parent->children_head, parent->children_tail, n); n->parent = NULL; - while (MESH_PEER_RELAY == parent->status && NULL == parent->children_head) + while (MESH_PEER_RELAY == parent->status && + NULL == parent->children_head) { #if MESH_TREE_DEBUG GNUNET_PEER_resolve (parent->peer, &id); @@ -677,6 +773,8 @@ tree_del_path (struct MeshTunnelTree *t, GNUNET_PEER_Id peer_id, GNUNET_i2s (&id)); #endif n = parent->parent; + if (parent == t->me) + t->me = NULL; tree_node_destroy (parent); parent = n; } @@ -970,7 +1068,6 @@ tree_del_peer (struct MeshTunnelTree *t, GNUNET_PEER_Id peer, GNUNET_break (0); return GNUNET_YES; } - GNUNET_break_op (NULL == n->children_head); tree_node_destroy (n); if (NULL == t->root->children_head && t->me != t->root) { @@ -1037,6 +1134,8 @@ void tree_debug (struct MeshTunnelTree *t) { tree_node_debug (t->root, 0); + FPRINTF (stderr, "root: %p\n", t->root); + FPRINTF (stderr, "me: %p\n", t->me); } @@ -1050,7 +1149,7 @@ tree_debug (struct MeshTunnelTree *t) * @return GNUNET_YES if we should continue to iterate, GNUNET_NO if not. */ static int -iterate_free (void *cls, const GNUNET_HashCode * key, void *value) +iterate_free (void *cls, const struct GNUNET_HashCode * key, void *value) { GNUNET_free (value); return GNUNET_YES; diff --git a/src/mesh/mesh_tunnel_tree.h b/src/mesh/mesh_tunnel_tree.h index 84fd0ac..e5d6592 100644 --- a/src/mesh/mesh_tunnel_tree.h +++ b/src/mesh/mesh_tunnel_tree.h @@ -126,15 +126,26 @@ path_destroy (struct MeshPeerPath *p); /******************************************************************************/ /** - * Method called whenever a node has been marked as disconnected. + * Iterator over all children of a node. * * @param cls Closure. - * @param peer_id short ID of peer that is no longer reachable. + * @param peer_id Short ID of the peer. */ typedef void (*MeshTreeCallback) (void *cls, GNUNET_PEER_Id peer_id); /** + * Iterator over all nodes in a tree. + * + * @param cls Closure. + * @param peer_id Short ID of the peer. + * @param peer_id Short ID of the parent of the peer. + */ +typedef void (*MeshWholeTreeCallback) (void *cls, + GNUNET_PEER_Id peer_id, + GNUNET_PEER_Id parent_id); + +/** * Create a new tunnel tree associated to a tunnel * * @param peer A short peer id of the root of the tree @@ -210,11 +221,35 @@ tree_find_peer (struct MeshTunnelTree *tree, GNUNET_PEER_Id peer_id); * * @param tree Tree to use. Must have "me" set. * @param cb Callback to call over each child. - * @param cls Closure. + * @param cb_cls Closure for @c cb. */ void -tree_iterate_children (struct MeshTunnelTree *tree, MeshTreeCallback cb, - void *cls); +tree_iterate_children (struct MeshTunnelTree *tree, + MeshTreeCallback cb, + void *cb_cls); + + +/** + * Iterate over all nodes in the tree. + * + * @param tree Tree to use.. + * @param cb Callback to call over each child. + * @param cb_cls Closure for @c cb. + * + * TODO: recursive implementation? (s/heap/stack/g) + */ +void +tree_iterate_all (struct MeshTunnelTree *tree, + MeshWholeTreeCallback cb, + void *cb_cls); + +/** + * Count how many children does the local node have in the tree. + * + * @param tree Tree to use. Must have "me" set. + */ +unsigned int +tree_count_children (struct MeshTunnelTree *tree); /** @@ -302,6 +337,8 @@ tree_notify_connection_broken (struct MeshTunnelTree *t, GNUNET_PEER_Id p1, * If the tree is not local and no longer has any paths, the root node will be * destroyed and marked as NULL. * + * FIXME: dont destroy the root + * * @param t Tunnel tree to use. * @param peer Short ID of the peer to remove from the tunnel tree. * @param cb Callback to notify client of disconnected peers. diff --git a/src/mesh/plugin_block_mesh.c b/src/mesh/plugin_block_mesh.c new file mode 100644 index 0000000..f0dae71 --- /dev/null +++ b/src/mesh/plugin_block_mesh.c @@ -0,0 +1,205 @@ +/* + This file is part of GNUnet + (C) 2012,2013 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 mesh/plugin_block_mesh.c + * @brief blocks used for mesh peer discovery + * @author Bartlomiej Polot + */ + +#include "platform.h" +#include "gnunet_block_plugin.h" +#include "block_mesh.h" + +/** + * Number of bits we set per entry in the bloomfilter. + * Do not change! + */ +#define BLOOMFILTER_K 16 + + +/** + * Function called to validate a reply or a request. For + * request evaluation, simply pass "NULL" for the reply_block. + * Note that it is assumed that the reply has already been + * matched to the key (and signatures checked) as it would + * be done with the "get_key" function. + * + * @param cls closure + * @param type block type + * @param query original query (hash) + * @param bf pointer to bloom filter associated with query; possibly updated (!) + * @param bf_mutator mutation value for bf + * @param xquery extrended query data (can be NULL, depending on type) + * @param xquery_size number of bytes in xquery + * @param reply_block response to validate + * @param reply_block_size number of bytes in reply block + * @return characterization of result + */ +static enum GNUNET_BLOCK_EvaluationResult +block_plugin_mesh_evaluate (void *cls, enum GNUNET_BLOCK_Type type, + const struct GNUNET_HashCode * query, + struct GNUNET_CONTAINER_BloomFilter **bf, + int32_t bf_mutator, const void *xquery, + size_t xquery_size, const void *reply_block, + size_t reply_block_size) +{ + struct GNUNET_HashCode chash; + struct GNUNET_HashCode mhash; + + switch (type) + { + case GNUNET_BLOCK_TYPE_MESH_PEER: + if (0 != xquery_size) + { + GNUNET_break_op (0); + return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID; + } + if (NULL == reply_block) + return GNUNET_BLOCK_EVALUATION_REQUEST_VALID; + if (sizeof (struct PBlock) != reply_block_size) + return GNUNET_BLOCK_EVALUATION_RESULT_INVALID; + if (NULL != bf) + { + GNUNET_CRYPTO_hash (reply_block, reply_block_size, &chash); + GNUNET_BLOCK_mingle_hash (&chash, bf_mutator, &mhash); + if (NULL != *bf) + { + if (GNUNET_YES == GNUNET_CONTAINER_bloomfilter_test (*bf, &mhash)) + return GNUNET_BLOCK_EVALUATION_OK_DUPLICATE; + } + else + { + *bf = GNUNET_CONTAINER_bloomfilter_init (NULL, 8, BLOOMFILTER_K); + } + GNUNET_CONTAINER_bloomfilter_add (*bf, &mhash); + } + + return GNUNET_BLOCK_EVALUATION_OK_LAST; + + + case GNUNET_BLOCK_TYPE_MESH_PEER_BY_TYPE: + /* FIXME: have an xquery? not sure */ + if (0 != xquery_size) + { + GNUNET_break_op (0); + return GNUNET_BLOCK_EVALUATION_REQUEST_INVALID; + } + if (NULL == reply_block) + return GNUNET_BLOCK_EVALUATION_REQUEST_VALID; + if (sizeof (struct PBlock) != reply_block_size) + { + GNUNET_break_op(0); + return GNUNET_BLOCK_EVALUATION_RESULT_INVALID; + } + if (NULL != bf) + { + GNUNET_CRYPTO_hash (reply_block, reply_block_size, &chash); + GNUNET_BLOCK_mingle_hash (&chash, bf_mutator, &mhash); + if (NULL != *bf) + { + if (GNUNET_YES == GNUNET_CONTAINER_bloomfilter_test (*bf, &mhash)) + return GNUNET_BLOCK_EVALUATION_OK_DUPLICATE; + } + else + { + *bf = GNUNET_CONTAINER_bloomfilter_init (NULL, 8, BLOOMFILTER_K); + } + GNUNET_CONTAINER_bloomfilter_add (*bf, &mhash); + } + return GNUNET_BLOCK_EVALUATION_OK_MORE; + + default: + return GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED; + } +} + + +/** + * Function called to obtain the key for a block. + * + * @param cls closure + * @param type block type + * @param block block to get the key for + * @param block_size number of bytes in block + * @param key set to the key (query) for the given block + * @return GNUNET_OK on success, GNUNET_SYSERR if type not supported + * (or if extracting a key from a block of this type does not work) + */ +static int +block_plugin_mesh_get_key (void *cls, enum GNUNET_BLOCK_Type type, + const void *block, size_t block_size, + struct GNUNET_HashCode * key) +{ + const struct PBlock *pb; + pb = block; + + switch (type) + { + case GNUNET_BLOCK_TYPE_MESH_PEER: + if (sizeof (struct PBlock) != block_size) + return GNUNET_SYSERR; + *key = pb->id.hashPubKey; + return GNUNET_OK; + case GNUNET_BLOCK_TYPE_MESH_PEER_BY_TYPE: + GNUNET_CRYPTO_hash (&pb->type, sizeof(GNUNET_MESH_ApplicationType), key); + return GNUNET_OK; + default: + GNUNET_break (0); + return GNUNET_SYSERR; + } +} + + +/** + * Entry point for the plugin. + */ +void * +libgnunet_plugin_block_mesh_init (void *cls) +{ + static enum GNUNET_BLOCK_Type types[] = + { + GNUNET_BLOCK_TYPE_MESH_PEER, + GNUNET_BLOCK_TYPE_MESH_PEER_BY_TYPE, + GNUNET_BLOCK_TYPE_ANY /* end of list */ + }; + struct GNUNET_BLOCK_PluginFunctions *api; + + api = GNUNET_malloc (sizeof (struct GNUNET_BLOCK_PluginFunctions)); + api->evaluate = &block_plugin_mesh_evaluate; + api->get_key = &block_plugin_mesh_get_key; + api->types = types; + return api; +} + + +/** + * Exit point from the plugin. + */ +void * +libgnunet_plugin_block_mesh_done (void *cls) +{ + struct GNUNET_TRANSPORT_PluginFunctions *api = cls; + + GNUNET_free (api); + return NULL; +} + +/* end of plugin_block_mesh.c */ diff --git a/src/mesh/test_mesh.conf b/src/mesh/test_mesh.conf index f2e51e4..3b35a16 100644 --- a/src/mesh/test_mesh.conf +++ b/src/mesh/test_mesh.conf @@ -5,16 +5,26 @@ AUTOSTART = NO AUTOSTART = NO [mesh] -DEBUG = YES AUTOSTART = YES ACCEPT_FROM = 127.0.0.1; HOSTNAME = localhost PORT = 10511 # PREFIX = valgrind --leak-check=full # PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args +REFRESH_PATH_TIME = 3 s +APP_ANNOUNCE_TIME = 5 s +ID_ANNOUNCE_TIME = 5 s +CONNECT_TIMEOUT = 30 s +DEFAULT_TTL = 16 +DHT_REPLICATION_LEVEL = 3 +MAX_TUNNELS = 10 +MAX_MSGS_QUEUE = 20 + +[vpn] +AUTOSTART = NO +PORT = 10012 [dht] -DEBUG = NO AUTOSTART = YES ACCEPT_FROM6 = ::1; ACCEPT_FROM = 127.0.0.1; @@ -30,7 +40,6 @@ DATABASE = sqlite [transport] PLUGINS = tcp -DEBUG = NO ACCEPT_FROM6 = ::1; ACCEPT_FROM = 127.0.0.1; NEIGHBOUR_LIMIT = 50 @@ -46,7 +55,6 @@ PORT = 12092 [arm] DEFAULTSERVICES = core PORT = 12366 -DEBUG = NO [transport-tcp] TIMEOUT = 300 s @@ -59,7 +67,6 @@ WEAKRANDOM = YES HOSTKEY = $SERVICEHOME/.hostkey [PATHS] -DEFAULTCONFIG = test_mesh.conf SERVICEHOME = /tmp/test-mesh/ [dns] @@ -70,3 +77,6 @@ AUTOSTART = NO [namestore] AUTOSTART = NO + +[consensus] +AUTOSTART = NO diff --git a/src/mesh/test_mesh_2dtorus.c b/src/mesh/test_mesh_2dtorus.c index c708e8f..4b6affc 100644 --- a/src/mesh/test_mesh_2dtorus.c +++ b/src/mesh/test_mesh_2dtorus.c @@ -23,9 +23,9 @@ * @brief Test for creating a 2dtorus. */ #include "platform.h" -#include "gnunet_testing_lib.h" +#include "mesh_test_lib.h" +#include "gnunet_mesh_service.h" -#define VERBOSE GNUNET_YES #define REMOVE_DIR GNUNET_YES /** @@ -45,325 +45,83 @@ static int ok; /** - * Be verbose - */ -static int verbose; - -/** - * Total number of peers in the test. - */ -static unsigned long long num_peers; - -/** - * Global configuration file - */ -static struct GNUNET_CONFIGURATION_Handle *testing_cfg; - -/** * Total number of currently running peers. */ static unsigned long long peers_running; /** - * Total number of successful connections in the whole network. - */ -static unsigned int total_connections; - -/** - * Total number of counted topo connections - */ -static unsigned int topo_connections; - -/** - * Total number of failed connections in the whole network. - */ -static unsigned int failed_connections; - -/** - * The currently running peer group. - */ -static struct GNUNET_TESTING_PeerGroup *pg; - -/** - * Task called to disconnect peers - */ -static GNUNET_SCHEDULER_TaskIdentifier disconnect_task; - -/** - * Task called to shutdown test. - */ -static GNUNET_SCHEDULER_TaskIdentifier shutdown_handle; - - -/** - * Check whether peers successfully shut down. + * Task to time out. */ -static void -shutdown_callback (void *cls, const char *emsg) -{ - if (emsg != NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "test: Shutdown of peers failed! (%s)\n", emsg); - ok--; - } -#if VERBOSE - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "test: All peers successfully shut down!\n"); - } -#endif - GNUNET_CONFIGURATION_destroy (testing_cfg); -} +static GNUNET_SCHEDULER_TaskIdentifier timeout_task; static void shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { -#if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Ending test.\n"); -#endif - - GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); -} - - -static void -disconnect_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: disconnecting peers\n"); - - if (GNUNET_SCHEDULER_NO_TASK != shutdown_handle) - { - GNUNET_SCHEDULER_cancel (shutdown_handle); - shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); - } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "shutting down test\n"); } /** - * Prototype of a callback function indicating that two peers - * are currently connected. + * test main: start test when all peers are connected * - * @param cls closure - * @param first peer id for first daemon - * @param second peer id for the second daemon - * @param distance distance between the connected peers - * @param emsg error message (NULL on success) - */ -void -topo_cb (void *cls, const struct GNUNET_PeerIdentity *first, - const struct GNUNET_PeerIdentity *second, const char *emsg) -{ - topo_connections++; - if (NULL != emsg) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "test: Error by topo %u: %s\n", - topo_connections, emsg); - } - else - { - if (first == NULL || second == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Connection %u NULL\n", - topo_connections); - if (disconnect_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_peers, NULL); - } - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Connection %u ok\n", - topo_connections); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: %s\n", GNUNET_i2s (first)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: %s\n", GNUNET_i2s (second)); - } -} - - -/** - * peergroup_ready: start test when all peers are connected - * @param cls closure - * @param emsg error message + * @param cls Closure. + * @param ctx Argument to give to GNUNET_MESH_TEST_cleanup on test end. + * @param num_peers Number of peers that are running. + * @param peers Array of peers. + * @param meshes Handle to each of the MESHs of the peers. */ static void -peergroup_ready (void *cls, const char *emsg) +tmain (void *cls, + struct GNUNET_MESH_TEST_Context *ctx, + unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers, + struct GNUNET_MESH_Handle **meshes) { - if (emsg != NULL) + if (16 != num_peers) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "test: Peergroup callback called with error, aborting test!\n"); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Error from testing: `%s'\n", - emsg); + "running peers mismatch, aborting test!\n"); ok--; - GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); + GNUNET_MESH_TEST_cleanup (ctx); return; } -#if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "************************************************************\n"); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "test: Peer Group started successfully!\n"); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Have %u connections\n", - total_connections); -#endif - - peers_running = GNUNET_TESTING_daemons_running (pg); - if (0 < failed_connections) - { - ok = GNUNET_SYSERR; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "test: %u connections have FAILED!\n", - failed_connections); - disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_peers, NULL); - - } - else - { - GNUNET_TESTING_get_topology (pg, &topo_cb, NULL); - disconnect_task = - GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_peers, NULL); - ok = GNUNET_OK; - } - -} - - -/** - * Function that will be called whenever two daemons are connected by - * the testing library. - * - * @param cls closure - * @param first peer id for first daemon - * @param second peer id for the second daemon - * @param distance distance between the connected peers - * @param first_cfg config for the first daemon - * @param second_cfg config for the second daemon - * @param first_daemon handle for the first daemon - * @param second_daemon handle for the second daemon - * @param emsg error message (NULL on success) - */ -static void -connect_cb (void *cls, const struct GNUNET_PeerIdentity *first, - const struct GNUNET_PeerIdentity *second, uint32_t distance, - const struct GNUNET_CONFIGURATION_Handle *first_cfg, - const struct GNUNET_CONFIGURATION_Handle *second_cfg, - struct GNUNET_TESTING_Daemon *first_daemon, - struct GNUNET_TESTING_Daemon *second_daemon, const char *emsg) -{ - if (emsg == NULL) - { - total_connections++; - } - else - { - failed_connections++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "test: Problem with new connection (%s)\n", emsg); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: (%s)\n", GNUNET_i2s (first)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: (%s)\n", GNUNET_i2s (second)); - } - + "testbed started successfully with ?? connections\n"); + peers_running = num_peers; + timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, + &shutdown_task, ctx); + ok = GNUNET_OK; + GNUNET_MESH_TEST_cleanup (ctx); } /** - * run: load configuration options and schedule test to run (start peergroup) - * @param cls closure - * @param args argv - * @param cfgfile configuration file name (can be NULL) - * @param cfg configuration handle - */ -static void -run (void *cls, char *const *args, const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct GNUNET_TESTING_Host *hosts; - - ok = GNUNET_NO; - total_connections = 0; - failed_connections = 0; - testing_cfg = GNUNET_CONFIGURATION_dup (cfg); - - GNUNET_log_setup ("test_mesh_2dtorus", -#if VERBOSE - "DEBUG", -#else - "WARNING", -#endif - NULL); - -#if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Starting daemons.\n"); - GNUNET_CONFIGURATION_set_value_string (testing_cfg, "testing", - "use_progressbars", "YES"); -#endif - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (testing_cfg, "testing", - "num_peers", &num_peers)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Option TESTING:NUM_PEERS is required!\n"); - return; - } - - hosts = GNUNET_TESTING_hosts_load (testing_cfg); - - pg = GNUNET_TESTING_peergroup_start (testing_cfg, num_peers, TIMEOUT, - &connect_cb, &peergroup_ready, NULL, - hosts); - GNUNET_assert (pg != NULL); - shutdown_handle = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, - &shutdown_task, NULL); -} - - -/** - * test_mesh_2dtorus command line options - */ -static struct GNUNET_GETOPT_CommandLineOption options[] = { - {'V', "verbose", NULL, - gettext_noop ("be verbose (print progress information)"), - 0, &GNUNET_GETOPT_set_one, &verbose}, - GNUNET_GETOPT_OPTION_END -}; - - -/** * Main: start test */ int main (int argc, char *argv[]) { - char *const argv2[] = { - argv[0], - "-c", - "test_mesh_2dtorus.conf", -#if VERBOSE - "-L", - "DEBUG", -#endif - NULL - }; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Start\n"); + ok = GNUNET_SYSERR; + + GNUNET_MESH_TEST_run ("test_mesh_2dtorus", + "test_mesh_2dtorus.conf", + 16, + &tmain, + NULL, + NULL, + NULL, + NULL, + NULL + ); - - GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2, - "test_mesh_2dtorus", gettext_noop ("Test mesh 2d torus."), - options, &run, NULL); -#if REMOVE_DIR - GNUNET_DISK_directory_remove ("/tmp/test_mesh_2dtorus"); -#endif if (GNUNET_OK != ok) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "test: FAILED!\n"); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "FAILED!\n"); return 1; } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: success\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "success\n"); return 0; } diff --git a/src/mesh/test_mesh_2dtorus.conf b/src/mesh/test_mesh_2dtorus.conf index 9a2227b..fb235fe 100644 --- a/src/mesh/test_mesh_2dtorus.conf +++ b/src/mesh/test_mesh_2dtorus.conf @@ -1,23 +1,38 @@ [PATHS] SERVICEHOME = /tmp/test_mesh_small/ -DEFAULTCONFIG = test_mesh_small.conf + +[mesh] +PORT = 10005 +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +DHT_REPLICATION_LEVEL = 10 +# PREFIX = valgrind --leak-check=full +# PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args + +[testing] +WEAKRANDOM = YES + +[testbed] +NUM_PEERS = 16 +OVERLAY_TOPOLOGY = 2D_TORUS + + [arm] PORT = 10010 DEFAULTSERVICES = core dht mesh -#DEBUG = YES [statistics] AUTOSTART = YES PORT = 10000 [dht] -DEBUG = NO AUTOSTART = YES ACCEPT_FROM6 = ::1; ACCEPT_FROM = 127.0.0.1; HOSTNAME = localhost PORT = 10001 +DISABLE_TRY_CONNECT = YES [nse] WORKBITS = 0 @@ -26,6 +41,10 @@ WORKBITS = 0 AUTOSTART = NO PORT = 10011 +[vpn] +AUTOSTART = NO +PORT = 10012 + [transport] PORT = 10002 AUTOSTART = YES @@ -38,6 +57,8 @@ BEHIND_NAT = NO ALLOW_NAT = NO INTERNAL_ADDRESS = 127.0.0.1 EXTERNAL_ADDRESS = 127.0.0.1 +RETURN_LOCAL_ADDRESSES = YES +USE_LOCALADDR = YES [ats] WAN_QUOTA_IN = 1 GB @@ -50,38 +71,3 @@ PORT = 10003 [peerinfo] AUTOSTART = YES PORT = 10004 - -[mesh] -PORT = 10005 -DEBUG=YES -ACCEPT_FROM = 127.0.0.1; -HOSTNAME = localhost -# PREFIX = valgrind --leak-check=full -# PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args - -[testing] -NUM_PEERS = 16 -WEAKRANDOM = YES -TOPOLOGY = 2D_TORUS -CONNECT_TOPOLOGY = 2D_TORUS -#TOPOLOGY_FILE = small.dat -CONNECT_TOPOLOGY = 2D_TORUS -#CONNECT_TOPOLOGY_OPTION = CONNECT_MINIMUM -#CONNECT_TOPOLOGY_OPTION_MODIFIER = 25 -#PERCENTAGE = 3 -#PROBABILITY = .1 -F2F = NO -CONNECT_TIMEOUT = 600 s -CONNECT_ATTEMPTS = 2 -DEBUG = YES -HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat -MAX_CONCURRENT_SSH = 10 -USE_PROGRESSBARS = YES -PEERGROUP_TIMEOUT = 2400 s -TOPOLOGY_OUTPUT_FILE = mesh_topo_initial -MAX_OUTSTANDING_CONNECTIONS = 75 -#SINGLE_PEERINFO_PER_HOST = YES -#NUM_PEERINFO_PER_HOST = 10 -#SINGLE_STATISTICS_PER_HOST = YES -#NUM_STATISTICS_PER_HOST = 10 -DELETE_FILES = YES diff --git a/src/mesh/test_mesh_api.c b/src/mesh/test_mesh_api.c index 1e12e9b..349ec80 100644 --- a/src/mesh/test_mesh_api.c +++ b/src/mesh/test_mesh_api.c @@ -23,21 +23,20 @@ * @brief test mesh api: dummy test of callbacks * @author Bartlomiej Polot */ - #include "platform.h" #include "gnunet_util_lib.h" +#include "gnunet_testing_lib.h" #include "gnunet_dht_service.h" #include "gnunet_mesh_service.h" -#define VERBOSE 1 -#define VERBOSE_ARM 0 - -static struct GNUNET_OS_Process *arm_pid; static struct GNUNET_MESH_Handle *mesh; + static struct GNUNET_MESH_Tunnel *t; + static int result; + static GNUNET_SCHEDULER_TaskIdentifier abort_task; -static GNUNET_SCHEDULER_TaskIdentifier test_task; + /** * Function is called whenever a message is received. @@ -60,8 +59,10 @@ callback (void *cls, struct GNUNET_MESH_Tunnel *tunnel, void **tunnel_ctx, return GNUNET_OK; } -static struct GNUNET_MESH_MessageHandler handlers[] = { {&callback, 1, 0}, -{NULL, 0, 0} + +static struct GNUNET_MESH_MessageHandler handlers[] = { + { &callback, 1, 0 }, + { NULL, 0, 0 } }; @@ -80,113 +81,58 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { GNUNET_MESH_disconnect (mesh); } - if (0 != GNUNET_OS_process_kill (arm_pid, SIGTERM)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); - } - GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (arm_pid)); - GNUNET_OS_process_destroy (arm_pid); } + static void do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { - if (0 != test_task) - { - GNUNET_SCHEDULER_cancel (test_task); - } result = GNUNET_SYSERR; abort_task = 0; do_shutdown (cls, tc); } + static void -test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) { - struct GNUNET_CONFIGURATION_Handle *cfg = cls; static const GNUNET_MESH_ApplicationType app[] = - { 1, 2, 3, 4, 5, 6, 7, 8, 0 }; + { 1, 2, 3, 4, 5, 6, 7, 8, 0 }; - test_task = (GNUNET_SCHEDULER_TaskIdentifier) 0; - mesh = GNUNET_MESH_connect (cfg, 10, NULL, NULL, NULL, handlers, app); + mesh = GNUNET_MESH_connect (cfg, NULL, NULL, NULL, handlers, app); if (NULL == mesh) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "test: Couldn't connect to mesh :(\n"); + result = GNUNET_SYSERR; return; } else { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: YAY! CONNECTED TO MESH :D\n"); } - t = GNUNET_MESH_tunnel_create (mesh, NULL, NULL, NULL, NULL); - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5), &do_shutdown, NULL); -} - - -static void -run (void *cls, char *const *args, const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - GNUNET_log_setup ("test_mesh_api", -#if VERBOSE - "DEBUG", -#else - "WARNING", -#endif - NULL); - arm_pid = - GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-service-arm", - "gnunet-service-arm", -#if VERBOSE_ARM - "-L", "DEBUG", -#endif - "-c", "test_mesh.conf", NULL); - abort_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20), &do_abort, NULL); - - test_task = GNUNET_SCHEDULER_add_now (&test, (void *) cfg); - } int main (int argc, char *argv[]) { - int ret; - - char *const argv2[] = { "test-mesh-api", - "-c", "test_mesh.conf", -#if VERBOSE - "-L", "DEBUG", -#endif - NULL - }; - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - - ret = - GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2, - "test-mesh-api", "nohelp", options, &run, NULL); - - if (GNUNET_OK != ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "run failed with error code %d\n", - ret); - return 1; - } - if (GNUNET_SYSERR == result) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "test failed\n"); + result = GNUNET_OK; + if (0 != GNUNET_TESTING_peer_run ("test-mesh-api", + "test_mesh.conf", + &run, NULL)) return 1; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test ok\n"); - return 0; + return (result == GNUNET_OK) ? 0 : 1; } + +/* end of test_mesh_api.c */ + diff --git a/src/mesh/test_mesh_local_1.c b/src/mesh/test_mesh_local_1.c index d80bee8..4f40688 100644 --- a/src/mesh/test_mesh_local_1.c +++ b/src/mesh/test_mesh_local_1.c @@ -19,7 +19,7 @@ */ /** - * @file mesh/test_mesh_local.c + * @file mesh/test_mesh_local_1.c * @brief test mesh local: test of tunnels with just one peer * @author Bartlomiej Polot */ @@ -27,21 +27,24 @@ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_dht_service.h" +#include "gnunet_testing_lib.h" #include "gnunet_mesh_service.h" -#define VERBOSE 1 -#define VERBOSE_ARM 0 -static struct GNUNET_OS_Process *arm_pid; static struct GNUNET_MESH_Handle *mesh_peer_1; + static struct GNUNET_MESH_Handle *mesh_peer_2; + static struct GNUNET_MESH_Tunnel *t; + static unsigned int one = 1; + static unsigned int two = 2; -static int result; +static int result = GNUNET_OK; + static GNUNET_SCHEDULER_TaskIdentifier abort_task; -static GNUNET_SCHEDULER_TaskIdentifier test_task; + static GNUNET_SCHEDULER_TaskIdentifier shutdown_task; @@ -70,14 +73,6 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { GNUNET_MESH_disconnect (mesh_peer_2); } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: arm\n"); - if (0 != GNUNET_OS_process_kill (arm_pid, SIGTERM)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Wait\n"); - GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (arm_pid)); - GNUNET_OS_process_destroy (arm_pid); } @@ -88,10 +83,6 @@ static void do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: ABORT\n"); - if (0 != test_task) - { - GNUNET_SCHEDULER_cancel (test_task); - } result = GNUNET_SYSERR; abort_task = 0; if (GNUNET_SCHEDULER_NO_TASK != shutdown_task) @@ -244,18 +235,21 @@ do_find (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) /** - * Main test function + * Initialize framework and start test */ static void -test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) { - struct GNUNET_CONFIGURATION_Handle *cfg = cls; static const GNUNET_MESH_ApplicationType app1[] = { 1, 0 }; static const GNUNET_MESH_ApplicationType app2[] = { 0 }; - test_task = GNUNET_SCHEDULER_NO_TASK; + abort_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 20), &do_abort, + NULL); mesh_peer_1 = GNUNET_MESH_connect (cfg, /* configuration */ - 10, /* queue size */ (void *) &one, /* cls */ &inbound_tunnel, /* inbound new hndlr */ &inbound_end, /* inbound end hndlr */ @@ -263,7 +257,6 @@ test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) app1); /* apps offered */ mesh_peer_2 = GNUNET_MESH_connect (cfg, /* configuration */ - 10, /* queue size */ (void *) &two, /* cls */ &inbound_tunnel, /* inbound new hndlr */ &inbound_end, /* inbound end hndlr */ @@ -272,13 +265,13 @@ test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) if (NULL == mesh_peer_1 || NULL == mesh_peer_2) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "test: Couldn't connect to mesh :(\n"); + result = GNUNET_SYSERR; return; } else { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: YAY! CONNECTED TO MESH :D\n"); } - t = GNUNET_MESH_tunnel_create (mesh_peer_2, NULL, &peer_conected, &peer_disconnected, (void *) &two); GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &do_find, NULL); @@ -286,72 +279,16 @@ test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) /** - * Initialize framework and start test - */ -static void -run (void *cls, char *const *args, const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - GNUNET_log_setup ("test_mesh_local", -#if VERBOSE - "DEBUG", -#else - "WARNING", -#endif - NULL); - arm_pid = - GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-service-arm", - "gnunet-service-arm", -#if VERBOSE_ARM - "-L", "DEBUG", -#endif - "-c", "test_mesh.conf", NULL); - - abort_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 20), &do_abort, - NULL); - - test_task = GNUNET_SCHEDULER_add_now (&test, (void *) cfg); - -} - - -/** * Main */ int main (int argc, char *argv[]) { - int ret; - - char *const argv2[] = { "test-mesh-local", - "-c", "test_mesh.conf", -#if VERBOSE - "-L", "DEBUG", -#endif - NULL - }; - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - - result = GNUNET_OK; - ret = - GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2, - "test-mesh-local", "nohelp", options, &run, NULL); - - if (GNUNET_OK != ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "run failed with error code %d\n", - ret); + if (0 != GNUNET_TESTING_peer_run ("test-mesh-local-1", + "test_mesh.conf", + &run, NULL)) return 1; - } - if (GNUNET_SYSERR == result) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "test failed\n"); - return 1; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test ok\n"); - return 0; + return (result == GNUNET_OK) ? 0 : 1; } + +/* end of test_mesh_local_1.c */ diff --git a/src/mesh/test_mesh_local_2.c b/src/mesh/test_mesh_local_2.c index d495b71..935c3b2 100644 --- a/src/mesh/test_mesh_local_2.c +++ b/src/mesh/test_mesh_local_2.c @@ -26,21 +26,24 @@ #include "platform.h" #include "gnunet_util_lib.h" +#include "gnunet_testing_lib.h" #include "gnunet_dht_service.h" #include "gnunet_mesh_service.h" -#define VERBOSE 1 -#define VERBOSE_ARM 0 - -static struct GNUNET_OS_Process *arm_pid; static struct GNUNET_MESH_Handle *mesh_peer_1; + static struct GNUNET_MESH_Handle *mesh_peer_2; + static struct GNUNET_MESH_Tunnel *t; + static unsigned int one = 1; + static unsigned int two = 2; -static int result; +static int result = GNUNET_OK; + static GNUNET_SCHEDULER_TaskIdentifier abort_task; + static GNUNET_SCHEDULER_TaskIdentifier test_task; @@ -69,14 +72,6 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { GNUNET_MESH_disconnect (mesh_peer_2); } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: arm\n"); - if (0 != GNUNET_OS_process_kill (arm_pid, SIGTERM)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Wait\n"); - GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (arm_pid)); - GNUNET_OS_process_destroy (arm_pid); } @@ -226,12 +221,11 @@ static struct GNUNET_MESH_MessageHandler handlers2[] = { {NULL, 0, 0} }; static void do_connect_peer_1 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { - struct GNUNET_CONFIGURATION_Handle *cfg = cls; + const struct GNUNET_CONFIGURATION_Handle *cfg = cls; static const GNUNET_MESH_ApplicationType app1[] = { 1, 0 }; test_task = GNUNET_SCHEDULER_NO_TASK; mesh_peer_1 = GNUNET_MESH_connect (cfg, /* configuration */ - 10, /* queue size */ (void *) &one, /* cls */ &inbound_tunnel, /* inbound new hndlr */ &inbound_end, /* inbound end hndlr */ @@ -241,18 +235,20 @@ do_connect_peer_1 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) /** - * Main test function + * Initialize framework and start test */ static void -test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) { - struct GNUNET_CONFIGURATION_Handle *cfg = cls; static const GNUNET_MESH_ApplicationType app2[] = { 0 }; - test_task = GNUNET_SCHEDULER_NO_TASK; - + abort_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 20), &do_abort, + NULL); mesh_peer_2 = GNUNET_MESH_connect (cfg, /* configuration */ - 10, /* queue size */ (void *) &two, /* cls */ &inbound_tunnel, /* inbound new hndlr */ &inbound_end, /* inbound end hndlr */ @@ -261,52 +257,20 @@ test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) if (NULL == mesh_peer_2) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "test: Couldn't connect to mesh :(\n"); + result = GNUNET_SYSERR; return; } else { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: YAY! CONNECTED TO MESH :D\n"); } - t = GNUNET_MESH_tunnel_create (mesh_peer_2, NULL, &peer_conected, &peer_disconnected, (void *) &two); GNUNET_MESH_peer_request_connect_by_type (t, 1); test_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5), - &do_connect_peer_1, cfg); -} - - -/** - * Initialize framework and start test - */ -static void -run (void *cls, char *const *args, const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - GNUNET_log_setup ("test_mesh_local", -#if VERBOSE - "DEBUG", -#else - "WARNING", -#endif - NULL); - arm_pid = - GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-service-arm", - "gnunet-service-arm", -#if VERBOSE_ARM - "-L", "DEBUG", -#endif - "-c", "test_mesh.conf", NULL); - - abort_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 20), &do_abort, - NULL); - - test_task = GNUNET_SCHEDULER_add_now (&test, (void *) cfg); - + &do_connect_peer_1, (void*) cfg); } @@ -316,34 +280,11 @@ run (void *cls, char *const *args, const char *cfgfile, int main (int argc, char *argv[]) { - int ret; - - char *const argv2[] = { "test-mesh-local", - "-c", "test_mesh.conf", -#if VERBOSE - "-L", "DEBUG", -#endif - NULL - }; - struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - - ret = - GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2, - "test-mesh-local", "nohelp", options, &run, NULL); - - if (GNUNET_OK != ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "run failed with error code %d\n", - ret); + if (0 != GNUNET_TESTING_peer_run ("test-mesh-local-2", + "test_mesh.conf", + &run, NULL)) return 1; - } - if (GNUNET_SYSERR == result) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "test failed\n"); - return 1; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test ok\n"); - return 0; + return (result == GNUNET_OK) ? 0 : 1; } + +/* end of test_mesh_local_2.c */ diff --git a/src/mesh/test_mesh_local_traffic.c b/src/mesh/test_mesh_local_traffic.c new file mode 100644 index 0000000..cd54ea8 --- /dev/null +++ b/src/mesh/test_mesh_local_traffic.c @@ -0,0 +1,526 @@ +/* + 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 mesh/test_mesh_local_traffic.c + * @brief test mesh local traffic: test of tunnels with just one peer + * @author Bartlomiej Polot + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_mesh_service.h" +#include "gnunet_testing_lib.h" +#include <gauger.h> + +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + +#define TARGET 1000 + +/** + * DIFFERENT TESTS TO RUN + */ +#define FWD 0 +#define BCK 1 +#define BOTH 2 + + +GNUNET_NETWORK_STRUCT_BEGIN + +struct test_traffic_message +{ + struct GNUNET_MessageHeader header; + uint32_t data GNUNET_PACKED; +}; + +GNUNET_NETWORK_STRUCT_END + + +/** Which test to run, based on executable name */ +static int test; + +static int started; + +/** How many packets to send from root to leaf */ +static unsigned int to_send_fwd; + +/** How many packets to send from leaf to root */ +static unsigned int to_send_bck; + +static unsigned int sent_fwd = 0; + +static unsigned int got_fwd = 0; + +static unsigned int sent_bck = 0; + +static unsigned int got_bck = 0; + +static struct GNUNET_MESH_Handle *mesh_peer_1; + +static struct GNUNET_MESH_Handle *mesh_peer_2; + +static struct GNUNET_MESH_Tunnel *t_fwd; + +static struct GNUNET_MESH_Tunnel *t_bck; + +static unsigned int one = 1; + +static unsigned int two = 2; + +static int result = GNUNET_SYSERR; + +static GNUNET_SCHEDULER_TaskIdentifier abort_task; + +static GNUNET_SCHEDULER_TaskIdentifier shutdown_task; + +static struct GNUNET_TIME_Absolute start_time; + +static struct GNUNET_TIME_Absolute end_time; + +static struct GNUNET_PeerIdentity peer_id; + + +/** + * Shutdown nicely + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "shutdown\n"); + if (0 != abort_task) + { + GNUNET_SCHEDULER_cancel (abort_task); + } + if (NULL != t_fwd) + { + GNUNET_MESH_tunnel_destroy(t_fwd); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "D1\n"); + if (NULL != mesh_peer_1) + { + GNUNET_MESH_disconnect (mesh_peer_1); + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "D2\n"); + if (NULL != mesh_peer_2) + { + GNUNET_MESH_disconnect (mesh_peer_2); + } +} + + +/** + * Something went wrong and timed out. Kill everything and set error flag + */ +static void +do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "ABORT\n"); + result = GNUNET_SYSERR; + abort_task = 0; + if (GNUNET_SCHEDULER_NO_TASK != shutdown_task) + { + GNUNET_SCHEDULER_cancel (shutdown_task); + shutdown_task = GNUNET_SCHEDULER_NO_TASK; + } + do_shutdown (cls, tc); +} + +static void +finish(void) +{ + if (GNUNET_SCHEDULER_NO_TASK != shutdown_task) + GNUNET_SCHEDULER_cancel(shutdown_task); + shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &do_shutdown, NULL); +} + + +/** + * Transmit ready callback. + * + * @param cls Closure (peer number of peer sending the data). + * @param size Buffer size. + * @param buf Buffer. + */ +static size_t +tmt_rdy (void *cls, size_t size, void *buf) +{ + unsigned int peer_number = *(unsigned int *) cls; + struct GNUNET_MessageHeader *m = buf; + struct GNUNET_MESH_Tunnel *t; + struct test_traffic_message *msg = buf; + size_t msize = sizeof (struct test_traffic_message); + unsigned int *sent; + unsigned int target; + char *s; + + if (0 == size || NULL == buf) + return 0; + + if (1 == peer_number) + { + sent = &sent_fwd; + target = to_send_fwd; + t = t_fwd; + s = "FWD"; + } + else if (2 == peer_number) + { + sent = &sent_bck; + target = to_send_bck; + t = t_bck; + s = "BCK"; + } + else + GNUNET_abort(); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending %s data packet # %4u\n", + s, *sent); + GNUNET_assert (size >= msize); + if (GNUNET_YES == started) + { + (*sent)++; + if (target > *sent) { + GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO, + GNUNET_TIME_UNIT_FOREVER_REL, + &peer_id, msize, &tmt_rdy, cls); + } + } + m->size = htons (msize); + m->type = htons (1); + msg->data = htonl (*sent - 1); + return msize; +} + + +/** + * Function is called whenever a message is received. + * + * @param cls closure (set from GNUNET_MESH_connect) + * @param tunnel connection to the other end + * @param tunnel_ctx place to store local state associated with the tunnel + * @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 +data_callback (void *cls, struct GNUNET_MESH_Tunnel *tunnel, void **tunnel_ctx, + const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi) +{ + struct test_traffic_message *msg; + unsigned int *peer_number = cls; + unsigned int *got; + unsigned int target; + + if (GNUNET_NO == started) + { + GNUNET_break (2 == *peer_number); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got initial data packet\n"); + started = GNUNET_YES; + start_time = GNUNET_TIME_absolute_get(); + if (FWD != test) // Send leaf -> root + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " sending first BCK data\n"); + GNUNET_MESH_notify_transmit_ready (t_bck, GNUNET_NO, + GNUNET_TIME_UNIT_FOREVER_REL, + NULL, + sizeof (struct test_traffic_message), + &tmt_rdy, &two); + } + if (BCK != test) // Send root -> leaf + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " sending first FWD data\n"); + GNUNET_MESH_notify_transmit_ready (t_fwd, GNUNET_NO, + GNUNET_TIME_UNIT_FOREVER_REL, + &peer_id, + sizeof (struct test_traffic_message), + &tmt_rdy, &one); + } + return GNUNET_OK; + } + + if (*peer_number == 1) + { + got = &got_bck; + target = to_send_bck; + } + else if (*peer_number == 2) + { + got = &got_fwd; + target = to_send_fwd; + } + else + { + GNUNET_abort(); + } + msg = (struct test_traffic_message *) message; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got data packet # %u [%u]\n", + ntohl (msg->data), *got + 1); + (*got)++; + if (target == *got) + { + if (to_send_bck == sent_bck && to_send_fwd == sent_fwd) { + end_time = GNUNET_TIME_absolute_get(); + result = GNUNET_OK; + finish(); + } + return GNUNET_OK; + } + if (GNUNET_SCHEDULER_NO_TASK != shutdown_task) + GNUNET_SCHEDULER_cancel (shutdown_task); + shutdown_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &do_shutdown, + NULL); + return GNUNET_OK; +} + + +/** + * Method called whenever another peer has added us to a tunnel + * the other peer initiated. + * + * @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 (void *cls, struct GNUNET_MESH_Tunnel *tunnel, + const struct GNUNET_PeerIdentity *initiator, + const struct GNUNET_ATS_Information *atsi) +{ + unsigned int id = *(unsigned int *) cls; + + t_bck = tunnel; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "received incoming tunnel %p\n", tunnel); + if (id != 2) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "received incoming tunnel on peer 1\n"); + result = GNUNET_SYSERR; + } + 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 + */ +static void +inbound_end (void *cls, const struct GNUNET_MESH_Tunnel *tunnel, + void *tunnel_ctx) +{ + unsigned int id = *(unsigned int *) cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "incoming tunnel closed\n"); + if (id != 2) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "received closing tunnel on peer 1\n"); + result = GNUNET_SYSERR; + } +} + + +/** + * Method called whenever a peer has connected to the tunnel. + * + * @param cls Closure. + * @param peer Peer identity of connected peer. + */ +static void +peer_connected (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *atsi) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "peer connected\n"); + peer_id = *peer; + /* Force an inbound tunnel notification on peer 2 */ + GNUNET_MESH_notify_transmit_ready (t_fwd, GNUNET_NO, GNUNET_TIME_UNIT_FOREVER_REL, + peer, sizeof (struct test_traffic_message), + &tmt_rdy, &one); +} + + +/** + * Method called whenever a peer has connected to the tunnel. + * + * @param cls closure + * @param peer peer identity the tunnel was created to, NULL on timeout + * @param atsi performance data for the connection + */ +static void +peer_disconnected (void *cls, const struct GNUNET_PeerIdentity *peer) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "peer disconnected\n"); +} + + +/** + * Handler array for traffic received on peer1 + */ +static struct GNUNET_MESH_MessageHandler handlers[] = { + {&data_callback, 1, sizeof (struct test_traffic_message)}, + {NULL, 0, 0} +}; + + +/** + * Handler array for traffic received on peer2 (none expected) + */ +static struct GNUNET_MESH_MessageHandler handlers_null[] = { {NULL, 0, 0} }; + + +/** + * Initialize framework and start test + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + static const GNUNET_MESH_ApplicationType app1[] = { 0 }; + static const GNUNET_MESH_ApplicationType app2[] = { 1, 0 }; + + abort_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_SECONDS, 20), &do_abort, + NULL); + mesh_peer_1 = GNUNET_MESH_connect (cfg, /* configuration */ + (void *) &one, /* cls */ + NULL, /* inbound new hndlr */ + NULL, /* inbound end hndlr */ + /* traffic handlers */ + test == FWD ? handlers_null : handlers, + app1); /* apps offered */ + + mesh_peer_2 = GNUNET_MESH_connect (cfg, /* configuration */ + (void *) &two, /* cls */ + &inbound_tunnel, /* inbound new hndlr */ + &inbound_end, /* inbound end hndlr */ + handlers, /* traffic handlers */ + app2); /* apps offered */ + if (NULL == mesh_peer_1 || NULL == mesh_peer_2) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Couldn't connect to mesh\n"); + result = GNUNET_SYSERR; + return; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected to mesh\n"); + } + t_fwd = GNUNET_MESH_tunnel_create (mesh_peer_1, NULL, &peer_connected, + &peer_disconnected, (void *) &two); + GNUNET_MESH_peer_request_connect_by_type (t_fwd, 1); +} + + +/** + * Main + */ +int +main (int argc, char *argv[]) +{ + if (strstr (argv[0], "test_mesh_local_traffic_fwd") != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "FWD\n"); + test = FWD; + to_send_fwd = TARGET; + } + else if (strstr (argv[0], "test_mesh_local_traffic_bck") != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "BCK\n"); + test = BCK; + to_send_bck = TARGET; + } + else if (strstr (argv[0], "test_mesh_local_traffic_both") != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "BOTH\n"); + test = BOTH; + to_send_bck = to_send_fwd = TARGET; + } + else + { + return 1; + } + + if (0 != GNUNET_TESTING_peer_run ("test-mesh-local-traffic", + "test_mesh.conf", + &run, NULL)) + return 1; + if (result != GNUNET_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed.\nFWD expected: %u, Sent: %u, Got: %u\n", + to_send_fwd, sent_fwd, got_fwd); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "BCK expected: %u, Sent: %u, Got: %u\n", + to_send_bck, sent_bck, got_bck); + return 1; + } + else + { + struct GNUNET_TIME_Relative total_time; + unsigned int total_traffic; + char *name; + + total_traffic = BOTH == test ? 2 * TARGET : TARGET; + switch (test) + { + case FWD: + name = "Local traffic Root to Leaf"; + break; + case BCK: + name = "Local traffic Leaf to Root"; + break; + case BOTH: + name = "Local traffic bidirectional"; + break; + default: + GNUNET_assert (0); + } + + total_time = GNUNET_TIME_absolute_get_difference(start_time, end_time); + FPRINTF (stderr, "\nTest time %llu ms\n", + (unsigned long long) total_time.rel_value); + FPRINTF (stderr, "Test payload bandwidth: %f kb/s\n", + total_traffic * 4.0 / total_time.rel_value); // 4bytes * kb/ms + FPRINTF (stderr, "Test throughput: %f packets/s\n\n", + total_traffic * 1000.0 / total_time.rel_value); // 1000 packets * ms + GAUGER ("MESH", + name, + total_traffic * 1000.0 / total_time.rel_value, + "packets/s"); + } + return 0; +} + +/* end of test_mesh_local_traffic.c */ diff --git a/src/mesh/test_mesh_path.conf b/src/mesh/test_mesh_path.conf deleted file mode 100644 index c3d8a2c..0000000 --- a/src/mesh/test_mesh_path.conf +++ /dev/null @@ -1,69 +0,0 @@ -[fs] -AUTOSTART = NO - -[resolver] -AUTOSTART = NO - -[mesh] -DEBUG = YES -AUTOSTART = YES -ACCEPT_FROM = 127.0.0.1; -HOSTNAME = localhost -PORT = 10511 -# PREFIX = valgrind --leak-check=full -# PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args - -[dht] -DEBUG = NO -AUTOSTART = YES -ACCEPT_FROM6 = ::1; -ACCEPT_FROM = 127.0.0.1; -HOSTNAME = localhost -PORT = 12100 - -[block] -plugins = dht test - -[dhtcache] -QUOTA = 1 MB -DATABASE = sqlite - -[transport] -PLUGINS = tcp -DEBUG = NO -ACCEPT_FROM6 = ::1; -ACCEPT_FROM = 127.0.0.1; -NEIGHBOUR_LIMIT = 50 -PORT = 12365 - -[ats] -WAN_QUOTA_OUT = 3932160 -WAN_QUOTA_IN = 3932160 - -[core] -PORT = 12092 - -[arm] -DEFAULTSERVICES = core -PORT = 12366 -DEBUG = NO - -[transport-tcp] -TIMEOUT = 300 s -PORT = 12368 - -[TESTING] -WEAKRANDOM = YES - -[gnunetd] -HOSTKEY = $SERVICEHOME/.hostkey - -[PATHS] -DEFAULTCONFIG = test_mesh.conf -SERVICEHOME = /tmp/test-mesh/ - -[dns] -AUTOSTART = NO - -[nse] -AUTOSTART = NO diff --git a/src/mesh/test_mesh_regex.c b/src/mesh/test_mesh_regex.c new file mode 100644 index 0000000..d7089da --- /dev/null +++ b/src/mesh/test_mesh_regex.c @@ -0,0 +1,444 @@ +/* + 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 mesh/test_mesh_regex.c + * + * @brief Test for regex announce / by_string connect. + * based on the 2dtorus testcase + */ +#include "platform.h" +#include "mesh_test_lib.h" +#include "gnunet_mesh_service.h" + +#define REMOVE_DIR GNUNET_YES +#define MESH_REGEX_PEERS 4 + +/** + * How long until we give up on connecting the peers? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300) + +/** + * Time to wait for stuff that should be rather fast + */ +#define SHORT_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60) + +/** + * Which strings have been found & connected. + */ +static int ok[MESH_REGEX_PEERS]; + +/** + * How many connects have happened. + */ +static int regex_peers; + +/** + * Total number of currently running peers. + */ +static unsigned long long peers_running; + +/** + * Task called to disconnect peers + */ +static GNUNET_SCHEDULER_TaskIdentifier disconnect_task; + +/** + * Task called to shutdown test. + */ +static GNUNET_SCHEDULER_TaskIdentifier shutdown_handle; + +/** + * Mesh handle for connecting peer. + */ +static struct GNUNET_MESH_Handle *h1; + +/** + * Mesh handle for announcing peers. + */ +static struct GNUNET_MESH_Handle *h2[MESH_REGEX_PEERS]; + +/** + * Tunnel handles for announcing peer. + */ +static struct GNUNET_MESH_Tunnel *t[MESH_REGEX_PEERS]; + +/** + * Incoming tunnels for announcing peers. + */ +static struct GNUNET_MESH_Tunnel *incoming_t[MESH_REGEX_PEERS]; + +/** + * Test context (to shut down). + */ +struct GNUNET_MESH_TEST_Context *test_ctx; + +/** + * Regular expressions for the announces. + */ +static char *regexes[MESH_REGEX_PEERS] = {"(0|1)" + "(0|1)" + "23456789ABC", + + "0123456789A*BC", + + "1234567890123456789012340*123456789ABC*", + + "GNUNETVPN0001000IPEX401110011101100100000111(0|1)*"}; + + +/** + * Service strings to look for. + */ +static char *strings[MESH_REGEX_PEERS] = {"1123456789ABC", + + "0123456789AABC", + + "12345678901234567890123400123456789ABCCCC", + + "GNUNETVPN0001000IPEX401110011101100100000111"}; + + + +/** + * Task to run for shutdown: stops peers, ends test. + * + * @param cls Closure (not used). + * @param tc TaskContext. + * + */ +static void +shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Ending test.\n"); + shutdown_handle = GNUNET_SCHEDULER_NO_TASK; +} + + +/** + * Ends test: Disconnects peers and calls shutdown. + * @param cls Closure (not used). + * @param tc TaskContext. + */ +static void +disconnect_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + unsigned int i; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: disconnecting peers\n"); + + for (i = 0; i < MESH_REGEX_PEERS; i++) + { + GNUNET_MESH_tunnel_destroy (t[i]); + } + if (GNUNET_SCHEDULER_NO_TASK != shutdown_handle) + { + GNUNET_SCHEDULER_cancel (shutdown_handle); + } + GNUNET_MESH_TEST_cleanup (test_ctx); + shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, 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 + */ +static void +tunnel_cleaner (void *cls, const struct GNUNET_MESH_Tunnel *tunnel, + void *tunnel_ctx) +{ + long i = (long) cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Incoming tunnel disconnected at peer %d\n", + i); + return; +} + + +/** + * Method called whenever a tunnel falls apart. + * + * @param cls closure + * @param peer peer identity the tunnel stopped working with + */ +static void +dh (void *cls, const struct GNUNET_PeerIdentity *peer) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "peer %s disconnected\n", + GNUNET_i2s (peer)); + return; +} + +/** + * Function called to notify a client about the connection + * begin ready to queue more data. "buf" will be + * NULL and "size" zero if the connection was closed for + * writing in the meantime. + * + * @param cls closure + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +data_ready (void *cls, size_t size, void *buf) +{ + struct GNUNET_MessageHeader *m = buf; + + if (NULL == buf || size < sizeof(struct GNUNET_MessageHeader)) + return 0; + m->type = htons (1); + m->size = htons (sizeof(struct GNUNET_MessageHeader)); + return sizeof(struct GNUNET_MessageHeader); +} + +/** + * Method called whenever a peer connects to a tunnel. + * + * @param cls closure + * @param peer peer identity the tunnel was created to, NULL on timeout + * @param atsi performance data for the connection + */ +static void +ch (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *atsi) +{ + long i = (long) cls; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Peer connected: %s\n", + GNUNET_i2s (peer)); + regex_peers++; + + GNUNET_MESH_notify_transmit_ready(t[i], GNUNET_NO, + GNUNET_TIME_UNIT_FOREVER_REL, + peer, + sizeof(struct GNUNET_MessageHeader), + &data_ready, NULL); +} + +/** + * Method called whenever another peer has added us to a tunnel + * the other peer initiated. + * + * @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 * +incoming_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel, + const struct GNUNET_PeerIdentity *initiator, + const struct GNUNET_ATS_Information *atsi) +{ + long i = (long) cls; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Incoming tunnel from %s to peer %d\n", + GNUNET_i2s (initiator), i); + if ( (i >= 10L) && (i < 10L + MESH_REGEX_PEERS)) + { + incoming_t[i - 10L] = tunnel; + ok[i - 10L] = GNUNET_OK; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Incoming tunnel for unknown client %lu\n", (long) cls); + } + if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) + { + GNUNET_SCHEDULER_cancel (disconnect_task); + disconnect_task = + GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_peers, NULL); + } + return NULL; +} + +/** + * Function is called whenever a message is received. + * + * @param cls closure (set from GNUNET_MESH_connect) + * @param tunnel connection to the other end + * @param tunnel_ctx place to store local state associated with the tunnel + * @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) + */ +int +data_callback (void *cls, struct GNUNET_MESH_Tunnel *tunnel, void **tunnel_ctx, + const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi) +{ + unsigned int i; + long peer_number = (long) cls; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "got data on peer %ld!\n", peer_number); + for (i = 0; i < MESH_REGEX_PEERS; i++) + { + if (GNUNET_OK != ok[i]) { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "data from peer %u still missing!\n", i + 10); + return GNUNET_OK; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "test: EVERYONE GOT DATA, FINISHING!\n"); + if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) + { + GNUNET_SCHEDULER_cancel (disconnect_task); + } + disconnect_task = + GNUNET_SCHEDULER_add_now (&disconnect_peers, NULL); + return GNUNET_OK; +} + +/** + * Handlers, for diverse services + */ +static struct GNUNET_MESH_MessageHandler handlers[] = { + {&data_callback, 1, sizeof (struct GNUNET_MessageHeader)}, + {NULL, 0, 0} +}; + + +/** + * test main: start test when all peers are connected + * + * @param cls Closure. + * @param ctx Argument to give to GNUNET_MESH_TEST_cleanup on test end. + * @param num_peers Number of peers that are running. + * @param peers Array of peers. + * @param meshes Handle to each of the MESHs of the peers. + */ +static void +tmain (void *cls, + struct GNUNET_MESH_TEST_Context *ctx, + unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers, + struct GNUNET_MESH_Handle **meshes) +{ + unsigned int i; + + shutdown_handle = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, + &shutdown_task, NULL); + test_ctx = ctx; + if (16 != num_peers) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "running peers mismatch, aborting test!\n"); + GNUNET_MESH_TEST_cleanup (ctx); + return; + } + peers_running = num_peers; + disconnect_task = + GNUNET_SCHEDULER_add_delayed (TIMEOUT, &disconnect_peers, NULL); + +// h1 = GNUNET_MESH_connect (d->cfg, (void *) 1L, +// NULL, +// NULL, +// handlers, +// &app); + h1 = meshes[0]; + regex_peers = 0; + for (i = 0; i < MESH_REGEX_PEERS; i++) + { + ok[i] = GNUNET_NO; +// h2[i] = GNUNET_MESH_connect (d->cfg, (void *) (long) (i + 2), +// &incoming_tunnel, +// &tunnel_cleaner, +// handlers, +// &app); + h2[i] = meshes[10 + i]; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Announce REGEX %u: %s\n", i, regexes[i]); + GNUNET_MESH_announce_regex (h2[i], regexes[i], 1); + } + + for (i = 0; i < MESH_REGEX_PEERS; i++) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Create tunnel\n"); + t[i] = GNUNET_MESH_tunnel_create (h1, NULL, &ch, &dh, (void *) (long) i); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Connect by string %s\n", strings[i]); + GNUNET_MESH_peer_request_connect_by_string (t[i], strings[i]); + } + /* connect handler = success, timeout = error */ + +} + + +/** + * Main: start test + */ +int +main (int argc, char *argv[]) +{ + int result; + unsigned int i; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Start\n"); + + GNUNET_MESH_TEST_run ("test_mesh_regex", + "test_mesh_2dtorus.conf", + 16, + &tmain, + NULL, + &incoming_tunnel, + &tunnel_cleaner, + handlers, + NULL); + + result = GNUNET_OK; + for (i = 0; i < MESH_REGEX_PEERS; i++) + { + if (GNUNET_OK != ok[i]) + { + result = GNUNET_SYSERR; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "COULD NOT CONNECT TO %u: %s!\n", + i, strings[i]); + } + } + if (GNUNET_OK != result || regex_peers != MESH_REGEX_PEERS) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "FAILED! %u connected peers\n", + regex_peers); + return 1; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "success :)\n"); + return 0; +} + +/* end of test_mesh_regex.c */ diff --git a/src/mesh/test_mesh_small.c b/src/mesh/test_mesh_small.c index 974b635..efe9398 100644 --- a/src/mesh/test_mesh_small.c +++ b/src/mesh/test_mesh_small.c @@ -24,35 +24,25 @@ */ #include <stdio.h> #include "platform.h" -#include "gnunet_testing_lib.h" +#include "mesh_test_lib.h" #include "gnunet_mesh_service.h" #include <gauger.h> -#define VERBOSE GNUNET_YES -#define REMOVE_DIR GNUNET_YES - -struct MeshPeer -{ - struct MeshPeer *prev; - - struct MeshPeer *next; - - struct GNUNET_TESTING_Daemon *daemon; - - struct GNUNET_MESH_Handle *mesh_handle; -}; - +/** + * How namy messages to send + */ +#define TOTAL_PACKETS 1000 /** * How long until we give up on connecting the peers? */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1500) +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120) /** * Time to wait for stuff that should be rather fast */ -#define SHORT_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300) +#define SHORT_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20) /** * DIFFERENT TESTS TO RUN @@ -62,6 +52,9 @@ struct MeshPeer #define MULTICAST 2 #define SPEED 3 #define SPEED_ACK 4 +#define SPEED_MIN 5 +#define SPEED_NOBUF 6 +#define P2P_SIGNAL 10 /** * Which test are we running? @@ -69,64 +62,92 @@ struct MeshPeer static int test; /** + * String with test name + */ +char *test_name; + +/** + * Flag to send traffic leaf->root in speed tests to test BCK_ACK logic. + */ +static int test_backwards = GNUNET_NO; + +/** * How many events have happened */ static int ok; -static int peers_in_tunnel; + /** + * Each peer is supposed to generate the following callbacks: + * 1 incoming tunnel (@dest) + * 1 connected peer (@orig) + * 1 received data packet (@dest) + * 1 received data packet (@orig) + * 1 received tunnel destroy (@dest) + * _________________________________ + * 5 x ok expected per peer + */ +int ok_goal; -static int peers_responded; -static int data_sent; +/** + * Size of each test packet + */ +size_t size_payload = sizeof (struct GNUNET_MessageHeader) + sizeof (uint32_t); -static int data_received; +/** + * Operation to get peer ids. + */ +struct GNUNET_TESTBED_Operation *t_op[3]; -static int data_ack; +/** + * Peer ids. + */ +struct GNUNET_PeerIdentity *p_id[3]; /** - * Be verbose + * Peer ids counter. */ -static int verbose; +unsigned int p_ids; /** - * Total number of peers in the test. + * Is the setup initialized? */ -static unsigned long long num_peers; +static int initialized; /** - * Global configuration file + * Peers that have been connected */ -static struct GNUNET_CONFIGURATION_Handle *testing_cfg; +static int peers_in_tunnel; /** - * Total number of currently running peers. + * Peers that have responded */ -static unsigned long long peers_running; +static int peers_responded; /** - * Total number of connections in the whole network. + * Number of payload packes sent */ -static unsigned int total_connections; +static int data_sent; /** - * The currently running peer group. + * Number of payload packets received */ -static struct GNUNET_TESTING_PeerGroup *pg; +static int data_received; /** - * File to report results to. + * Number of payload packed explicitly (app level) acknowledged */ -static struct GNUNET_DISK_FileHandle *output_file; +static int data_ack; /** - * File to log connection info, statistics to. + * Total number of currently running peers. */ -static struct GNUNET_DISK_FileHandle *data_file; +static unsigned long long peers_running; /** - * How many data points to capture before triggering next round? + * Test context (to shut down). */ -static struct GNUNET_TIME_Relative wait_time; +struct GNUNET_MESH_TEST_Context *test_ctx; /** * Task called to disconnect peers. @@ -143,151 +164,189 @@ static GNUNET_SCHEDULER_TaskIdentifier test_task; */ static GNUNET_SCHEDULER_TaskIdentifier shutdown_handle; -static char *topology_file; - -static struct GNUNET_TESTING_Daemon *d1; - -static GNUNET_PEER_Id pid1; - -static struct GNUNET_TESTING_Daemon *d2; - -static struct GNUNET_TESTING_Daemon *d3; - +/** + * Mesh handle for the root peer + */ static struct GNUNET_MESH_Handle *h1; +/** + * Mesh handle for the first leaf peer + */ static struct GNUNET_MESH_Handle *h2; +/** + * Mesh handle for the second leaf peer + */ static struct GNUNET_MESH_Handle *h3; +/** + * Tunnel handle for the root peer + */ static struct GNUNET_MESH_Tunnel *t; +/** + * Tunnel handle for the first leaf peer + */ static struct GNUNET_MESH_Tunnel *incoming_t; +/** + * Tunnel handle for the second leaf peer + */ static struct GNUNET_MESH_Tunnel *incoming_t2; +/** + * Time we started the data transmission (after tunnel has been established + * and initilized). + */ static struct GNUNET_TIME_Absolute start_time; -static struct GNUNET_TIME_Absolute end_time; - -static struct GNUNET_TIME_Relative total_time; - /** - * Check whether peers successfully shut down. + * Show the results of the test (banwidth acheived) and log them to GAUGER */ static void -shutdown_callback (void *cls, const char *emsg) +show_end_data (void) { - if (emsg != NULL) - { -#if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Shutdown of peers failed!\n"); -#endif - ok--; - } - else - { -#if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "All peers successfully shut down!\n"); -#endif - } - GNUNET_CONFIGURATION_destroy (testing_cfg); + static struct GNUNET_TIME_Absolute end_time; + static struct GNUNET_TIME_Relative total_time; + + end_time = GNUNET_TIME_absolute_get(); + total_time = GNUNET_TIME_absolute_get_difference(start_time, end_time); + FPRINTF (stderr, "\nResults of test \"%s\"\n", test_name); + FPRINTF (stderr, "Test time %llu ms\n", + (unsigned long long) total_time.rel_value); + FPRINTF (stderr, "Test bandwidth: %f kb/s\n", + 4 * TOTAL_PACKETS * 1.0 / total_time.rel_value); // 4bytes * ms + FPRINTF (stderr, "Test throughput: %f packets/s\n\n", + TOTAL_PACKETS * 1000.0 / total_time.rel_value); // packets * ms + GAUGER ("MESH", test_name, + TOTAL_PACKETS * 1000.0 / total_time.rel_value, + "packets/s"); } /** * Shut down peergroup, clean up. + * + * @param cls Closure (unused). + * @param tc Task Context. */ static void shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { -#if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Ending test.\n"); -#endif - - if (disconnect_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = GNUNET_SCHEDULER_NO_TASK; - } - - if (NULL != h1) - { - GNUNET_MESH_disconnect (h1); - h1 = NULL; - } - if (NULL != h2) - { - GNUNET_MESH_disconnect (h2); - h2 = NULL; - } - if (test == MULTICAST && NULL != h3) - { - GNUNET_MESH_disconnect (h3); - h3 = NULL; - } - - if (data_file != NULL) - GNUNET_DISK_file_close (data_file); - GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending test.\n"); + shutdown_handle = GNUNET_SCHEDULER_NO_TASK; } /** * Disconnect from mesh services af all peers, call shutdown. + * + * @param cls Closure (unused). + * @param tc Task Context. */ static void disconnect_mesh_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { + long line = (long) cls; + unsigned int i; + + for (i = 0; i < 3; i++) + if (NULL != t_op[i]) + { + GNUNET_TESTBED_operation_done (t_op[i]); + t_op[i] = NULL; + } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "disconnecting mesh service of peers\n"); + "disconnecting mesh service of peers, called from line %ld\n", + line); disconnect_task = GNUNET_SCHEDULER_NO_TASK; if (NULL != t) { - GNUNET_MESH_tunnel_destroy(t); + GNUNET_MESH_tunnel_destroy (t); t = NULL; } if (NULL != incoming_t) { - GNUNET_MESH_tunnel_destroy(incoming_t); + GNUNET_MESH_tunnel_destroy (incoming_t); incoming_t = NULL; } if (NULL != incoming_t2) { - GNUNET_MESH_tunnel_destroy(incoming_t2); + GNUNET_MESH_tunnel_destroy (incoming_t2); incoming_t2 = NULL; } - GNUNET_MESH_disconnect (h1); - GNUNET_MESH_disconnect (h2); - h1 = h2 = NULL; - if (test == MULTICAST) - { - GNUNET_MESH_disconnect (h3); - h3 = NULL; - } + GNUNET_MESH_TEST_cleanup (test_ctx); if (GNUNET_SCHEDULER_NO_TASK != shutdown_handle) { GNUNET_SCHEDULER_cancel (shutdown_handle); - shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); } + shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); } -size_t + +/** + * Abort test: schedule disconnect and shutdown immediately + * + * @param line Line in the code the abort is requested from (__LINE__). + */ +void +abort_test (long line) +{ + if (disconnect_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (disconnect_task); + } + disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME, + &disconnect_mesh_peers, + (void *) line); +} + +/** + * Transmit ready callback. + * + * @param cls Closure (message type). + * @param size Size of the tranmist buffer. + * @param buf Pointer to the beginning of the buffer. + * + * @return Number of bytes written to buf. + */ +static size_t tmt_rdy (void *cls, size_t size, void *buf); + +/** + * Task to schedule a new data transmission. + * + * @param cls Closure (peer #). + * @param tc Task Context. + */ static void data_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct GNUNET_MESH_TransmitHandle *th; + struct GNUNET_MESH_Tunnel *tunnel; + struct GNUNET_PeerIdentity *destination; + if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0) return; - th = GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO, 0, - GNUNET_TIME_UNIT_FOREVER_REL, &d2->id, - sizeof (struct GNUNET_MessageHeader), - &tmt_rdy, (void *) 1L); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Data task\n"); + if (GNUNET_YES == test_backwards) + { + tunnel = incoming_t; + destination = p_id[0]; + } + else + { + tunnel = t; + destination = p_id[2]; + } + th = GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO, + GNUNET_TIME_UNIT_FOREVER_REL, + destination, + size_payload, + &tmt_rdy, (void *) 1L); if (NULL == th) { unsigned long i = (unsigned long) cls; @@ -311,10 +370,11 @@ data_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } } + /** * Transmit ready callback * - * @param cls Closure. + * @param cls Closure (message type). * @param size Size of the buffer we have. * @param buf Buffer to copy data to. */ @@ -322,24 +382,38 @@ size_t tmt_rdy (void *cls, size_t size, void *buf) { struct GNUNET_MessageHeader *msg = buf; + uint32_t *data; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " tmt_rdy called\n"); - if (size < sizeof (struct GNUNET_MessageHeader) || NULL == buf) + if (size < size_payload || NULL == buf) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "size %u, buf %p, data_sent %u, data_received %u\n", + size, + buf, + data_sent, + data_received); return 0; - msg->size = htons (sizeof (struct GNUNET_MessageHeader)); + } + msg->size = htons (size); msg->type = htons ((long) cls); - if (test == SPEED) + data = (uint32_t *) &msg[1]; + *data = htonl (data_sent); + if (SPEED == test && GNUNET_YES == initialized) { data_sent++; - if (data_sent < 1000) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " Sent packet %d\n", data_sent); + if (data_sent < TOTAL_PACKETS) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - " Scheduling %d packet\n", data_sent); + " Scheduling packet %d\n", data_sent + 1); GNUNET_SCHEDULER_add_now(&data_task, NULL); } } - return sizeof (struct GNUNET_MessageHeader); + return size_payload; } @@ -362,82 +436,121 @@ data_callback (void *cls, struct GNUNET_MESH_Tunnel *tunnel, void **tunnel_ctx, const struct GNUNET_ATS_Information *atsi) { long client = (long) cls; + long expected_target_client; + uint32_t *data; - switch (client) + ok++; + + if ((ok % 20) == 0) { - case 1L: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Origin client got a response!\n"); - ok++; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok); - peers_responded++; - data_ack++; if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) { GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = - GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, - NULL); } + disconnect_task = + GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, + (void *) __LINE__); + } + + switch (client) + { + case 0L: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Root client got a message!\n"); + peers_responded++; if (test == MULTICAST && peers_responded < 2) return GNUNET_OK; - if (test == SPEED_ACK) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - " received ack %u\n", data_ack); - GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO, 0, - GNUNET_TIME_UNIT_FOREVER_REL, sender, - sizeof (struct GNUNET_MessageHeader), - &tmt_rdy, (void *) 1L); - if (data_ack < 1000) - return GNUNET_OK; - end_time = GNUNET_TIME_absolute_get(); - total_time = GNUNET_TIME_absolute_get_difference(start_time, end_time); - FPRINTF (stderr, "\nTest time %llu ms\n", - (unsigned long long) total_time.rel_value); - FPRINTF (stderr, "Test bandwidth: %f kb/s\n", - 4000.0 / total_time.rel_value); - FPRINTF (stderr, "Test throughput: %f packets/s\n", - 1000000.0 / total_time.rel_value); - GAUGER ("MESH", "Tunnel 5 peers", 1000000.0 / total_time.rel_value, - "packets/s"); - } - GNUNET_assert (tunnel == t); - GNUNET_MESH_tunnel_destroy (t); - t = NULL; break; - case 2L: case 3L: + case 4L: GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Destination client %u got a message.\n", + "Leaf client %li got a message.\n", client); - ok++; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok); - if (SPEED != test) + client = 4L; + break; + default: + GNUNET_assert (0); + break; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: (%d/%d)\n", ok, ok_goal); + data = (uint32_t *) &message[1]; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, " payload: (%u)\n", ntohl (*data)); + if (SPEED == test && GNUNET_YES == test_backwards) + { + expected_target_client = 0L; + } + else + { + expected_target_client = 4L; + } + + if (GNUNET_NO == initialized) + { + initialized = GNUNET_YES; + start_time = GNUNET_TIME_absolute_get (); + if (SPEED == test) { - GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO, 0, + GNUNET_assert (4L == client); + GNUNET_SCHEDULER_add_now (&data_task, NULL); + return GNUNET_OK; + } + } + + if (client == expected_target_client) // Normally 3 or 4 + { + data_received++; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + " received data %u\n", data_received); + if (SPEED != test || (ok_goal - 2) == ok) + { + GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO, GNUNET_TIME_UNIT_FOREVER_REL, sender, - sizeof (struct GNUNET_MessageHeader), + size_payload, &tmt_rdy, (void *) 1L); + return GNUNET_OK; } else { - data_received++; + if (data_received < TOTAL_PACKETS) + return GNUNET_OK; + } + } + else // Normally 0 + { + if (test == SPEED_ACK || test == SPEED) + { + data_ack++; GNUNET_log (GNUNET_ERROR_TYPE_INFO, - " received data %u\n", data_received); - if (data_received < 1000) + " received ack %u\n", data_ack); + GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO, + GNUNET_TIME_UNIT_FOREVER_REL, sender, + size_payload, + &tmt_rdy, (void *) 1L); + if (data_ack < TOTAL_PACKETS && SPEED != test) return GNUNET_OK; + if (ok == 2 && SPEED == test) + return GNUNET_OK; + show_end_data(); } - if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) + if (test == P2P_SIGNAL) { - GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = - GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, - NULL); + GNUNET_MESH_tunnel_destroy (incoming_t); + incoming_t = NULL; } - break; - default: - break; + else + { + GNUNET_MESH_tunnel_destroy (t); + t = NULL; + } + } + + if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) + { + GNUNET_SCHEDULER_cancel (disconnect_task); } + disconnect_task = + GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, + (void *) __LINE__); + return GNUNET_OK; } @@ -472,7 +585,7 @@ incoming_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel, GNUNET_i2s (initiator), (long) cls); ok++; GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok); - if ((long) cls == 2L) + if ((long) cls == 4L) incoming_t = tunnel; else if ((long) cls == 3L) incoming_t2 = tunnel; @@ -480,13 +593,16 @@ incoming_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel, { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Incoming tunnel for unknown client %lu\n", (long) cls); + GNUNET_break(0); } if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) { GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = - GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, NULL); } + disconnect_task = + GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, + (void *) __LINE__); + return NULL; } @@ -508,7 +624,7 @@ tunnel_cleaner (void *cls, const struct GNUNET_MESH_Tunnel *tunnel, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Incoming tunnel disconnected at peer %d\n", i); - if (2L == i) + if (4L == i) { ok++; incoming_t = NULL; @@ -529,8 +645,9 @@ tunnel_cleaner (void *cls, const struct GNUNET_MESH_Tunnel *tunnel, if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) { GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_mesh_peers, NULL); } + disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_mesh_peers, + (void *) __LINE__); return; } @@ -548,6 +665,16 @@ dh (void *cls, const struct GNUNET_PeerIdentity *peer) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "peer %s disconnected\n", GNUNET_i2s (peer)); + if (P2P_SIGNAL == test) + { + ok ++; + if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) + { + GNUNET_SCHEDULER_cancel (disconnect_task); + } + disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_mesh_peers, + (void *) __LINE__); + } return; } @@ -563,52 +690,59 @@ static void ch (void *cls, const struct GNUNET_PeerIdentity *peer, const struct GNUNET_ATS_Information *atsi) { + long i = (long) cls; + struct GNUNET_PeerIdentity *dest; GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "peer %s connected\n", GNUNET_i2s (peer)); + "%ld peer %s connected\n", i, GNUNET_i2s (peer)); - if (0 == memcmp (&d2->id, peer, sizeof (d2->id)) && (long) cls == 1L) + if (0 == memcmp (p_id[2], peer, sizeof (struct GNUNET_PeerIdentity)) && + i == 0L) { ok++; } - if (test == MULTICAST && 0 == memcmp (&d3->id, peer, sizeof (d3->id)) && - (long) cls == 1L) + if (test == MULTICAST && + 0 == memcmp (p_id[1], peer, sizeof (struct GNUNET_PeerIdentity)) && + i == 0L) { ok++; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok); switch (test) { - case UNICAST: - case SPEED: - case SPEED_ACK: - dest = &d2->id; - break; - case MULTICAST: - peers_in_tunnel++; - if (peers_in_tunnel < 2) + case UNICAST: + case P2P_SIGNAL: + case SPEED: + case SPEED_ACK: + // incoming_t is NULL unless we send a relevant data packet + dest = p_id[2]; + break; + case MULTICAST: + peers_in_tunnel++; + if (peers_in_tunnel < 2) + return; + dest = NULL; + break; + default: + GNUNET_assert (0); return; - dest = NULL; - break; - default: - return; } if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) { GNUNET_SCHEDULER_cancel (disconnect_task); disconnect_task = - GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, NULL); + GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, + (void *) __LINE__); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending data...\n"); + "Sending data initializer...\n"); peers_responded = 0; data_ack = 0; data_received = 0; data_sent = 0; - start_time = GNUNET_TIME_absolute_get(); - GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO, 0, + GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO, GNUNET_TIME_UNIT_FOREVER_REL, dest, - sizeof (struct GNUNET_MessageHeader), + size_payload, &tmt_rdy, (void *) 1L); } else @@ -622,376 +756,210 @@ ch (void *cls, const struct GNUNET_PeerIdentity *peer, } +/** + * START THE TESTCASE ITSELF, AS WE ARE CONNECTED TO THE MESH SERVICES. + * + * Testcase continues when the root receives confirmation of connected peers, + * on callback funtion ch. + * + * @param cls Closure (unsued). + * @param tc Task Context. + */ static void do_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test_task\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "add peer 2\n"); + GNUNET_MESH_peer_request_connect_add (t, p_id[2]); + if (test == MULTICAST) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "add peer 3\n"); - GNUNET_MESH_peer_request_connect_add (t, &d3->id); + GNUNET_MESH_peer_request_connect_add (t, p_id[1]); } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "add peer 2\n"); - GNUNET_MESH_peer_request_connect_add (t, &d2->id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "schedule timeout in 90s\n"); + "schedule timeout in TIMEOUT\n"); if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) { GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = - GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, NULL); } + disconnect_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &disconnect_mesh_peers, + (void *) __LINE__); } - /** - * connect_mesh_service: connect to the mesh service of one of the peers + * Callback to be called when the requested peer information is available * - */ -static void -connect_mesh_service (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) + * @param cls the closure from GNUNET_TETSBED_peer_get_information() + * @param op the operation this callback corresponds to + * @param pinfo the result; will be NULL if the operation has failed + * @param emsg error message if the operation has failed; + * NULL if the operation is successfull + */ +void +pi_cb (void *cls, + struct GNUNET_TESTBED_Operation *op, + const struct GNUNET_TESTBED_PeerInformation *pinfo, + const char *emsg) { - GNUNET_MESH_ApplicationType app; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "connect_mesh_service\n"); - - d2 = GNUNET_TESTING_daemon_get (pg, 4); - if (test == MULTICAST) - { - d3 = GNUNET_TESTING_daemon_get (pg, 3); - } - app = (GNUNET_MESH_ApplicationType) 0; - -#if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "connecting to mesh service of peer %s\n", - GNUNET_i2s (&d1->id)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "connecting to mesh service of peer %s\n", - GNUNET_i2s (&d2->id)); - if (test == MULTICAST) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "connecting to mesh service of peer %s\n", - GNUNET_i2s (&d3->id)); - } -#endif - h1 = GNUNET_MESH_connect (d1->cfg, 5, (void *) 1L, NULL, &tunnel_cleaner, - handlers, &app); - h2 = GNUNET_MESH_connect (d2->cfg, 5, (void *) 2L, &incoming_tunnel, - &tunnel_cleaner, handlers, &app); - if (test == MULTICAST) - { - h3 = GNUNET_MESH_connect (d3->cfg, 5, (void *) 3L, &incoming_tunnel, - &tunnel_cleaner, handlers, &app); - } - t = GNUNET_MESH_tunnel_create (h1, NULL, &ch, &dh, (void *) 1L); - peers_in_tunnel = 0; - test_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 1), &do_test, - NULL); -} - - - -/** - * peergroup_ready: start test when all peers are connected - * @param cls closure - * @param emsg error message - */ -static void -peergroup_ready (void *cls, const char *emsg) -{ - char *buf; - int buf_len; - unsigned int i; + long i = (long) cls; - if (emsg != NULL) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "id callback for %ld\n", i); + if (NULL == pinfo || NULL != emsg) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Peergroup callback called with error, aborting test!\n"); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Error from testing: `%s'\n", emsg); - ok--; - GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "pi_cb: %s\n", emsg); + abort_test (__LINE__); return; } -#if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "************************************************************\n"); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Peer Group started successfully!\n"); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Have %u connections\n", - total_connections); -#endif - - if (data_file != NULL) - { - buf = NULL; - buf_len = GNUNET_asprintf (&buf, "CONNECTIONS_0: %u\n", total_connections); - if (buf_len > 0) - GNUNET_DISK_file_write (data_file, buf, buf_len); - GNUNET_free (buf); - } - peers_running = GNUNET_TESTING_daemons_running (pg); - for (i = 0; i < num_peers; i++) - { - GNUNET_PEER_Id peer_id; - - d1 = GNUNET_TESTING_daemon_get (pg, i); - peer_id = GNUNET_PEER_intern (&d1->id); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " %u: %s\n", - peer_id, GNUNET_i2s (&d1->id)); - } - d1 = GNUNET_TESTING_daemon_get (pg, 0); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Peer looking: %s\n", - GNUNET_i2s (&d1->id)); - pid1 = GNUNET_PEER_intern (&d1->id); - - GNUNET_SCHEDULER_add_now (&connect_mesh_service, NULL); - disconnect_task = - GNUNET_SCHEDULER_add_delayed (wait_time, &disconnect_mesh_peers, NULL); - + p_id[i] = pinfo->result.id; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " id: %s\n", GNUNET_i2s (p_id[i])); + p_ids++; + if ((MULTICAST == test && p_ids < 3) || p_ids < 2) + return; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got all IDs, starting test\n"); + test_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &do_test, NULL); } - /** - * Function that will be called whenever two daemons are connected by - * the testing library. + * test main: start test when all peers are connected * - * @param cls closure - * @param first peer id for first daemon - * @param second peer id for the second daemon - * @param distance distance between the connected peers - * @param first_cfg config for the first daemon - * @param second_cfg config for the second daemon - * @param first_daemon handle for the first daemon - * @param second_daemon handle for the second daemon - * @param emsg error message (NULL on success) - */ -static void -connect_cb (void *cls, const struct GNUNET_PeerIdentity *first, - const struct GNUNET_PeerIdentity *second, uint32_t distance, - const struct GNUNET_CONFIGURATION_Handle *first_cfg, - const struct GNUNET_CONFIGURATION_Handle *second_cfg, - struct GNUNET_TESTING_Daemon *first_daemon, - struct GNUNET_TESTING_Daemon *second_daemon, const char *emsg) -{ - if (emsg == NULL) - { - total_connections++; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Problem with new connection (%s)\n", - emsg); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " (%s)\n", GNUNET_i2s (first)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " (%s)\n", GNUNET_i2s (second)); - } - -} - - -/** - * run: load configuration options and schedule test to run (start peergroup) - * @param cls closure - * @param args argv - * @param cfgfile configuration file name (can be NULL) - * @param cfg configuration handle + * @param cls Closure. + * @param ctx Argument to give to GNUNET_MESH_TEST_cleanup on test end. + * @param num_peers Number of peers that are running. + * @param peers Array of peers. + * @param meshes Handle to each of the MESHs of the peers. */ static void -run (void *cls, char *const *args, const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *cfg) +tmain (void *cls, + struct GNUNET_MESH_TEST_Context *ctx, + unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers, + struct GNUNET_MESH_Handle **meshes) { - char *temp_str; - struct GNUNET_TESTING_Host *hosts; - char *data_filename; - + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test main\n"); ok = 0; - testing_cfg = GNUNET_CONFIGURATION_dup (cfg); - - GNUNET_log_setup ("test_mesh_small", -#if VERBOSE - "DEBUG", -#else - "WARNING", -#endif - NULL); - -#if VERBOSE - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Starting daemons.\n"); - GNUNET_CONFIGURATION_set_value_string (testing_cfg, "testing", - "use_progressbars", "YES"); -#endif - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (testing_cfg, "testing", - "num_peers", &num_peers)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Option TESTING:NUM_PEERS is required!\n"); - return; + test_ctx = ctx; + peers_running = num_peers; + h1 = meshes[0]; + h2 = meshes[num_peers - 1]; + t = GNUNET_MESH_tunnel_create (h1, NULL, &ch, &dh, (void *) 0L); + if (SPEED_MIN == test) + { + GNUNET_MESH_tunnel_speed_min(t); + test = SPEED; } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (testing_cfg, "test_mesh_small", - "WAIT_TIME", &wait_time)) + if (SPEED_NOBUF == test) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Option test_mesh_small:wait_time is required!\n"); - return; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (testing_cfg, "testing", - "topology_output_file", - &topology_file)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Option test_mesh_small:topology_output_file is required!\n"); - return; + GNUNET_MESH_tunnel_buffer(t, GNUNET_NO); + test = SPEED; } - - if (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (testing_cfg, "test_mesh_small", - "data_output_file", - &data_filename)) - { - data_file = - GNUNET_DISK_file_open (data_filename, - GNUNET_DISK_OPEN_READWRITE | - GNUNET_DISK_OPEN_CREATE, - GNUNET_DISK_PERM_USER_READ | - GNUNET_DISK_PERM_USER_WRITE); - if (data_file == NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n", - data_filename); - GNUNET_free (data_filename); - } + peers_in_tunnel = 0; + disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME, + &disconnect_mesh_peers, + (void *) __LINE__); + shutdown_handle = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, + &shutdown_task, NULL); + t_op[0] = GNUNET_TESTBED_peer_get_information (peers[0], + GNUNET_TESTBED_PIT_IDENTITY, + &pi_cb, (void *) 0L); + t_op[2] = GNUNET_TESTBED_peer_get_information (peers[num_peers - 1], + GNUNET_TESTBED_PIT_IDENTITY, + &pi_cb, (void *) 2L); + if (MULTICAST == test) + { + h3 = meshes[num_peers - 2]; + t_op[1] = GNUNET_TESTBED_peer_get_information (peers[num_peers - 2], + GNUNET_TESTBED_PIT_IDENTITY, + &pi_cb, (void *) 1L); } - - if (GNUNET_YES == - GNUNET_CONFIGURATION_get_value_string (cfg, "test_mesh_small", - "output_file", &temp_str)) + else { - output_file = - GNUNET_DISK_file_open (temp_str, - GNUNET_DISK_OPEN_READWRITE | - GNUNET_DISK_OPEN_CREATE, - GNUNET_DISK_PERM_USER_READ | - GNUNET_DISK_PERM_USER_WRITE); - if (output_file == NULL) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n", - temp_str); + t_op[1] = NULL; } - GNUNET_free_non_null (temp_str); - - hosts = GNUNET_TESTING_hosts_load (testing_cfg); - - pg = GNUNET_TESTING_peergroup_start (testing_cfg, num_peers, TIMEOUT, - &connect_cb, &peergroup_ready, NULL, - hosts); - GNUNET_assert (pg != NULL); - shutdown_handle = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, - &shutdown_task, NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "requested peer ids\n"); } - -/** - * test_mesh_small command line options - */ -static struct GNUNET_GETOPT_CommandLineOption options[] = { - {'V', "verbose", NULL, - gettext_noop ("be verbose (print progress information)"), - 0, &GNUNET_GETOPT_set_one, &verbose}, - GNUNET_GETOPT_OPTION_END -}; - - /** * Main: start test */ int main (int argc, char *argv[]) { - char * argv2[] = { - argv[0], - "-c", - "test_mesh_small.conf", -#if VERBOSE - "-L", - "DEBUG", -#endif - NULL - }; - int argc2 = (sizeof (argv2) / sizeof (char *)) - 1; - - /* Each peer is supposed to generate the following callbacks: - * 1 incoming tunnel (@dest) - * 1 connected peer (@orig) - * 1 received data packet (@dest) - * 1 received data packet (@orig) - * 1 received tunnel destroy (@dest) - * _________________________________ - * 5 x ok expected per peer - */ - int ok_goal; + initialized = GNUNET_NO; + GNUNET_log_setup ("test", "DEBUG", NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start\n"); if (strstr (argv[0], "test_mesh_small_unicast") != NULL) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "UNICAST\n"); test = UNICAST; + test_name = "unicast"; ok_goal = 5; } else if (strstr (argv[0], "test_mesh_small_multicast") != NULL) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MULTICAST\n"); test = MULTICAST; + test_name = "multicast"; ok_goal = 10; } + else if (strstr (argv[0], "test_mesh_small_signal") != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SIGNAL\n"); + test = P2P_SIGNAL; + test_name = "signal"; + ok_goal = 5; + } else if (strstr (argv[0], "test_mesh_small_speed_ack") != NULL) { /* Each peer is supposed to generate the following callbacks: * 1 incoming tunnel (@dest) * 1 connected peer (@orig) - * 1000 received data packet (@dest) - * 1000 received data packet (@orig) + * TOTAL_PACKETS received data packet (@dest) + * TOTAL_PACKETS received data packet (@orig) * 1 received tunnel destroy (@dest) * _________________________________ * 5 x ok expected per peer */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED_ACK\n"); test = SPEED_ACK; - ok_goal = 2003; - argv2 [3] = NULL; // remove -L DEBUG -#if VERBOSE - argc2 -= 2; -#endif + test_name = "speed ack"; + ok_goal = TOTAL_PACKETS * 2 + 3; } else if (strstr (argv[0], "test_mesh_small_speed") != NULL) { /* Each peer is supposed to generate the following callbacks: * 1 incoming tunnel (@dest) * 1 connected peer (@orig) - * 1000 received data packet (@dest) + * 1 initial packet (@dest) + * TOTAL_PACKETS received data packet (@dest) + * 1 received data packet (@orig) * 1 received tunnel destroy (@dest) * _________________________________ - * 5 x ok expected per peer */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED\n"); - test = SPEED; - ok_goal = 1003; + ok_goal = TOTAL_PACKETS + 5; + if (strstr (argv[0], "_min") != NULL) + { + test = SPEED_MIN; + test_name = "speed min"; + } + else if (strstr (argv[0], "_nobuf") != NULL) + { + test = SPEED_NOBUF; + test_name = "speed nobuf"; + } + else + { + test = SPEED; + test_name = "speed"; + } } else { @@ -1000,16 +968,31 @@ main (int argc, char *argv[]) ok_goal = 0; } - GNUNET_PROGRAM_run (argc2, argv2, - "test_mesh_small", - gettext_noop ("Test mesh in a small network."), options, - &run, NULL); -#if REMOVE_DIR - GNUNET_DISK_directory_remove ("/tmp/test_mesh_small"); -#endif + if (strstr (argv[0], "backwards") != NULL) + { + char *aux; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "BACKWARDS (LEAF TO ROOT)\n"); + test_backwards = GNUNET_YES; + aux = GNUNET_malloc (32); + sprintf (aux, "backwards %s", test_name); + test_name = aux; + } + + p_ids = 0; + GNUNET_MESH_TEST_run ("test_mesh_small", + "test_mesh_small.conf", + 5, + &tmain, + NULL, + &incoming_tunnel, + &tunnel_cleaner, + handlers, + NULL); + if (ok_goal > ok) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "FAILED! (%d/%d)\n", ok, ok_goal); return 1; } diff --git a/src/mesh/test_mesh_small.conf b/src/mesh/test_mesh_small.conf index 005c490..943ef36 100644 --- a/src/mesh/test_mesh_small.conf +++ b/src/mesh/test_mesh_small.conf @@ -1,18 +1,32 @@ [PATHS] SERVICEHOME = /tmp/test_mesh_small/ -DEFAULTCONFIG = test_mesh_small.conf + +[mesh] +PORT = 10005 +ACCEPT_FROM = 127.0.0.1; +HOSTNAME = localhost +REFRESH_PATH_TIME = 5 s +APP_ANNOUNCE_TIME = 2 s +ID_ANNOUNCE_TIME = 2 s +CONNECT_TIMEOUT = 30 s +DEFAULT_TTL = 64 +DHT_REPLICAITON_LEVEL = 3 +# PREFIX = valgrind --leak-check=full --suppressions=valgrind-mesh.supp +# PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args + +[testbed] +NUM_PEERS = 5 +OVERLAY_TOPOLOGY = LINE [arm] PORT = 10010 DEFAULTSERVICES = core dht mesh -#DEBUG = YES [statistics] AUTOSTART = YES PORT = 10000 [dht] -DEBUG = NO AUTOSTART = YES ACCEPT_FROM6 = ::1; ACCEPT_FROM = 127.0.0.1; @@ -28,6 +42,10 @@ WORKDELAY = 500 ms AUTOSTART = NO PORT = 10011 +[vpn] +AUTOSTART = NO +PORT = 10012 + [transport] PORT = 10002 AUTOSTART = YES @@ -40,6 +58,8 @@ BEHIND_NAT = NO ALLOW_NAT = NO INTERNAL_ADDRESS = 127.0.0.1 EXTERNAL_ADDRESS = 127.0.0.1 +RETURN_LOCAL_ADDRESSES = YES +USE_LOCALADDR = YES [ats] WAN_QUOTA_IN = 1 GB @@ -53,43 +73,6 @@ PORT = 10003 AUTOSTART = YES PORT = 10004 -[mesh] -PORT = 10005 -DEBUG = YES -ACCEPT_FROM = 127.0.0.1; -HOSTNAME = localhost -# PREFIX = valgrind --leak-check=full --suppressions=valgrind-mesh.supp -# PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args - [testing] -NUM_PEERS = 5 WEAKRANDOM = YES -TOPOLOGY = NONE -CONNECT_TOPOLOGY = LINE -BLACKLIST_TOPOLOGY = LINE -BLACKLIST_TRANSPORTS = tcp udp http unix -#TOPOLOGY_FILE = small.dat -#CONNECT_TOPOLOGY_OPTION = CONNECT_MINIMUM -#CONNECT_TOPOLOGY_OPTION_MODIFIER = 25 -#PERCENTAGE = 3 -#PROBABILITY = .1 -F2F = NO -CONNECT_TIMEOUT = 660 s -CONNECT_ATTEMPTS = 2 -DEBUG = YES -HOSTKEYSFILE = ../../contrib/testing_hostkeys.dat -MAX_CONCURRENT_SSH = 10 -USE_PROGRESSBARS = YES -PEERGROUP_TIMEOUT = 2400 s -TOPOLOGY_OUTPUT_FILE = mesh_topo_initial -MAX_OUTSTANDING_CONNECTIONS = 75 -#SINGLE_PEERINFO_PER_HOST = YES -#NUM_PEERINFO_PER_HOST = 10 -#SINGLE_STATISTICS_PER_HOST = YES -#NUM_STATISTICS_PER_HOST = 10 -DELETE_FILES = YES -[test_mesh_small] -WAIT_TIME = 300 s -CONNECTION_LIMIT = 16 -#DATA_OUTPUT_FILE=data_output diff --git a/src/mesh/test_mesh_tree_api.c b/src/mesh/test_mesh_tree_api.c index 1d43135..3bdb82e 100644 --- a/src/mesh/test_mesh_tree_api.c +++ b/src/mesh/test_mesh_tree_api.c @@ -35,13 +35,32 @@ #define MESH_TUNNEL_TREE_C #endif -#define VERBOSE 1 +static int failed; +static int cb_call; +static struct GNUNET_PeerIdentity *pi[10]; +static struct MeshTunnelTree *tree; -int failed; -int cb_call; -struct GNUNET_PeerIdentity *pi[10]; -struct MeshTunnelTree *tree; +/** + * Whole tree iterator. + * + * @param cls Closure (unused). + * @param peer_id Short ID of the node. + * @param parent_id Short ID of the parent node. + */ +static void +tree_cb (void *cls, GNUNET_PEER_Id peer_id, GNUNET_PEER_Id parent_id) +{ + fprintf (stdout, "%u -> %u\n", peer_id, parent_id);; +} + + +/** + * Node children iterator. + * + * @param cls Closure (unused). + * @param peer_idShort ID of the child. + */ static void cb (void *cls, GNUNET_PEER_Id peer_id) { @@ -56,6 +75,19 @@ cb (void *cls, GNUNET_PEER_Id peer_id) /** + * Print debug information about the state of the tree. + * + * @param tree Tree to debug-print. + */ +static void +test_debug (struct MeshTunnelTree *tree) +{ + tree_debug (tree); + tree_iterate_all (tree, &tree_cb, NULL); +} + + +/** * Check if a node has all expected properties. * * @param peer_id Short ID of the peer to test. @@ -118,6 +150,9 @@ test_assert (GNUNET_PEER_Id peer_id, enum MeshPeerState status, } +/** + * Clean up and free all memory. + */ static void finish (void) { @@ -155,11 +190,7 @@ main (int argc, char *argv[]) failed = 0; cb_call = 0; GNUNET_log_setup ("test_mesh_api_tree", -#if VERBOSE - "DEBUG", -#else "WARNING", -#endif NULL); for (i = 0; i < 10; i++) { @@ -179,7 +210,7 @@ main (int argc, char *argv[]) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Adding first path: 1 2 3 4\n"); tree_add_path (tree, path, &cb, NULL); - tree_debug (tree); + test_debug (tree); path1 = tree_get_path_to_peer (tree, 4); if (NULL == path1 || path->length != path1->length || memcmp (path->peers, path1->peers, path->length) != 0) @@ -196,7 +227,7 @@ main (int argc, char *argv[]) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Adding second path: 1 2 3\n"); path->length--; tree_add_path (tree, path, &cb, NULL); - tree_debug (tree); + test_debug (tree); test_assert (4, MESH_PEER_SEARCHING, 0, 2); test_assert (3, MESH_PEER_SEARCHING, 1, 2); @@ -207,7 +238,7 @@ main (int argc, char *argv[]) path->length++; path->peers[3] = 5; tree_add_path (tree, path, &cb, NULL); - tree_debug (tree); + test_debug (tree); test_assert (5, MESH_PEER_SEARCHING, 0, 2); test_assert (4, MESH_PEER_SEARCHING, 0, 2); @@ -249,7 +280,7 @@ main (int argc, char *argv[]) tree_set_status (tree, 5, MESH_PEER_READY); cb_call = 1; node = tree_del_path (tree, 5, &cb, NULL); - tree_debug (tree); + test_debug (tree); if (cb_call != 0) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%u callbacks missed!\n", cb_call); @@ -276,7 +307,7 @@ main (int argc, char *argv[]) cb_call = 1; tree_find_peer (tree, 4)->status = MESH_PEER_READY; tree_add_path (tree, path, &cb, NULL); - tree_debug (tree); + test_debug (tree); if (cb_call != 0) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%u callbacks missed!\n", cb_call); @@ -309,7 +340,7 @@ main (int argc, char *argv[]) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Adding first path: 2 1 3\n"); tree_add_path (tree, path, &cb, NULL); - tree_debug (tree); + test_debug (tree); test_assert (3, MESH_PEER_SEARCHING, 0, 3); test_assert (1, MESH_PEER_RELAY, 1, 0); @@ -321,7 +352,7 @@ main (int argc, char *argv[]) path->peers[4] = 3; path->length = 5; tree_add_path (tree, path, &cb, NULL); - tree_debug (tree); + test_debug (tree); test_assert (3, MESH_PEER_SEARCHING, 0, 4); test_assert (5, MESH_PEER_RELAY, 1, 4); @@ -341,7 +372,7 @@ main (int argc, char *argv[]) path->peers[7] = 3; path->length = 8; tree_add_path (tree, path, &cb, NULL); - tree_debug (tree); + test_debug (tree); test_assert (3, MESH_PEER_SEARCHING, 0, 7); test_assert (5, MESH_PEER_RELAY, 1, 7); @@ -357,7 +388,7 @@ main (int argc, char *argv[]) path->peers[2] = 3; path->length = 3; tree_add_path (tree, path, &cb, NULL); - tree_debug (tree); + test_debug (tree); test_assert (3, MESH_PEER_SEARCHING, 0, 3); test_assert (1, MESH_PEER_RELAY, 1, 0); |