diff options
63 files changed, 7516 insertions, 26962 deletions
diff --git a/src/arm/gnunet-service-arm.c b/src/arm/gnunet-service-arm.c index 4f3e964e31..cc23ef1f68 100644 --- a/src/arm/gnunet-service-arm.c +++ b/src/arm/gnunet-service-arm.c @@ -812,6 +812,7 @@ start_process (struct ServiceList *sl, "%s %s", fin_options, optpos); + GNUNET_free (fin_options); GNUNET_free (optpos); } else diff --git a/src/cadet/Makefile.am b/src/cadet/Makefile.am index b52079b2e1..1fe9123057 100644 --- a/src/cadet/Makefile.am +++ b/src/cadet/Makefile.am @@ -23,7 +23,6 @@ AM_CLFAGS = -g libexec_PROGRAMS = \ gnunet-service-cadet \ - gnunet-service-cadet-new \ $(EXP_LIBEXEC) bin_PROGRAMS = \ @@ -61,88 +60,40 @@ gnunet_cadet_LDADD = \ libgnunetcadetnew.la \ $(top_builddir)/src/util/libgnunetutil.la -gnunet_service_cadet_new_SOURCES = \ - gnunet-service-cadet-new.c gnunet-service-cadet-new.h \ - gnunet-service-cadet-new_channel.c gnunet-service-cadet-new_channel.h \ - gnunet-service-cadet-new_connection.c gnunet-service-cadet-new_connection.h \ - gnunet-service-cadet-new_core.c gnunet-service-cadet-new_core.h \ - gnunet-service-cadet-new_dht.c gnunet-service-cadet-new_dht.h \ - gnunet-service-cadet-new_hello.c gnunet-service-cadet-new_hello.h \ - gnunet-service-cadet-new_tunnels.c gnunet-service-cadet-new_tunnels.h \ - gnunet-service-cadet-new_paths.c gnunet-service-cadet-new_paths.h \ - gnunet-service-cadet-new_peer.c gnunet-service-cadet-new_peer.h -gnunet_service_cadet_new_LDADD = \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/ats/libgnunetats.la \ - $(top_builddir)/src/core/libgnunetcore.la \ - $(top_builddir)/src/dht/libgnunetdht.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la \ - $(top_builddir)/src/transport/libgnunettransport.la \ - $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ - $(top_builddir)/src/hello/libgnunethello.la \ - $(top_builddir)/src/block/libgnunetblock.la - gnunet_service_cadet_SOURCES = \ - gnunet-service-cadet_tunnel.c gnunet-service-cadet_tunnel.h \ - gnunet-service-cadet_connection.c gnunet-service-cadet_connection.h \ + gnunet-service-cadet.c gnunet-service-cadet.h \ gnunet-service-cadet_channel.c gnunet-service-cadet_channel.h \ - gnunet-service-cadet_local.c gnunet-service-cadet_local.h \ - gnunet-service-cadet_peer.c gnunet-service-cadet_peer.h \ + gnunet-service-cadet_connection.c gnunet-service-cadet_connection.h \ + gnunet-service-cadet_core.c gnunet-service-cadet_core.h \ gnunet-service-cadet_dht.c gnunet-service-cadet_dht.h \ gnunet-service-cadet_hello.c gnunet-service-cadet_hello.h \ - cadet_path.c cadet_path.h \ - cadet_common.c \ - gnunet-service-cadet.c -gnunet_service_cadet_CFLAGS = $(AM_CFLAGS) + gnunet-service-cadet_tunnels.c gnunet-service-cadet_tunnels.h \ + gnunet-service-cadet_paths.c gnunet-service-cadet_paths.h \ + gnunet-service-cadet_peer.c gnunet-service-cadet_peer.h gnunet_service_cadet_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/transport/libgnunettransport.la \ - $(top_builddir)/src/core/libgnunetcore.la \ $(top_builddir)/src/ats/libgnunetats.la \ + $(top_builddir)/src/core/libgnunetcore.la \ $(top_builddir)/src/dht/libgnunetdht.la \ $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ $(top_builddir)/src/hello/libgnunethello.la \ $(top_builddir)/src/block/libgnunetblock.la if LINUX - gnunet_service_cadet_LDFLAGS = -lrt + gnunet_service_cadet_new_LDFLAGS = -lrt endif if HAVE_TESTING - noinst_LTLIBRARIES = libgnunetcadettest.la libgnunetcadettestnew.la $(noinst_LIB_EXP) - noinst_PROGRAMS = gnunet-cadet-profiler + noinst_LTLIBRARIES = libgnunetcadettest.la $(noinst_LIB_EXP) +# noinst_PROGRAMS = gnunet-cadet-profiler endif -libgnunetcadettest_la_SOURCES = \ - cadet_test_lib.c cadet_test_lib.h -libgnunetcadettest_la_LIBADD = \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testbed/libgnunettestbed.la \ - libgnunetcadet.la - if HAVE_TESTING check_PROGRAMS = \ test_cadet_local_mq \ - test_cadet_2_forward_new \ - test_cadet_2_forward_new \ - test_cadet_2_signal_new \ - test_cadet_2_keepalive_new \ - test_cadet_2_speed_new \ - test_cadet_2_speed_ack_new \ - test_cadet_2_speed_backwards_new \ - test_cadet_2_speed_reliable_new \ - test_cadet_2_speed_reliable_backwards_new \ - test_cadet_5_forward_new \ - test_cadet_5_signal_new \ - test_cadet_5_keepalive_new \ - test_cadet_5_speed_new \ - test_cadet_5_speed_ack_new \ - test_cadet_5_speed_reliable_new \ - test_cadet_5_speed_reliable_backwards_new \ - test_cadet_5_speed_backwards_new \ - test_cadet_single \ - test_cadet_local \ + test_cadet_2_forward \ test_cadet_2_forward \ test_cadet_2_signal \ test_cadet_2_keepalive \ @@ -161,32 +112,10 @@ check_PROGRAMS = \ test_cadet_5_speed_backwards endif -ld_cadet_test_lib = \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testing/libgnunettesting.la \ - libgnunetcadettest.la \ - libgnunetcadet.la \ - $(top_builddir)/src/testbed/libgnunettestbed.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la - -dep_cadet_test_lib = \ - libgnunetcadet.la \ - libgnunetcadettest.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la - - -gnunet_cadet_profiler_SOURCES = \ - gnunet-cadet-profiler.c -gnunet_cadet_profiler_LDADD = $(ld_cadet_test_lib) - -test_cadet_single_SOURCES = \ - test_cadet_single.c -test_cadet_single_LDADD = $(ld_cadet_test_lib) - -test_cadet_local_SOURCES = \ - test_cadet_local.c -test_cadet_local_LDADD = $(ld_cadet_test_lib) +#gnunet_cadet_profiler_SOURCES = \ +# gnunet-cadet-profiler.c +#gnunet_cadet_profiler_LDADD = $(ld_cadet_test_lib) test_cadet_local_mq_SOURCES = \ @@ -196,6 +125,26 @@ test_cadet_local_mq_LDADD = \ $(top_builddir)/src/testing/libgnunettesting.la \ $(top_builddir)/src/util/libgnunetutil.la + +libgnunetcadettest_la_SOURCES = \ + cadet_test_lib.c cadet_test_lib.h +libgnunetcadettest_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + libgnunetcadetnew.la + +ld_cadet_test_lib = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + libgnunetcadetnew.la \ + libgnunetcadettest.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la +dep_cadet_test_lib = \ + libgnunetcadetnew.la \ + libgnunetcadettest.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la + test_cadet_2_forward_SOURCES = \ test_cadet.c test_cadet_2_forward_LDADD = $(ld_cadet_test_lib) @@ -228,7 +177,6 @@ test_cadet_2_speed_reliable_backwards_SOURCES = \ test_cadet.c test_cadet_2_speed_reliable_backwards_LDADD = $(ld_cadet_test_lib) - test_cadet_5_forward_SOURCES = \ test_cadet.c test_cadet_5_forward_LDADD = $(ld_cadet_test_lib) @@ -262,92 +210,6 @@ test_cadet_5_speed_reliable_backwards_SOURCES = \ test_cadet_5_speed_reliable_backwards_LDADD = $(ld_cadet_test_lib) -# NEW TESTS -libgnunetcadettestnew_la_SOURCES = \ - cadet_test_lib_new.c cadet_test_lib_new.h -libgnunetcadettestnew_la_LIBADD = \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testbed/libgnunettestbed.la \ - libgnunetcadetnew.la - -ld_cadet_test_lib_new = \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testing/libgnunettesting.la \ - libgnunetcadetnew.la \ - libgnunetcadettestnew.la \ - $(top_builddir)/src/testbed/libgnunettestbed.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la -dep_cadet_test_lib_new = \ - libgnunetcadetnew.la \ - libgnunetcadettestnew.la \ - $(top_builddir)/src/statistics/libgnunetstatistics.la - -test_cadet_2_forward_new_SOURCES = \ - test_cadet_new.c -test_cadet_2_forward_new_LDADD = $(ld_cadet_test_lib_new) - -test_cadet_2_signal_new_SOURCES = \ - test_cadet_new.c -test_cadet_2_signal_new_LDADD = $(ld_cadet_test_lib_new) - -test_cadet_2_keepalive_new_SOURCES = \ - test_cadet_new.c -test_cadet_2_keepalive_new_LDADD = $(ld_cadet_test_lib_new) - -test_cadet_2_speed_new_SOURCES = \ - test_cadet_new.c -test_cadet_2_speed_new_LDADD = $(ld_cadet_test_lib_new) - -test_cadet_2_speed_ack_new_SOURCES = \ - test_cadet_new.c -test_cadet_2_speed_ack_new_LDADD = $(ld_cadet_test_lib_new) - -test_cadet_2_speed_backwards_new_SOURCES = \ - test_cadet_new.c -test_cadet_2_speed_backwards_new_LDADD = $(ld_cadet_test_lib_new) - -test_cadet_2_speed_reliable_new_SOURCES = \ - test_cadet_new.c -test_cadet_2_speed_reliable_new_LDADD = $(ld_cadet_test_lib_new) - -test_cadet_2_speed_reliable_backwards_new_SOURCES = \ - test_cadet_new.c -test_cadet_2_speed_reliable_backwards_new_LDADD = $(ld_cadet_test_lib_new) - - -test_cadet_5_forward_new_SOURCES = \ - test_cadet_new.c -test_cadet_5_forward_new_LDADD = $(ld_cadet_test_lib_new) - -test_cadet_5_signal_new_SOURCES = \ - test_cadet_new.c -test_cadet_5_signal_new_LDADD = $(ld_cadet_test_lib_new) - -test_cadet_5_keepalive_new_SOURCES = \ - test_cadet_new.c -test_cadet_5_keepalive_new_LDADD = $(ld_cadet_test_lib_new) - -test_cadet_5_speed_new_SOURCES = \ - test_cadet_new.c -test_cadet_5_speed_new_LDADD = $(ld_cadet_test_lib_new) - -test_cadet_5_speed_ack_new_SOURCES = \ - test_cadet_new.c -test_cadet_5_speed_ack_new_LDADD = $(ld_cadet_test_lib_new) - -test_cadet_5_speed_backwards_new_SOURCES = \ - test_cadet_new.c -test_cadet_5_speed_backwards_new_LDADD = $(ld_cadet_test_lib_new) - -test_cadet_5_speed_reliable_new_SOURCES = \ - test_cadet_new.c -test_cadet_5_speed_reliable_new_LDADD = $(ld_cadet_test_lib_new) - -test_cadet_5_speed_reliable_backwards_new_SOURCES = \ - test_cadet_new.c -test_cadet_5_speed_reliable_backwards_new_LDADD = $(ld_cadet_test_lib_new) - - if ENABLE_TEST_RUN AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; TESTS = \ diff --git a/src/cadet/cadet.conf.in b/src/cadet/cadet.conf.in index 86ba2e535d..d50e168f09 100644 --- a/src/cadet/cadet.conf.in +++ b/src/cadet/cadet.conf.in @@ -3,7 +3,7 @@ FORCESTART = YES AUTOSTART = @AUTOSTART@ @JAVAPORT@PORT = 2096 HOSTNAME = localhost -BINARY = gnunet-service-cadet-new +BINARY = gnunet-service-cadet # PREFIX = valgrind --leak-check=yes ACCEPT_FROM = 127.0.0.1; ACCEPT_FROM6 = ::1; diff --git a/src/cadet/cadet.h b/src/cadet/cadet.h index 451d1f3543..99f9f26531 100644 --- a/src/cadet/cadet.h +++ b/src/cadet/cadet.h @@ -59,7 +59,7 @@ extern "C" #include "gnunet_core_service.h" #include "gnunet_cadet_service.h" #include "gnunet_protocols.h" -#include <gnunet_cadet_service.h> +#include "gnunet_cadet_service.h" /******************************************************************************/ /************************** CONSTANTS ******************************/ diff --git a/src/cadet/cadet_path.c b/src/cadet/cadet_path.c deleted file mode 100644 index 79a498805b..0000000000 --- a/src/cadet/cadet_path.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2001 - 2013 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file cadet/cadet_path.c - * @brief Path handling functions - * @author Bartlomiej Polot - */ - -#include "cadet.h" -#include "cadet_path.h" -#include "gnunet-service-cadet_peer.h" - -#define LOG(level, ...) GNUNET_log_from (level,"cadet-pth",__VA_ARGS__) - - -/** - * @brief Destroy a path after some time has past. - * Removes the path from the peer (must not be used for direct paths). - * - * @param cls Closure (path to destroy). - */ -static void -path_destroy_delayed (void *cls) -{ - struct CadetPeerPath *path = cls; - struct CadetPeer *peer; - - path->path_delete = NULL; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Destroy delayed %p (%u)\n", - path, - path->length); - GNUNET_assert (2 < path->length); - peer = GCP_get_short (path->peers[path->length - 1], - GNUNET_NO); - GNUNET_assert (NULL != peer); - GCP_remove_path (peer, path); -} - - -/** - * Create a new path - * - * @param length How many hops will the path have. - * @return A newly allocated path with a peer array of the specified length. - */ -struct CadetPeerPath * -path_new (unsigned int length) -{ - struct CadetPeerPath *p; - - p = GNUNET_new (struct CadetPeerPath); - if (length > 0) - { - p->length = length; - p->peers = GNUNET_malloc (length * sizeof (GNUNET_PEER_Id)); - } - LOG (GNUNET_ERROR_TYPE_DEBUG, "New path %p (%u)\n", p, p->length); - return p; -} - - -/** - * Invert the path - * - * @param path the path to invert - */ -void -path_invert (struct CadetPeerPath *path) -{ - GNUNET_PEER_Id aux; - unsigned int i; - - for (i = 0; i < path->length / 2; i++) - { - aux = path->peers[i]; - path->peers[i] = path->peers[path->length - i - 1]; - path->peers[path->length - i - 1] = aux; - } -} - - -/** - * Duplicate a path, incrementing short peer's rc. - * - * @param path The path to duplicate. - */ -struct CadetPeerPath * -path_duplicate (const struct CadetPeerPath *path) -{ - struct CadetPeerPath *aux; - unsigned int i; - - aux = path_new (path->length); - GNUNET_memcpy (aux->peers, - path->peers, - path->length * sizeof (GNUNET_PEER_Id)); - for (i = 0; i < aux->length; i++) - GNUNET_PEER_change_rc (aux->peers[i], 1); - return aux; -} - - -/** - * Get the length of a path. - * - * @param path The path to measure, with the local peer at any point of it. - * - * @return Number of hops to reach destination. - * UINT_MAX in case the peer is not in the path. - */ -unsigned int -path_get_length (struct CadetPeerPath *path) -{ - if (NULL == path) - return UINT_MAX; - return path->length; -} - - - -/** - * Mark path as invalid: keep it aroud for a while to avoid trying it in a loop. - * - * Never invalidates a two-hop (direct) path, only a core handler can do that. - * - * Rationale: DHT_get sometimes returns bad cached results, for instance, - * on a locally cached result where the PUT followed a path that is no longer - * current. The path must remain "known and marked as invalid" for a while. - * - * @param p Path to invalidate. - */ -void -path_invalidate (struct CadetPeerPath *p) -{ - if (NULL != p->path_delete) - return; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Invalidating path %p (%u)\n", - p, - p->length); - p->path_delete - = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, - &path_destroy_delayed, p); -} - - -/** - * Builds a path from a PeerIdentity array. - * - * @param peers PeerIdentity array. - * @param size Size of the @c peers array. - * @param myid ID of local peer, to find @c own_pos. - * @param own_pos Output parameter: own position in the path. - * - * @return Fixed and shortened path. - */ -struct CadetPeerPath * -path_build_from_peer_ids (struct GNUNET_PeerIdentity *peers, - unsigned int size, - GNUNET_PEER_Id myid, - unsigned int *own_pos) -{ - struct CadetPeerPath *path; - GNUNET_PEER_Id shortid; - unsigned int i; - unsigned int j; - unsigned int offset; - - /* Create path */ - LOG (GNUNET_ERROR_TYPE_DEBUG, " Creating path...\n"); - path = path_new (size); - *own_pos = 0; - offset = 0; - for (i = 0; i < size; i++) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " - %u: taking %s\n", - i, GNUNET_i2s (&peers[i])); - shortid = GNUNET_PEER_intern (&peers[i]); - - /* Check for loops / duplicates */ - for (j = 0; j < i - offset; j++) - { - if (path->peers[j] == shortid) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " already exists at pos %u\n", j); - offset = i - j; - LOG (GNUNET_ERROR_TYPE_DEBUG, " offset now %u\n", offset); - GNUNET_PEER_change_rc (shortid, -1); - } - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " storing at %u\n", i - offset); - path->peers[i - offset] = shortid; - if (path->peers[i - offset] == myid) - *own_pos = i - offset; - } - path->length -= offset; - - if (path->peers[*own_pos] != myid) - { - /* create path: self not found in path through self */ - GNUNET_break_op (0); - path_destroy (path); - return NULL; - } - - return path; -} - - -/** - * Test if two paths are equivalent (equal or revese of each other). - * - * @param p1 First path - * @param p2 Second path - * - * @return #GNUNET_YES if both paths are equivalent - * #GNUNET_NO otherwise - */ -int -path_equivalent (const struct CadetPeerPath *p1, - const struct CadetPeerPath *p2) -{ - unsigned int i; - unsigned int l; - unsigned int half; - - if (NULL == p1 || NULL == p2) - return GNUNET_NO; - - if (p1->length != p2->length) - return GNUNET_NO; - - l = p1->length; - if (0 == memcmp (p1->peers, p2->peers, sizeof (p1->peers[0]) * l)) - return GNUNET_YES; - - half = l / 2; - l = l - 1; - for (i = 0; i <= half; i++) - if (p1->peers[i] != p2->peers[l - i]) - return GNUNET_NO; - - return GNUNET_YES; -} - - -/** - * Test if a path is valid (or at least not known to be invalid). - * - * @param path Path to test. - * - * @return #GNUNET_YES If the path is valid or unknown, - * #GNUNET_NO If the path is known to be invalid. - */ -int -path_is_valid (const struct CadetPeerPath *path) -{ - return (NULL == path->path_delete); -} - - -/** - * Destroy the path and free any allocated resources linked to it - * - * @param p the path to destroy - * - * @return #GNUNET_OK on success - */ -int -path_destroy (struct CadetPeerPath *p) -{ - if (NULL == p) - return GNUNET_OK; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "destroying path %p (%u)\n", - p, - p->length); - GNUNET_PEER_decrement_rcs (p->peers, p->length); - GNUNET_free_non_null (p->peers); - if (NULL != p->path_delete) - GNUNET_SCHEDULER_cancel (p->path_delete); - GNUNET_free (p); - return GNUNET_OK; -} - - -/** - * Compare two paths. - * - * @param p1 First path. - * @param p2 Second path. - * - * @return > 0 if p1 is longer, or the first differing PEER_Id is higher on p1. - * < 0 if p2 is longer, or the first differing PEER_Id is higher on p2. - * 0 if they are identical. - */ -int -path_cmp (const struct CadetPeerPath *p1, - const struct CadetPeerPath *p2) -{ - if (p1->length > p2->length) - return 1; - - if (p1->length < p2->length) - return -1; - - return memcmp (p1->peers, - p2->peers, - sizeof (GNUNET_PEER_Id) * p1->length); -} - - -char * -path_2s (struct CadetPeerPath *p) -{ - char *s; - char *old; - unsigned int i; - - old = GNUNET_strdup (""); - for (i = 0; i < p->length; i++) - { - GNUNET_asprintf (&s, "%s %s", - old, GNUNET_i2s (GNUNET_PEER_resolve2 (p->peers[i]))); - GNUNET_free_non_null (old); - old = s; - } - return old; -} - - -void -path_debug (struct CadetPeerPath *p) -{ - unsigned int i; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "PATH:\n"); - for (i = 0; i < p->length; i++) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " %s\n", - GNUNET_i2s (GNUNET_PEER_resolve2 (p->peers[i]))); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "END\n"); -} diff --git a/src/cadet/cadet_path.h b/src/cadet/cadet_path.h deleted file mode 100644 index bb68eec42e..0000000000 --- a/src/cadet/cadet_path.h +++ /dev/null @@ -1,226 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2001 - 2013 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file cadet/cadet_path.h - * @brief Path handling functions - * @author Bartlomiej Polot - */ - -#ifndef CADET_PATH_H_ -#define CADET_PATH_H_ - -#ifdef __cplusplus -extern "C" -{ - #if 0 /* keep Emacsens' auto-indent happy */ -} -#endif -#endif - - -/******************************************************************************/ -/************************ DATA STRUCTURES ****************************/ -/******************************************************************************/ - -/** - * Information regarding a possible path to reach a single peer - */ -struct CadetPeerPath -{ - - /** - * Linked list - */ - struct CadetPeerPath *next; - struct CadetPeerPath *prev; - - /** - * List of all the peers that form the path from origin to target. - */ - GNUNET_PEER_Id *peers; - - /** - * Number of peers (hops) in the path - */ - unsigned int length; - - /** - * User defined data store. - */ - struct CadetConnection *c; - - /** - * Path's score, how reliable is the path. - */ -// int score; - - /** - * Task to delete the path. - * We tried it, it didn't work, don't try again in a while. - */ - struct GNUNET_SCHEDULER_Task * path_delete; - -}; - -/******************************************************************************/ -/************************* FUNCTIONS *****************************/ -/******************************************************************************/ - -/** - * Create a new path. - * - * @param length How many hops will the path have. - * - * @return A newly allocated path with a peer array of the specified length. - */ -struct CadetPeerPath * -path_new (unsigned int length); - - -/** - * Invert the path. - * - * @param path The path to invert. - */ -void -path_invert (struct CadetPeerPath *path); - - -/** - * Duplicate a path, incrementing short peer's rc. - * - * @param path The path to duplicate. - */ -struct CadetPeerPath * -path_duplicate (const struct CadetPeerPath *path); - - -/** - * Get the length of a path. - * - * @param path The path to measure, with the local peer at any point of it. - * - * @return Number of hops to reach destination. - * UINT_MAX in case the peer is not in the path. - */ -unsigned int -path_get_length (struct CadetPeerPath *path); - -/** - * Mark path as invalid: keep it aroud for a while to avoid trying it in a loop. - * - * DHT_get sometimes returns bad cached results, for instance, on a locally - * cached result where the PUT followed a path that is no longer current. - * - * @param p Path to invalidate. - */ -void -path_invalidate (struct CadetPeerPath *p); - -/** - * Test if two paths are equivalent (equal or revese of each other). - * - * @param p1 First path - * @param p2 Second path - * - * @return GNUNET_YES if both paths are equivalent - * GNUNET_NO otherwise - */ -int -path_equivalent (const struct CadetPeerPath *p1, - const struct CadetPeerPath *p2); - -/** - * Test if a path is valid (or at least not known to be invalid). - * - * @param path Path to test. - * - * @return #GNUNET_YES If the path is valid or unknown, - * #GNUNET_NO If the path is known to be invalid. - */ -int -path_is_valid (const struct CadetPeerPath *path); - -/** - * Destroy the path and free any allocated resources linked to it - * - * @param p the path to destroy - * - * @return GNUNET_OK on success - */ -int -path_destroy (struct CadetPeerPath *p); - -/** - * Compare two paths. - * - * @param p1 First path. - * @param p2 Second path. - * - * @return > 0 if p1 is longer, or the first differing PEER_Id is higher on p1. - * < 0 if p2 is longer, or the first differing PEER_Id is higher on p2. - * 0 if they are identical. - */ -int -path_cmp (const struct CadetPeerPath *p1, const struct CadetPeerPath *p2); - -/** - * Builds a path from a PeerIdentity array. - * - * @param peers PeerIdentity array. - * @param size Size of the @c peers array. - * @param myid ID of local peer, to find @c own_pos. - * @param own_pos Output parameter: own position in the path. - * - * @return Fixed and shortened path. - */ -struct CadetPeerPath * -path_build_from_peer_ids (struct GNUNET_PeerIdentity *peers, - unsigned int size, - GNUNET_PEER_Id myid, - unsigned int *own_pos); - -/** - * Path -> allocated one line string. Caller must free. - * - * @param p Path. - */ -char * -path_2s (struct CadetPeerPath *p); - -/** - * Print info about the path for debug. - * - * @param p Path to debug. - */ -void -path_debug (struct CadetPeerPath *p); - -#if 0 /* keep Emacsens' auto-indent happy */ -{ - #endif - #ifdef __cplusplus -} -#endif - - -/* ifndef CADET_PATH_H */ -#endif diff --git a/src/cadet/cadet_protocol.h b/src/cadet/cadet_protocol.h index d2426addbd..560c186cd5 100644 --- a/src/cadet/cadet_protocol.h +++ b/src/cadet/cadet_protocol.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2001 - 2011 GNUnet e.V. + Copyright (C) 2007 - 2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -19,8 +19,10 @@ */ /** - * @author Bartlomiej Polot * @file cadet/cadet_protocol.h + * @brief P2P messages used by CADET + * @author Bartlomiej Polot + * @author Christian Grothoff */ #ifndef CADET_PROTOCOL_H_ @@ -298,17 +300,10 @@ struct GNUNET_CADET_TunnelEncryptedMessage */ struct GNUNET_MessageHeader header; -#if NEW_CADET /** * Reserved, for alignment. */ uint32_t reserved GNUNET_PACKED; -#else - /** - * Maximum packet ID authorized. - */ - struct CadetEncryptedMessageIdentifier cemi; -#endif /** * ID of the connection. @@ -322,89 +317,18 @@ struct GNUNET_CADET_TunnelEncryptedMessage */ struct GNUNET_ShortHashCode hmac; - #if NEW_CADET /** * Axolotl-header that specifies which keys to use in which ratchet * to decrypt the body that follows. */ struct GNUNET_CADET_AxHeader ax_header; -#else - /** - * Number of messages sent with the current ratchet key. - */ - uint32_t Ns GNUNET_PACKED; - - /** - * Number of messages sent with the previous ratchet key. - */ - uint32_t PNs GNUNET_PACKED; /** - * Current ratchet key. - */ - struct GNUNET_CRYPTO_EcdhePublicKey DHRs; -#endif - /** * Encrypted content follows. */ }; -#ifndef NEW_CADET - -/** - * Message to query a peer about its Flow Control status regarding a tunnel. - * - * It is NOT yet clear if we need this. - */ -struct GNUNET_CADET_ConnectionHopByHopPollMessage -{ - /** - * Type: #GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED_POLL - */ - struct GNUNET_MessageHeader header; - - /** - * Last packet sent. - */ - struct CadetEncryptedMessageIdentifier cemi; - - /** - * ID of the connection. - */ - struct GNUNET_CADET_ConnectionTunnelIdentifier cid; - -}; - - -/** - * Message to acknowledge cadet encrypted traffic, used for - * flow-control on a hop-by-hop basis on the connection-level. Note - * that we do use the @e cemi from the tunnel layer as the connection - * layer's header is included/shared with the tunnel layer messages, - * and we only do flow control for the payload. - */ -struct GNUNET_CADET_ConnectionEncryptedAckMessage -{ - /** - * Type: #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_HOP_BY_HOP_ENCRYPTED_ACK - */ - struct GNUNET_MessageHeader header; - - /** - * Maximum packet ID authorized. - */ - struct CadetEncryptedMessageIdentifier cemi_max; - - /** - * ID of the connection. - */ - struct GNUNET_CADET_ConnectionTunnelIdentifier cid; -}; - -#endif - - /******************************************************************************/ /******************************* CHANNEL ***********************************/ /******************************************************************************/ @@ -450,83 +374,19 @@ struct GNUNET_CADET_ChannelManageMessage */ struct GNUNET_MessageHeader header; -#ifdef NEW_CADET /** * For alignment. */ uint32_t reserved GNUNET_PACKED; -#endif - - /** - * ID of the channel - */ - struct GNUNET_CADET_ChannelTunnelNumber ctn; -}; - - -#ifndef NEW_CADET - -/** - * Message for cadet data traffic. - */ -struct GNUNET_CADET_ChannelAppDataMessage -{ - /** - * Type: #GNUNET_MESSAGE_TYPE_CADET_UNICAST, - * #GNUNET_MESSAGE_TYPE_CADET_TO_ORIGIN - */ - struct GNUNET_MessageHeader header; - - /** - * Unique ID of the payload message - */ - /* NEW: struct ChannelMessageIdentifier */ - uint32_t mid GNUNET_PACKED; /** * ID of the channel */ struct GNUNET_CADET_ChannelTunnelNumber ctn; - - /** - * Payload follows - */ }; /** - * Message to acknowledge end-to-end data. - */ -struct GNUNET_CADET_ChannelDataAckMessage -{ - /** - * Type: #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK - */ - struct GNUNET_MessageHeader header; - - /** - * ID of the channel - */ - struct GNUNET_CADET_ChannelTunnelNumber ctn; - - /** - * Bitfield of already-received messages past @e mid. - * pid + 1 @ LSB - * pid + 64 @ MSB - */ - uint64_t futures GNUNET_PACKED; - - /** - * Last message ID received. - */ - /* NEW: struct ChannelMessageIdentifier */ - uint32_t mid GNUNET_PACKED; -}; - -#else - - -/** * Number used to uniquely identify messages in a CADET Channel. */ struct ChannelMessageIdentifier @@ -595,8 +455,6 @@ struct GNUNET_CADET_ChannelDataAckMessage }; -#endif - GNUNET_NETWORK_STRUCT_END #if 0 /* keep Emacsens' auto-indent happy */ diff --git a/src/cadet/cadet_test_lib.c b/src/cadet/cadet_test_lib.c index 9a70dad493..c3a1540f42 100644 --- a/src/cadet/cadet_test_lib.c +++ b/src/cadet/cadet_test_lib.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2012 GNUnet e.V. + Copyright (C) 2012, 2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -24,9 +24,10 @@ */ #include "platform.h" #include "gnunet_util_lib.h" -#include "cadet_test_lib.h" +#include "cadet_test_lib_new.h" #include "gnunet_cadet_service.h" + /** * Test context for a CADET Test. */ @@ -40,7 +41,7 @@ struct GNUNET_CADET_TEST_Context /** * Array of handles to the CADET for each peer. */ - struct GNUNET_CADET_Handle **cadetes; + struct GNUNET_CADET_Handle **cadets; /** * Operation associated with the connection to the CADET. @@ -48,6 +49,11 @@ struct GNUNET_CADET_TEST_Context struct GNUNET_TESTBED_Operation **ops; /** + * Number of peers running, size of the arrays above. + */ + unsigned int num_peers; + + /** * Main function of the test to run once all CADETs are available. */ GNUNET_CADET_TEST_AppMain app_main; @@ -58,30 +64,35 @@ struct GNUNET_CADET_TEST_Context void *app_main_cls; /** - * Number of peers running, size of the arrays above. + * Handler for incoming tunnels. */ - unsigned int num_peers; + GNUNET_CADET_ConnectEventHandler connects; /** - * Handler for incoming tunnels. + * Function called when the transmit window size changes. */ - GNUNET_CADET_InboundChannelNotificationHandler *new_channel; + GNUNET_CADET_WindowSizeEventHandler window_changes; /** * Cleaner for destroyed incoming tunnels. */ - GNUNET_CADET_ChannelEndHandler *cleaner; + GNUNET_CADET_DisconnectEventHandler disconnects; /** * Message handlers. */ - struct GNUNET_CADET_MessageHandler* handlers; + struct GNUNET_MQ_MessageHandler *handlers; /** * Application ports. */ const struct GNUNET_HashCode **ports; + /** + * Number of ports in #ports. + */ + unsigned int port_count; + }; @@ -94,6 +105,11 @@ struct GNUNET_CADET_TEST_AdapterContext * Peer number for the particular peer. */ unsigned int peer; + + /** + * Port handlers for open ports. + */ + struct GNUNET_CADET_Port **ports; /** * General context. @@ -114,26 +130,28 @@ struct GNUNET_CADET_TEST_AdapterContext */ static void * cadet_connect_adapter (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg) + const struct GNUNET_CONFIGURATION_Handle *cfg) { struct GNUNET_CADET_TEST_AdapterContext *actx = cls; struct GNUNET_CADET_TEST_Context *ctx = actx->ctx; struct GNUNET_CADET_Handle *h; + unsigned int i; - h = GNUNET_CADET_connect (cfg, - (void *) (long) actx->peer, - ctx->cleaner, - ctx->handlers); + h = GNUNET_CADET_connecT (cfg); if (NULL == ctx->ports) return h; - for (int i = 0; NULL != ctx->ports[i]; i++) + actx->ports = GNUNET_new_array (ctx->port_count, struct GNUNET_CADET_Port *); + for (i = 0; i < ctx->port_count; i++) { - (void ) GNUNET_CADET_open_port (h, ctx->ports[i], - ctx->new_channel, - (void *) (long) actx->peer); + actx->ports[i] = GNUNET_CADET_open_porT (h, + ctx->ports[i], + ctx->connects, + (void *) (long) actx->peer, + ctx->window_changes, + ctx->disconnects, + ctx->handlers); } - return h; } @@ -152,6 +170,15 @@ cadet_disconnect_adapter (void *cls, struct GNUNET_CADET_Handle *cadet = op_result; struct GNUNET_CADET_TEST_AdapterContext *actx = cls; + if (NULL != actx->ports) + { + for (int i = 0; i < actx->ctx->port_count; i++) + { + GNUNET_CADET_close_port (actx->ports[i]); + actx->ports[i] = NULL; + } + GNUNET_free (actx->ports); + } GNUNET_free (actx); GNUNET_CADET_disconnect (cadet); } @@ -186,18 +213,18 @@ cadet_connect_cb (void *cls, for (i = 0; i < ctx->num_peers; i++) if (op == ctx->ops[i]) { - ctx->cadetes[i] = ca_result; + ctx->cadets[i] = ca_result; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "...cadet %u connected\n", i); } for (i = 0; i < ctx->num_peers; i++) - if (NULL == ctx->cadetes[i]) + if (NULL == ctx->cadets[i]) return; /* still some CADET connections missing */ /* all CADET connections ready! */ ctx->app_main (ctx->app_main_cls, ctx, ctx->num_peers, ctx->peers, - ctx->cadetes); + ctx->cadets); } @@ -213,7 +240,7 @@ GNUNET_CADET_TEST_cleanup (struct GNUNET_CADET_TEST_Context *ctx) ctx->ops[i] = NULL; } GNUNET_free (ctx->ops); - GNUNET_free (ctx->cadetes); + GNUNET_free (ctx->cadets); GNUNET_free (ctx); GNUNET_SCHEDULER_shutdown (); } @@ -243,12 +270,23 @@ cadet_test_run (void *cls, struct GNUNET_CADET_TEST_Context *ctx = cls; unsigned int i; + if (0 != links_failed) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Some links failed (%u), ending\n", + links_failed); + exit (2); + } + if (num_peers != ctx->num_peers) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Peers started %u/%u, ending\n", num_peers, ctx->num_peers); exit (1); } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Testbed up, %u peers and %u links\n", + num_peers, links_succeeded); ctx->peers = peers; for (i = 0; i < num_peers; i++) { @@ -270,31 +308,52 @@ cadet_test_run (void *cls, } +/** + * Run a test using the given name, configuration file and number of peers. + * All cadet callbacks will receive the peer number (long) as the closure. + * + * @param testname Name of the test (for logging). + * @param cfgfile 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 @a tmain. + * @param connects Handler for incoming channels. + * @param window_changes Handler for the window size change notification. + * @param disconnects Cleaner for destroyed incoming channels. + * @param handlers Message handlers. + * @param ports Ports the peers offer, NULL-terminated. + */ void -GNUNET_CADET_TEST_run (const char *testname, - const char *cfgname, - unsigned int num_peers, - GNUNET_CADET_TEST_AppMain tmain, - void *tmain_cls, - GNUNET_CADET_InboundChannelNotificationHandler new_channel, - GNUNET_CADET_ChannelEndHandler cleaner, - struct GNUNET_CADET_MessageHandler* handlers, - const struct GNUNET_HashCode **ports) +GNUNET_CADET_TEST_ruN (const char *testname, + const char *cfgfile, + unsigned int num_peers, + GNUNET_CADET_TEST_AppMain tmain, + void *tmain_cls, + GNUNET_CADET_ConnectEventHandler connects, + GNUNET_CADET_WindowSizeEventHandler window_changes, + GNUNET_CADET_DisconnectEventHandler disconnects, + struct GNUNET_MQ_MessageHandler *handlers, + const struct GNUNET_HashCode **ports) { struct GNUNET_CADET_TEST_Context *ctx; ctx = GNUNET_new (struct GNUNET_CADET_TEST_Context); ctx->num_peers = num_peers; - ctx->ops = GNUNET_malloc (num_peers * sizeof (struct GNUNET_TESTBED_Operation *)); - ctx->cadetes = GNUNET_malloc (num_peers * sizeof (struct GNUNET_CADET_Handle *)); + ctx->ops = GNUNET_new_array (num_peers, struct GNUNET_TESTBED_Operation *); + ctx->cadets = GNUNET_new_array (num_peers, struct GNUNET_CADET_Handle *); ctx->app_main = tmain; ctx->app_main_cls = tmain_cls; - ctx->new_channel = new_channel; - ctx->cleaner = cleaner; - ctx->handlers = handlers; + ctx->connects = connects; + ctx->window_changes = window_changes; + ctx->disconnects = disconnects; + ctx->handlers = GNUNET_MQ_copy_handlers (handlers); ctx->ports = ports; + ctx->port_count = 0; + while (NULL != ctx->ports[ctx->port_count]) + ctx->port_count++; + GNUNET_TESTBED_test_run (testname, - cfgname, + cfgfile, num_peers, 0LL, NULL, NULL, &cadet_test_run, ctx); diff --git a/src/cadet/cadet_test_lib.h b/src/cadet/cadet_test_lib.h index 464977d42d..4b3a6b18dd 100644 --- a/src/cadet/cadet_test_lib.h +++ b/src/cadet/cadet_test_lib.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2012 GNUnet e.V. + Copyright (C) 2012,2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -49,41 +49,41 @@ struct GNUNET_CADET_TEST_Context; * @param ctx Argument to give to GNUNET_CADET_TEST_cleanup on test end. * @param num_peers Number of peers that are running. * @param peers Array of peers. - * @param cadetes Handle to each of the CADETs of the peers. + * @param cadets Handle to each of the CADETs of the peers. */ typedef void (*GNUNET_CADET_TEST_AppMain) (void *cls, struct GNUNET_CADET_TEST_Context *ctx, unsigned int num_peers, struct GNUNET_TESTBED_Peer **peers, - struct GNUNET_CADET_Handle **cadetes); + struct GNUNET_CADET_Handle **cadets); /** - * Run a test using the given name, configuration file and number of - * peers. - * All cadet callbacks will receive the peer number as the closure. + * Run a test using the given name, configuration file and number of peers. + * All cadet callbacks will receive the peer number (long) as the closure. * * @param testname Name of the test (for logging). - * @param cfgname Name of the configuration file. + * @param cfgfile 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_channel Handler for incoming tunnels. - * @param cleaner Cleaner for destroyed incoming tunnels. + * @param tmain_cls Closure for @a tmain. + * @param connects Handler for incoming channels. + * @param window_changes Handler for the window size change notification. + * @param disconnects Cleaner for destroyed incoming channels. * @param handlers Message handlers. * @param ports Ports the peers offer, NULL-terminated. */ void -GNUNET_CADET_TEST_run (const char *testname, - const char *cfgname, - unsigned int num_peers, - GNUNET_CADET_TEST_AppMain tmain, - void *tmain_cls, - GNUNET_CADET_InboundChannelNotificationHandler new_channel, - GNUNET_CADET_ChannelEndHandler cleaner, - struct GNUNET_CADET_MessageHandler* handlers, - const struct GNUNET_HashCode **ports); - +GNUNET_CADET_TEST_ruN (const char *testname, + const char *cfgfile, + unsigned int num_peers, + GNUNET_CADET_TEST_AppMain tmain, + void *tmain_cls, + GNUNET_CADET_ConnectEventHandler connects, + GNUNET_CADET_WindowSizeEventHandler window_changes, + GNUNET_CADET_DisconnectEventHandler disconnects, + struct GNUNET_MQ_MessageHandler *handlers, + const struct GNUNET_HashCode **ports); /** * Clean up the testbed. diff --git a/src/cadet/cadet_test_lib_new.c b/src/cadet/cadet_test_lib_new.c deleted file mode 100644 index c3a1540f42..0000000000 --- a/src/cadet/cadet_test_lib_new.c +++ /dev/null @@ -1,362 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012, 2017 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file cadet/cadet_test_lib.c - * @author Bartlomiej Polot - * @brief library for writing CADET tests - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "cadet_test_lib_new.h" -#include "gnunet_cadet_service.h" - - -/** - * Test context for a CADET Test. - */ -struct GNUNET_CADET_TEST_Context -{ - /** - * Array of running peers. - */ - struct GNUNET_TESTBED_Peer **peers; - - /** - * Array of handles to the CADET for each peer. - */ - struct GNUNET_CADET_Handle **cadets; - - /** - * Operation associated with the connection to the CADET. - */ - struct GNUNET_TESTBED_Operation **ops; - - /** - * Number of peers running, size of the arrays above. - */ - unsigned int num_peers; - - /** - * Main function of the test to run once all CADETs are available. - */ - GNUNET_CADET_TEST_AppMain app_main; - - /** - * Closure for 'app_main'. - */ - void *app_main_cls; - - /** - * Handler for incoming tunnels. - */ - GNUNET_CADET_ConnectEventHandler connects; - - /** - * Function called when the transmit window size changes. - */ - GNUNET_CADET_WindowSizeEventHandler window_changes; - - /** - * Cleaner for destroyed incoming tunnels. - */ - GNUNET_CADET_DisconnectEventHandler disconnects; - - /** - * Message handlers. - */ - struct GNUNET_MQ_MessageHandler *handlers; - - /** - * Application ports. - */ - const struct GNUNET_HashCode **ports; - - /** - * Number of ports in #ports. - */ - unsigned int port_count; - -}; - - -/** - * Context for a cadet adapter callback. - */ -struct GNUNET_CADET_TEST_AdapterContext -{ - /** - * Peer number for the particular peer. - */ - unsigned int peer; - - /** - * Port handlers for open ports. - */ - struct GNUNET_CADET_Port **ports; - - /** - * General context. - */ - struct GNUNET_CADET_TEST_Context *ctx; -}; - - -/** - * Adapter function called to establish a connection to - * the CADET 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 * -cadet_connect_adapter (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct GNUNET_CADET_TEST_AdapterContext *actx = cls; - struct GNUNET_CADET_TEST_Context *ctx = actx->ctx; - struct GNUNET_CADET_Handle *h; - unsigned int i; - - h = GNUNET_CADET_connecT (cfg); - if (NULL == ctx->ports) - return h; - - actx->ports = GNUNET_new_array (ctx->port_count, struct GNUNET_CADET_Port *); - for (i = 0; i < ctx->port_count; i++) - { - actx->ports[i] = GNUNET_CADET_open_porT (h, - ctx->ports[i], - ctx->connects, - (void *) (long) actx->peer, - ctx->window_changes, - ctx->disconnects, - ctx->handlers); - } - return h; -} - - -/** - * Adapter function called to destroy a connection to - * the CADET service. - * - * @param cls closure - * @param op_result service handle returned from the connect adapter - */ -static void -cadet_disconnect_adapter (void *cls, - void *op_result) -{ - struct GNUNET_CADET_Handle *cadet = op_result; - struct GNUNET_CADET_TEST_AdapterContext *actx = cls; - - if (NULL != actx->ports) - { - for (int i = 0; i < actx->ctx->port_count; i++) - { - GNUNET_CADET_close_port (actx->ports[i]); - actx->ports[i] = NULL; - } - GNUNET_free (actx->ports); - } - GNUNET_free (actx); - GNUNET_CADET_disconnect (cadet); -} - - -/** - * 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() (cadet handle). - * @param emsg Error message in case the operation has failed. - * NULL if operation has executed successfully. - */ -static void -cadet_connect_cb (void *cls, - struct GNUNET_TESTBED_Operation *op, - void *ca_result, - const char *emsg) -{ - struct GNUNET_CADET_TEST_Context *ctx = cls; - unsigned int i; - - if (NULL != emsg) - { - fprintf (stderr, "Failed to connect to CADET service: %s\n", - emsg); - GNUNET_SCHEDULER_shutdown (); - return; - } - for (i = 0; i < ctx->num_peers; i++) - if (op == ctx->ops[i]) - { - ctx->cadets[i] = ca_result; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "...cadet %u connected\n", i); - } - for (i = 0; i < ctx->num_peers; i++) - if (NULL == ctx->cadets[i]) - return; /* still some CADET connections missing */ - /* all CADET connections ready! */ - ctx->app_main (ctx->app_main_cls, - ctx, - ctx->num_peers, - ctx->peers, - ctx->cadets); -} - - -void -GNUNET_CADET_TEST_cleanup (struct GNUNET_CADET_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->cadets); - 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 h the run handle - * @param num_peers Number of peers that are running. - * @param peers Handles to each one of the @c num_peers peers. - * @param links_succeeded the number of overlay link connection attempts that - * succeeded - * @param links_failed the number of overlay link connection attempts that - * failed - */ -static void -cadet_test_run (void *cls, - struct GNUNET_TESTBED_RunHandle *h, - unsigned int num_peers, - struct GNUNET_TESTBED_Peer **peers, - unsigned int links_succeeded, - unsigned int links_failed) -{ - struct GNUNET_CADET_TEST_Context *ctx = cls; - unsigned int i; - - if (0 != links_failed) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Some links failed (%u), ending\n", - links_failed); - exit (2); - } - - if (num_peers != ctx->num_peers) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Peers started %u/%u, ending\n", - num_peers, ctx->num_peers); - exit (1); - } - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Testbed up, %u peers and %u links\n", - num_peers, links_succeeded); - ctx->peers = peers; - for (i = 0; i < num_peers; i++) - { - struct GNUNET_CADET_TEST_AdapterContext *newctx; - newctx = GNUNET_new (struct GNUNET_CADET_TEST_AdapterContext); - newctx->peer = i; - newctx->ctx = ctx; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connecting to cadet %u\n", i); - ctx->ops[i] = GNUNET_TESTBED_service_connect (ctx, - peers[i], - "cadet", - &cadet_connect_cb, - ctx, - &cadet_connect_adapter, - &cadet_disconnect_adapter, - newctx); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "op handle %p\n", ctx->ops[i]); - } -} - - -/** - * Run a test using the given name, configuration file and number of peers. - * All cadet callbacks will receive the peer number (long) as the closure. - * - * @param testname Name of the test (for logging). - * @param cfgfile 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 @a tmain. - * @param connects Handler for incoming channels. - * @param window_changes Handler for the window size change notification. - * @param disconnects Cleaner for destroyed incoming channels. - * @param handlers Message handlers. - * @param ports Ports the peers offer, NULL-terminated. - */ -void -GNUNET_CADET_TEST_ruN (const char *testname, - const char *cfgfile, - unsigned int num_peers, - GNUNET_CADET_TEST_AppMain tmain, - void *tmain_cls, - GNUNET_CADET_ConnectEventHandler connects, - GNUNET_CADET_WindowSizeEventHandler window_changes, - GNUNET_CADET_DisconnectEventHandler disconnects, - struct GNUNET_MQ_MessageHandler *handlers, - const struct GNUNET_HashCode **ports) -{ - struct GNUNET_CADET_TEST_Context *ctx; - - ctx = GNUNET_new (struct GNUNET_CADET_TEST_Context); - ctx->num_peers = num_peers; - ctx->ops = GNUNET_new_array (num_peers, struct GNUNET_TESTBED_Operation *); - ctx->cadets = GNUNET_new_array (num_peers, struct GNUNET_CADET_Handle *); - ctx->app_main = tmain; - ctx->app_main_cls = tmain_cls; - ctx->connects = connects; - ctx->window_changes = window_changes; - ctx->disconnects = disconnects; - ctx->handlers = GNUNET_MQ_copy_handlers (handlers); - ctx->ports = ports; - ctx->port_count = 0; - while (NULL != ctx->ports[ctx->port_count]) - ctx->port_count++; - - GNUNET_TESTBED_test_run (testname, - cfgfile, - num_peers, - 0LL, NULL, NULL, - &cadet_test_run, ctx); -} - -/* end of cadet_test_lib.c */ diff --git a/src/cadet/cadet_test_lib_new.h b/src/cadet/cadet_test_lib_new.h deleted file mode 100644 index 4b3a6b18dd..0000000000 --- a/src/cadet/cadet_test_lib_new.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012,2017 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file cadet/cadet_test_lib.h - * @author Bartlomiej Polot - * @brief library for writing CADET tests - */ -#ifndef CADET_TEST_LIB_H -#define CADET_TEST_LIB_H - -#ifdef __cplusplus -extern "C" -{ -#if 0 /* keep Emacsens' auto-indent happy */ -} -#endif -#endif - -#include "gnunet_testbed_service.h" -#include "gnunet_cadet_service.h" - -/** - * Test context for a CADET Test. - */ -struct GNUNET_CADET_TEST_Context; - - -/** - * Main function of a CADET test. - * - * @param cls Closure. - * @param ctx Argument to give to GNUNET_CADET_TEST_cleanup on test end. - * @param num_peers Number of peers that are running. - * @param peers Array of peers. - * @param cadets Handle to each of the CADETs of the peers. - */ -typedef void (*GNUNET_CADET_TEST_AppMain) (void *cls, - struct GNUNET_CADET_TEST_Context *ctx, - unsigned int num_peers, - struct GNUNET_TESTBED_Peer **peers, - struct GNUNET_CADET_Handle **cadets); - - -/** - * Run a test using the given name, configuration file and number of peers. - * All cadet callbacks will receive the peer number (long) as the closure. - * - * @param testname Name of the test (for logging). - * @param cfgfile 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 @a tmain. - * @param connects Handler for incoming channels. - * @param window_changes Handler for the window size change notification. - * @param disconnects Cleaner for destroyed incoming channels. - * @param handlers Message handlers. - * @param ports Ports the peers offer, NULL-terminated. - */ -void -GNUNET_CADET_TEST_ruN (const char *testname, - const char *cfgfile, - unsigned int num_peers, - GNUNET_CADET_TEST_AppMain tmain, - void *tmain_cls, - GNUNET_CADET_ConnectEventHandler connects, - GNUNET_CADET_WindowSizeEventHandler window_changes, - GNUNET_CADET_DisconnectEventHandler disconnects, - struct GNUNET_MQ_MessageHandler *handlers, - const struct GNUNET_HashCode **ports); - -/** - * Clean up the testbed. - * - * @param ctx handle for the testbed - */ -void -GNUNET_CADET_TEST_cleanup (struct GNUNET_CADET_TEST_Context *ctx); - - -#if 0 /* keep Emacsens' auto-indent happy */ -{ -#endif -#ifdef __cplusplus -} -#endif - - -/* ifndef CADET_TEST_LIB_H */ -#endif diff --git a/src/cadet/gnunet-service-cadet-new.c b/src/cadet/gnunet-service-cadet-new.c deleted file mode 100644 index 93f53de4c8..0000000000 --- a/src/cadet/gnunet-service-cadet-new.c +++ /dev/null @@ -1,1496 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2001-2013, 2017 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file cadet/gnunet-service-cadet-new.c - * @brief GNUnet CADET service with encryption - * @author Bartlomiej Polot - * @author Christian Grothoff - * - * Dictionary: - * - peer: other cadet instance. If there is direct connection it's a neighbor. - * - path: series of directly connected peer from one peer to another. - * - connection: path which is being used in a tunnel. - * - tunnel: encrypted connection to a peer, neighbor or not. - * - channel: logical link between two clients, on the same or different peers. - * have properties like reliability. - */ - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "cadet.h" -#include "gnunet_statistics_service.h" -#include "gnunet-service-cadet-new.h" -#include "gnunet-service-cadet-new_channel.h" -#include "gnunet-service-cadet-new_connection.h" -#include "gnunet-service-cadet-new_core.h" -#include "gnunet-service-cadet-new_dht.h" -#include "gnunet-service-cadet-new_hello.h" -#include "gnunet-service-cadet-new_tunnels.h" -#include "gnunet-service-cadet-new_peer.h" -#include "gnunet-service-cadet-new_paths.h" - -#define LOG(level, ...) GNUNET_log (level,__VA_ARGS__) - - -/** - * Struct containing information about a client of the service - */ -struct CadetClient -{ - /** - * Linked list next - */ - struct CadetClient *next; - - /** - * Linked list prev - */ - struct CadetClient *prev; - - /** - * Tunnels that belong to this client, indexed by local id, - * value is a `struct CadetChannel`. - */ - struct GNUNET_CONTAINER_MultiHashMap32 *channels; - - /** - * Handle to communicate with the client - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Client handle. - */ - struct GNUNET_SERVICE_Client *client; - - /** - * Ports that this client has declared interest in. - * Indexed by port, contains *Client. - */ - struct GNUNET_CONTAINER_MultiHashMap *ports; - - /** - * Channel ID to use for the next incoming channel for this client. - * Wraps around (in theory). - */ - struct GNUNET_CADET_ClientChannelNumber next_ccn; - - /** - * ID of the client, mainly for debug messages. Purely internal to this file. - */ - unsigned int id; -}; - -/******************************************************************************/ -/*********************** GLOBAL VARIABLES ****************************/ -/******************************************************************************/ - -/****************************** Global variables ******************************/ - -/** - * Handle to our configuration. - */ -const struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * Handle to the statistics service. - */ -struct GNUNET_STATISTICS_Handle *stats; - -/** - * Handle to communicate with ATS. - */ -struct GNUNET_ATS_ConnectivityHandle *ats_ch; - -/** - * Local peer own ID. - */ -struct GNUNET_PeerIdentity my_full_id; - -/** - * Own private key. - */ -struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; - -/** - * Signal that shutdown is happening: prevent recovery measures. - */ -int shutting_down; - -/** - * DLL with all the clients, head. - */ -static struct CadetClient *clients_head; - -/** - * DLL with all the clients, tail. - */ -static struct CadetClient *clients_tail; - -/** - * Next ID to assign to a client. - */ -static unsigned int next_client_id; - -/** - * All ports clients of this peer have opened. - */ -struct GNUNET_CONTAINER_MultiHashMap *open_ports; - -/** - * Map from ports to channels where the ports were closed at the - * time we got the inbound connection. - * Indexed by port, contains `struct CadetChannel`. - */ -struct GNUNET_CONTAINER_MultiHashMap *loose_channels; - -/** - * Map from PIDs to `struct CadetPeer` entries. - */ -struct GNUNET_CONTAINER_MultiPeerMap *peers; - -/** - * Map from `struct GNUNET_CADET_ConnectionTunnelIdentifier` - * hash codes to `struct CadetConnection` objects. - */ -struct GNUNET_CONTAINER_MultiShortmap *connections; - -/** - * How many messages are needed to trigger an AXOLOTL ratchet advance. - */ -unsigned long long ratchet_messages; - -/** - * How long until we trigger a ratched advance due to time. - */ -struct GNUNET_TIME_Relative ratchet_time; - -/** - * How frequently do we send KEEPALIVE messages on idle connections? - */ -struct GNUNET_TIME_Relative keepalive_period; - -/** - * Set to non-zero values to create random drops to test retransmissions. - */ -unsigned long long drop_percent; - - -/** - * Send a message to a client. - * - * @param c client to get the message - * @param env envelope with the message - */ -void -GSC_send_to_client (struct CadetClient *c, - struct GNUNET_MQ_Envelope *env) -{ - GNUNET_MQ_send (c->mq, - env); -} - - -/** - * Return identifier for a client as a string. - * - * @param c client to identify - * @return string for debugging - */ -const char * -GSC_2s (struct CadetClient *c) -{ - static char buf[32]; - - GNUNET_snprintf (buf, - sizeof (buf), - "Client(%u)", - c->id); - return buf; -} - - -/** - * Lookup channel of client @a c by @a ccn. - * - * @param c client to look in - * @param ccn channel ID to look up - * @return NULL if no such channel exists - */ -static struct CadetChannel * -lookup_channel (struct CadetClient *c, - struct GNUNET_CADET_ClientChannelNumber ccn) -{ - return GNUNET_CONTAINER_multihashmap32_get (c->channels, - ntohl (ccn.channel_of_client)); -} - - -/** - * Obtain the next LID to use for incoming connections to - * the given client. - * - * @param c client handle - */ -static struct GNUNET_CADET_ClientChannelNumber -client_get_next_ccn (struct CadetClient *c) -{ - struct GNUNET_CADET_ClientChannelNumber ccn = c->next_ccn; - - /* increment until we have a free one... */ - while (NULL != - lookup_channel (c, - ccn)) - { - ccn.channel_of_client - = htonl (1 + (ntohl (ccn.channel_of_client))); - if (ntohl (ccn.channel_of_client) >= - GNUNET_CADET_LOCAL_CHANNEL_ID_CLI) - ccn.channel_of_client = htonl (0); - } - c->next_ccn.channel_of_client - = htonl (1 + (ntohl (ccn.channel_of_client))); - return ccn; -} - - -/** - * Bind incoming channel to this client, and notify client about - * incoming connection. Caller is responsible for notifying the other - * peer about our acceptance of the channel. - * - * @param c client to bind to - * @param ch channel to be bound - * @param dest peer that establishes the connection - * @param port port number - * @param options options - * @return local channel number assigned to the new client - */ -struct GNUNET_CADET_ClientChannelNumber -GSC_bind (struct CadetClient *c, - struct CadetChannel *ch, - struct CadetPeer *dest, - const struct GNUNET_HashCode *port, - uint32_t options) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_CADET_LocalChannelCreateMessage *cm; - struct GNUNET_CADET_ClientChannelNumber ccn; - - ccn = client_get_next_ccn (c); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap32_put (c->channels, - ntohl (ccn.channel_of_client), - ch, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Accepting incoming %s from %s on open port %s (%u), assigning ccn %X\n", - GCCH_2s (ch), - GCP_2s (dest), - GNUNET_h2s (port), - (uint32_t) ntohl (options), - (uint32_t) ntohl (ccn.channel_of_client)); - /* notify local client about incoming connection! */ - env = GNUNET_MQ_msg (cm, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_CREATE); - cm->ccn = ccn; - cm->port = *port; - cm->opt = htonl (options); - cm->peer = *GCP_get_id (dest); - GSC_send_to_client (c, - env); - return ccn; -} - - -/** - * Callback invoked on all peers to destroy all tunnels - * that may still exist. - * - * @param cls NULL - * @param pid identify of a peer - * @param value a `struct CadetPeer` that may still have a tunnel - * @return #GNUNET_OK (iterate over all entries) - */ -static int -destroy_tunnels_now (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct CadetPeer *cp = value; - struct CadetTunnel *t = GCP_get_tunnel (cp, - GNUNET_NO); - - if (NULL != t) - GCT_destroy_tunnel_now (t); - return GNUNET_OK; -} - - -/** - * Callback invoked on all peers to destroy all tunnels - * that may still exist. - * - * @param cls NULL - * @param pid identify of a peer - * @param value a `struct CadetPeer` that may still have a tunnel - * @return #GNUNET_OK (iterate over all entries) - */ -static int -destroy_paths_now (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct CadetPeer *cp = value; - - GCP_drop_owned_paths (cp); - return GNUNET_OK; -} - - -/** - * Shutdown everything once the clients have disconnected. - */ -static void -shutdown_rest () -{ - if (NULL != stats) - { - GNUNET_STATISTICS_destroy (stats, - GNUNET_NO); - stats = NULL; - } - if (NULL != open_ports) - { - GNUNET_CONTAINER_multihashmap_destroy (open_ports); - open_ports = NULL; - } - if (NULL != loose_channels) - { - GNUNET_CONTAINER_multihashmap_destroy (loose_channels); - loose_channels = NULL; - } - /* Destroy tunnels. Note that all channels must be destroyed first! */ - GCP_iterate_all (&destroy_tunnels_now, - NULL); - /* All tunnels, channels, connections and CORE must be down before this point. */ - GCP_iterate_all (&destroy_paths_now, - NULL); - /* All paths, tunnels, channels, connections and CORE must be down before this point. */ - GCP_destroy_all_peers (); - if (NULL != peers) - { - GNUNET_CONTAINER_multipeermap_destroy (peers); - peers = NULL; - } - if (NULL != connections) - { - GNUNET_CONTAINER_multishortmap_destroy (connections); - connections = NULL; - } - if (NULL != ats_ch) - { - GNUNET_ATS_connectivity_done (ats_ch); - ats_ch = NULL; - } - GCD_shutdown (); - GCH_shutdown (); - GNUNET_free_non_null (my_private_key); - my_private_key = NULL; -} - - -/** - * Task run during shutdown. - * - * @param cls unused - */ -static void -shutdown_task (void *cls) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Shutting down\n"); - shutting_down = GNUNET_YES; - GCO_shutdown (); - if (NULL == clients_head) - shutdown_rest (); -} - - -/** - * We had a remote connection @a value to port @a port before - * client @a cls opened port @a port. Bind them now. - * - * @param cls the `struct CadetClient` - * @param port the port - * @param value the `struct CadetChannel` - * @return #GNUNET_YES (iterate over all such channels) - */ -static int -bind_loose_channel (void *cls, - const struct GNUNET_HashCode *port, - void *value) -{ - struct CadetClient *c = cls; - struct CadetChannel *ch = value; - - GCCH_bind (ch, - c); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (loose_channels, - port, - value)); - return GNUNET_YES; -} - - -/** - * Handle port open request. Creates a mapping from the - * port to the respective client and checks whether we have - * loose channels trying to bind to the port. If so, those - * are bound. - * - * @param cls Identification of the client. - * @param pmsg The actual message. - */ -static void -handle_port_open (void *cls, - const struct GNUNET_CADET_PortMessage *pmsg) -{ - struct CadetClient *c = cls; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Open port %s requested by %s\n", - GNUNET_h2s (&pmsg->port), - GSC_2s (c)); - if (NULL == c->ports) - c->ports = GNUNET_CONTAINER_multihashmap_create (4, - GNUNET_NO); - if (GNUNET_OK != - GNUNET_CONTAINER_multihashmap_put (c->ports, - &pmsg->port, - c, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (c->client); - return; - } - (void) GNUNET_CONTAINER_multihashmap_put (open_ports, - &pmsg->port, - c, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); - GNUNET_CONTAINER_multihashmap_get_multiple (loose_channels, - &pmsg->port, - &bind_loose_channel, - c); - GNUNET_SERVICE_client_continue (c->client); -} - - -/** - * Handler for port close requests. Marks this port as closed - * (unless of course we have another client with the same port - * open). Note that existing channels accepted on the port are - * not affected. - * - * @param cls Identification of the client. - * @param pmsg The actual message. - */ -static void -handle_port_close (void *cls, - const struct GNUNET_CADET_PortMessage *pmsg) -{ - struct CadetClient *c = cls; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Closing port %s as requested by %s\n", - GNUNET_h2s (&pmsg->port), - GSC_2s (c)); - if (GNUNET_YES != - GNUNET_CONTAINER_multihashmap_remove (c->ports, - &pmsg->port, - c)) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (c->client); - return; - } - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (open_ports, - &pmsg->port, - c)); - GNUNET_SERVICE_client_continue (c->client); -} - - -/** - * Handler for requests for us creating a new channel to another peer and port. - * - * @param cls Identification of the client. - * @param tcm The actual message. - */ -static void -handle_channel_create (void *cls, - const struct GNUNET_CADET_LocalChannelCreateMessage *tcm) -{ - struct CadetClient *c = cls; - struct CadetChannel *ch; - - if (ntohl (tcm->ccn.channel_of_client) < GNUNET_CADET_LOCAL_CHANNEL_ID_CLI) - { - /* Channel ID not in allowed range. */ - GNUNET_break (0); - GNUNET_SERVICE_client_drop (c->client); - return; - } - ch = lookup_channel (c, - tcm->ccn); - if (NULL != ch) - { - /* Channel ID already in use. Not allowed. */ - GNUNET_break (0); - GNUNET_SERVICE_client_drop (c->client); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "New channel to %s at port %s requested by %s\n", - GNUNET_i2s (&tcm->peer), - GNUNET_h2s (&tcm->port), - GSC_2s (c)); - - /* Create channel */ - ch = GCCH_channel_local_new (c, - tcm->ccn, - GCP_get (&tcm->peer, - GNUNET_YES), - &tcm->port, - ntohl (tcm->opt)); - if (NULL == ch) - { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (c->client); - return; - } - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap32_put (c->channels, - ntohl (tcm->ccn.channel_of_client), - ch, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - - GNUNET_SERVICE_client_continue (c->client); -} - - -/** - * Handler for requests of destroying an existing channel. - * - * @param cls client identification of the client - * @param msg the actual message - */ -static void -handle_channel_destroy (void *cls, - const struct GNUNET_CADET_LocalChannelDestroyMessage *msg) -{ - struct CadetClient *c = cls; - struct CadetChannel *ch; - - ch = lookup_channel (c, - msg->ccn); - if (NULL == ch) - { - /* Client attempted to destroy unknown channel. - Can happen if the other side went down at the same time.*/ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "%s tried to destroy unknown channel %X\n", - GSC_2s(c), - (uint32_t) ntohl (msg->ccn.channel_of_client)); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "%s is destroying %s\n", - GSC_2s(c), - GCCH_2s (ch)); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap32_remove (c->channels, - ntohl (msg->ccn.channel_of_client), - ch)); - GCCH_channel_local_destroy (ch, - c, - msg->ccn); - GNUNET_SERVICE_client_continue (c->client); -} - - -/** - * Check for client traffic data message is well-formed. - * - * @param cls identification of the client - * @param msg the actual message - * @return #GNUNET_OK if @a msg is OK, #GNUNET_SYSERR if not - */ -static int -check_local_data (void *cls, - const struct GNUNET_CADET_LocalData *msg) -{ - size_t payload_size; - size_t payload_claimed_size; - const char *buf; - struct GNUNET_MessageHeader pa; - - /* FIXME: what is the format we shall allow for @a msg? - ONE payload item or multiple? Seems current cadet_api - at least in theory allows more than one. Next-gen - cadet_api will likely no more, so we could then - simplify this mess again. */ - /* Sanity check for message size */ - payload_size = ntohs (msg->header.size) - sizeof (*msg); - buf = (const char *) &msg[1]; - while (payload_size >= sizeof (struct GNUNET_MessageHeader)) - { - /* need to memcpy() for alignment */ - GNUNET_memcpy (&pa, - buf, - sizeof (pa)); - payload_claimed_size = ntohs (pa.size); - if ( (payload_size < payload_claimed_size) || - (payload_claimed_size < sizeof (struct GNUNET_MessageHeader)) || - (GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE < payload_claimed_size) ) - { - GNUNET_break (0); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Local data of %u total size had sub-message %u at %u with %u bytes\n", - ntohs (msg->header.size), - ntohs (pa.type), - (unsigned int) (buf - (const char *) &msg[1]), - (unsigned int) payload_claimed_size); - return GNUNET_SYSERR; - } - payload_size -= payload_claimed_size; - buf += payload_claimed_size; - } - if (0 != payload_size) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Handler for client payload traffic to be send on a channel to - * another peer. - * - * @param cls identification of the client - * @param msg the actual message - */ -static void -handle_local_data (void *cls, - const struct GNUNET_CADET_LocalData *msg) -{ - struct CadetClient *c = cls; - struct CadetChannel *ch; - size_t payload_size; - const char *buf; - - ch = lookup_channel (c, - msg->ccn); - if (NULL == ch) - { - /* Channel does not exist (anymore) */ - LOG (GNUNET_ERROR_TYPE_WARNING, - "Dropping payload for channel %u from client (channel unknown, other endpoint may have disconnected)\n", - (unsigned int) ntohl (msg->ccn.channel_of_client)); - GNUNET_SERVICE_client_continue (c->client); - return; - } - payload_size = ntohs (msg->header.size) - sizeof (*msg); - GNUNET_STATISTICS_update (stats, - "# payload received from clients", - payload_size, - GNUNET_NO); - buf = (const char *) &msg[1]; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received %u bytes payload from %s for %s\n", - (unsigned int) payload_size, - GSC_2s (c), - GCCH_2s (ch)); - if (GNUNET_OK != - GCCH_handle_local_data (ch, - msg->ccn, - buf, - payload_size)) - { - GNUNET_SERVICE_client_drop (c->client); - return; - } - GNUNET_SERVICE_client_continue (c->client); -} - - -/** - * Handler for client's ACKs for payload traffic. - * - * @param cls identification of the client. - * @param msg The actual message. - */ -static void -handle_local_ack (void *cls, - const struct GNUNET_CADET_LocalAck *msg) -{ - struct CadetClient *c = cls; - struct CadetChannel *ch; - - ch = lookup_channel (c, - msg->ccn); - if (NULL == ch) - { - /* Channel does not exist (anymore) */ - LOG (GNUNET_ERROR_TYPE_WARNING, - "Ignoring local ACK for channel %u from client (channel unknown, other endpoint may have disconnected)\n", - (unsigned int) ntohl (msg->ccn.channel_of_client)); - GNUNET_SERVICE_client_continue (c->client); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got a local ACK from %s for %s\n", - GSC_2s(c), - GCCH_2s (ch)); - GCCH_handle_local_ack (ch, - msg->ccn); - GNUNET_SERVICE_client_continue (c->client); -} - - -/** - * Iterator over all peers to send a monitoring client info about each peer. - * - * @param cls Closure (). - * @param peer Peer ID (tunnel remote peer). - * @param value Peer info. - * @return #GNUNET_YES, to keep iterating. - */ -static int -get_all_peers_iterator (void *cls, - const struct GNUNET_PeerIdentity *peer, - void *value) -{ - struct CadetClient *c = cls; - struct CadetPeer *p = value; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_CADET_LocalInfoPeer *msg; - - env = GNUNET_MQ_msg (msg, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEERS); - msg->destination = *peer; - msg->paths = htons (GCP_count_paths (p)); - msg->tunnel = htons (NULL != GCP_get_tunnel (p, - GNUNET_NO)); - GNUNET_MQ_send (c->mq, - env); - return GNUNET_YES; -} - - -/** - * Handler for client's INFO PEERS request. - * - * @param cls Identification of the client. - * @param message The actual message. - */ -static void -handle_get_peers (void *cls, - const struct GNUNET_MessageHeader *message) -{ - struct CadetClient *c = cls; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_MessageHeader *reply; - - GCP_iterate_all (&get_all_peers_iterator, - c); - env = GNUNET_MQ_msg (reply, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEERS); - GNUNET_MQ_send (c->mq, - env); - GNUNET_SERVICE_client_continue (c->client); -} - - -/** - * Iterator over all paths of a peer to build an InfoPeer message. - * Message contains blocks of peers, first not included. - * - * @param cls message queue for transmission - * @param path Path itself - * @param off offset of the peer on @a path - * @return #GNUNET_YES if should keep iterating. - * #GNUNET_NO otherwise. - */ -static int -path_info_iterator (void *cls, - struct CadetPeerPath *path, - unsigned int off) -{ - struct GNUNET_MQ_Handle *mq = cls; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_MessageHeader *resp; - struct GNUNET_PeerIdentity *id; - uint16_t path_size; - unsigned int i; - unsigned int path_length; - - path_length = GCPP_get_length (path); - path_size = sizeof (struct GNUNET_PeerIdentity) * (path_length - 1); - if (sizeof (*resp) + path_size > UINT16_MAX) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - "Path of %u entries is too long for info message\n", - path_length); - return GNUNET_YES; - } - env = GNUNET_MQ_msg_extra (resp, - path_size, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEER); - id = (struct GNUNET_PeerIdentity *) &resp[1]; - - /* Don't copy first peer. First peer is always the local one. Last - * peer is always the destination (leave as 0, EOL). - */ - for (i = 0; i < off; i++) - id[i] = *GCP_get_id (GCPP_get_peer_at_offset (path, - i + 1)); - GNUNET_MQ_send (mq, - env); - return GNUNET_YES; -} - - -/** - * Handler for client's SHOW_PEER request. - * - * @param cls Identification of the client. - * @param msg The actual message. - */ -static void -handle_show_peer (void *cls, - const struct GNUNET_CADET_LocalInfo *msg) -{ - struct CadetClient *c = cls; - struct CadetPeer *p; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_MessageHeader *resp; - - p = GCP_get (&msg->peer, - GNUNET_NO); - if (NULL != p) - GCP_iterate_paths (p, - &path_info_iterator, - c->mq); - /* Send message with 0/0 to indicate the end */ - env = GNUNET_MQ_msg (resp, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEER_END); - GNUNET_MQ_send (c->mq, - env); - GNUNET_SERVICE_client_continue (c->client); -} - - -/** - * Iterator over all tunnels to send a monitoring client info about each tunnel. - * - * @param cls Closure (). - * @param peer Peer ID (tunnel remote peer). - * @param value a `struct CadetPeer` - * @return #GNUNET_YES, to keep iterating. - */ -static int -get_all_tunnels_iterator (void *cls, - const struct GNUNET_PeerIdentity *peer, - void *value) -{ - struct CadetClient *c = cls; - struct CadetPeer *p = value; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_CADET_LocalInfoTunnel *msg; - struct CadetTunnel *t; - - t = GCP_get_tunnel (p, - GNUNET_NO); - if (NULL == t) - return GNUNET_YES; - env = GNUNET_MQ_msg (msg, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNELS); - msg->destination = *peer; - msg->channels = htonl (GCT_count_channels (t)); - msg->connections = htonl (GCT_count_any_connections (t)); - msg->cstate = htons (0); - msg->estate = htons ((uint16_t) GCT_get_estate (t)); - GNUNET_MQ_send (c->mq, - env); - return GNUNET_YES; -} - - -/** - * Handler for client's #GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNELS request. - * - * @param cls client Identification of the client. - * @param message The actual message. - */ -static void -handle_info_tunnels (void *cls, - const struct GNUNET_MessageHeader *message) -{ - struct CadetClient *c = cls; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_MessageHeader *reply; - - GCP_iterate_all (&get_all_tunnels_iterator, - c); - env = GNUNET_MQ_msg (reply, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNELS); - GNUNET_MQ_send (c->mq, - env); - GNUNET_SERVICE_client_continue (c->client); -} - - -/** - * Update the message with information about the connection. - * - * @param cls a `struct GNUNET_CADET_LocalInfoTunnel` message to update - * @param ct a connection about which we should store information in @a cls - */ -static void -iter_connection (void *cls, - struct CadetTConnection *ct) -{ - struct GNUNET_CADET_LocalInfoTunnel *msg = cls; - struct CadetConnection *cc = ct->cc; - struct GNUNET_CADET_ConnectionTunnelIdentifier *h; - - h = (struct GNUNET_CADET_ConnectionTunnelIdentifier *) &msg[1]; - h[msg->connections++] = *(GCC_get_id (cc)); -} - - -/** - * Update the message with information about the channel. - * - * @param cls a `struct GNUNET_CADET_LocalInfoTunnel` message to update - * @param ch a channel about which we should store information in @a cls - */ -static void -iter_channel (void *cls, - struct CadetChannel *ch) -{ - struct GNUNET_CADET_LocalInfoTunnel *msg = cls; - struct GNUNET_CADET_ConnectionTunnelIdentifier *h = (struct GNUNET_CADET_ConnectionTunnelIdentifier *) &msg[1]; - struct GNUNET_CADET_ChannelTunnelNumber *chn - = (struct GNUNET_CADET_ChannelTunnelNumber *) &h[msg->connections]; - - chn[msg->channels++] = GCCH_get_id (ch); -} - - -/** - * Handler for client's #GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNEL request. - * - * @param cls Identification of the client. - * @param msg The actual message. - */ -static void -handle_info_tunnel (void *cls, - const struct GNUNET_CADET_LocalInfo *msg) -{ - struct CadetClient *c = cls; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_CADET_LocalInfoTunnel *resp; - struct CadetTunnel *t; - struct CadetPeer *p; - unsigned int ch_n; - unsigned int c_n; - - p = GCP_get (&msg->peer, - GNUNET_NO); - t = GCP_get_tunnel (p, - GNUNET_NO); - if (NULL == t) - { - /* We don't know the tunnel */ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_CADET_LocalInfoTunnel *warn; - - LOG (GNUNET_ERROR_TYPE_INFO, - "Tunnel to %s unknown\n", - GNUNET_i2s_full (&msg->peer)); - env = GNUNET_MQ_msg (warn, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNEL); - warn->destination = msg->peer; - GNUNET_MQ_send (c->mq, - env); - GNUNET_SERVICE_client_continue (c->client); - return; - } - - /* Initialize context */ - ch_n = GCT_count_channels (t); - c_n = GCT_count_any_connections (t); - env = GNUNET_MQ_msg_extra (resp, - c_n * sizeof (struct GNUNET_CADET_ConnectionTunnelIdentifier) + - ch_n * sizeof (struct GNUNET_CADET_ChannelTunnelNumber), - GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNEL); - resp->destination = msg->peer; - /* Do not reorder! #iter_channel needs counters in HBO! */ - GCT_iterate_connections (t, - &iter_connection, - resp); - GCT_iterate_channels (t, - &iter_channel, - resp); - resp->connections = htonl (resp->connections); - resp->channels = htonl (resp->channels); - resp->cstate = htons (0); - resp->estate = htons (GCT_get_estate (t)); - GNUNET_MQ_send (c->mq, - env); - GNUNET_SERVICE_client_continue (c->client); -} - - -/** - * Iterator over all peers to dump info for each peer. - * - * @param cls Closure (unused). - * @param peer Peer ID (tunnel remote peer). - * @param value Peer info. - * - * @return #GNUNET_YES, to keep iterating. - */ -static int -show_peer_iterator (void *cls, - const struct GNUNET_PeerIdentity *peer, - void *value) -{ - struct CadetPeer *p = value; - struct CadetTunnel *t; - - t = GCP_get_tunnel (p, - GNUNET_NO); - if (NULL != t) - GCT_debug (t, - GNUNET_ERROR_TYPE_ERROR); - LOG (GNUNET_ERROR_TYPE_ERROR, "\n"); - return GNUNET_YES; -} - - -/** - * Handler for client's INFO_DUMP request. - * - * @param cls Identification of the client. - * @param message The actual message. - */ -static void -handle_info_dump (void *cls, - const struct GNUNET_MessageHeader *message) -{ - struct CadetClient *c = cls; - - LOG (GNUNET_ERROR_TYPE_INFO, - "Received dump info request from client %u\n", - c->id); - - LOG (GNUNET_ERROR_TYPE_ERROR, - "*************************** DUMP START ***************************\n"); - for (struct CadetClient *ci = clients_head; - NULL != ci; - ci = ci->next) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Client %u (%p), handle: %p, ports: %u, channels: %u\n", - ci->id, - ci, - ci->client, - (NULL != c->ports) - ? GNUNET_CONTAINER_multihashmap_size (ci->ports) - : 0, - GNUNET_CONTAINER_multihashmap32_size (ci->channels)); - } - LOG (GNUNET_ERROR_TYPE_ERROR, "***************************\n"); - GCP_iterate_all (&show_peer_iterator, - NULL); - - LOG (GNUNET_ERROR_TYPE_ERROR, - "**************************** DUMP END ****************************\n"); - - GNUNET_SERVICE_client_continue (c->client); -} - - - -/** - * Callback called when a client connects to the service. - * - * @param cls closure for the service - * @param client the new client that connected to the service - * @param mq the message queue used to send messages to the client - * @return @a c - */ -static void * -client_connect_cb (void *cls, - struct GNUNET_SERVICE_Client *client, - struct GNUNET_MQ_Handle *mq) -{ - struct CadetClient *c; - - c = GNUNET_new (struct CadetClient); - c->client = client; - c->mq = mq; - c->id = next_client_id++; /* overflow not important: just for debug */ - c->channels - = GNUNET_CONTAINER_multihashmap32_create (32); - GNUNET_CONTAINER_DLL_insert (clients_head, - clients_tail, - c); - GNUNET_STATISTICS_update (stats, - "# clients", - +1, - GNUNET_NO); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "%s connected\n", - GSC_2s (c)); - return c; -} - - -/** - * A channel was destroyed by the other peer. Tell our client. - * - * @param c client that lost a channel - * @param ccn channel identification number for the client - * @param ch the channel object - */ -void -GSC_handle_remote_channel_destroy (struct CadetClient *c, - struct GNUNET_CADET_ClientChannelNumber ccn, - struct CadetChannel *ch) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_CADET_LocalChannelDestroyMessage *tdm; - - env = GNUNET_MQ_msg (tdm, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_DESTROY); - tdm->ccn = ccn; - GSC_send_to_client (c, - env); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap32_remove (c->channels, - ntohl (ccn.channel_of_client), - ch)); -} - - -/** - * A client that created a loose channel that was not bound to a port - * disconnected, drop it from the #loose_channels list. - * - * @param port the port the channel was trying to bind to - * @param ch the channel that was lost - */ -void -GSC_drop_loose_channel (const struct GNUNET_HashCode *port, - struct CadetChannel *ch) -{ - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (loose_channels, - port, - ch)); -} - - -/** - * Iterator for deleting each channel whose client endpoint disconnected. - * - * @param cls Closure (client that has disconnected). - * @param key The local channel id in host byte order - * @param value The value stored at the key (channel to destroy). - * @return #GNUNET_OK, keep iterating. - */ -static int -channel_destroy_iterator (void *cls, - uint32_t key, - void *value) -{ - struct CadetClient *c = cls; - struct GNUNET_CADET_ClientChannelNumber ccn; - struct CadetChannel *ch = value; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Destroying %s, due to %s disconnecting.\n", - GCCH_2s (ch), - GSC_2s (c)); - ccn.channel_of_client = htonl (key); - GCCH_channel_local_destroy (ch, - c, - ccn); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap32_remove (c->channels, - key, - ch)); - return GNUNET_OK; -} - - -/** - * Remove client's ports from the global hashmap on disconnect. - * - * @param cls Closure (unused). - * @param key the port. - * @param value the `struct CadetClient` to remove - * @return #GNUNET_OK, keep iterating. - */ -static int -client_release_ports (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct CadetClient *c = value; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Closing port %s due to %s disconnect.\n", - GNUNET_h2s (key), - GSC_2s (c)); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (open_ports, - key, - value)); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (c->ports, - key, - value)); - return GNUNET_OK; -} - - -/** - * Callback called when a client disconnected from the service - * - * @param cls closure for the service - * @param client the client that disconnected - * @param internal_cls should be equal to @a c - */ -static void -client_disconnect_cb (void *cls, - struct GNUNET_SERVICE_Client *client, - void *internal_cls) -{ - struct CadetClient *c = internal_cls; - - GNUNET_assert (c->client == client); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "%s is disconnecting.\n", - GSC_2s (c)); - if (NULL != c->channels) - { - GNUNET_CONTAINER_multihashmap32_iterate (c->channels, - &channel_destroy_iterator, - c); - GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap32_size (c->channels)); - GNUNET_CONTAINER_multihashmap32_destroy (c->channels); - } - if (NULL != c->ports) - { - GNUNET_CONTAINER_multihashmap_iterate (c->ports, - &client_release_ports, - c); - GNUNET_CONTAINER_multihashmap_destroy (c->ports); - } - GNUNET_CONTAINER_DLL_remove (clients_head, - clients_tail, - c); - GNUNET_STATISTICS_update (stats, - "# clients", - -1, - GNUNET_NO); - GNUNET_free (c); - if ( (NULL == clients_head) && - (GNUNET_YES == shutting_down) ) - shutdown_rest (); -} - - -/** - * Setup CADET internals. - * - * @param cls closure - * @param server the initialized server - * @param c configuration to use - */ -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *c, - struct GNUNET_SERVICE_Handle *service) -{ - cfg = c; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (c, - "CADET", - "RATCHET_MESSAGES", - &ratchet_messages)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, - "CADET", - "RATCHET_MESSAGES", - "needs to be a number"); - ratchet_messages = 64; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (c, - "CADET", - "RATCHET_TIME", - &ratchet_time)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, - "CADET", - "RATCHET_TIME", - "need delay value"); - ratchet_time = GNUNET_TIME_UNIT_HOURS; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (c, - "CADET", - "REFRESH_CONNECTION_TIME", - &keepalive_period)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, - "CADET", - "REFRESH_CONNECTION_TIME", - "need delay value"); - keepalive_period = GNUNET_TIME_UNIT_MINUTES; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (c, - "CADET", - "DROP_PERCENT", - &drop_percent)) - { - drop_percent = 0; - } - else - { - LOG (GNUNET_ERROR_TYPE_WARNING, "**************************************\n"); - LOG (GNUNET_ERROR_TYPE_WARNING, "Cadet is running with DROP enabled.\n"); - LOG (GNUNET_ERROR_TYPE_WARNING, "This is NOT a good idea!\n"); - LOG (GNUNET_ERROR_TYPE_WARNING, "Remove DROP_PERCENT from config file.\n"); - LOG (GNUNET_ERROR_TYPE_WARNING, "**************************************\n"); - } - my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (c); - if (NULL == my_private_key) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - return; - } - GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, - &my_full_id.public_key); - stats = GNUNET_STATISTICS_create ("cadet", - c); - GNUNET_SCHEDULER_add_shutdown (&shutdown_task, - NULL); - ats_ch = GNUNET_ATS_connectivity_init (c); - /* FIXME: optimize code to allow GNUNET_YES here! */ - open_ports = GNUNET_CONTAINER_multihashmap_create (16, - GNUNET_NO); - loose_channels = GNUNET_CONTAINER_multihashmap_create (16, - GNUNET_NO); - peers = GNUNET_CONTAINER_multipeermap_create (16, - GNUNET_YES); - connections = GNUNET_CONTAINER_multishortmap_create (256, - GNUNET_YES); - GCH_init (c); - GCD_init (c); - GCO_init (c); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "CADET started for peer %s\n", - GNUNET_i2s (&my_full_id)); - -} - - -/** - * Define "main" method using service macro. - */ -GNUNET_SERVICE_MAIN -("cadet", - GNUNET_SERVICE_OPTION_NONE, - &run, - &client_connect_cb, - &client_disconnect_cb, - NULL, - GNUNET_MQ_hd_fixed_size (port_open, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_PORT_OPEN, - struct GNUNET_CADET_PortMessage, - NULL), - GNUNET_MQ_hd_fixed_size (port_close, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_PORT_CLOSE, - struct GNUNET_CADET_PortMessage, - NULL), - GNUNET_MQ_hd_fixed_size (channel_create, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_CREATE, - struct GNUNET_CADET_LocalChannelCreateMessage, - NULL), - GNUNET_MQ_hd_fixed_size (channel_destroy, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_DESTROY, - struct GNUNET_CADET_LocalChannelDestroyMessage, - NULL), - GNUNET_MQ_hd_var_size (local_data, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA, - struct GNUNET_CADET_LocalData, - NULL), - GNUNET_MQ_hd_fixed_size (local_ack, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_ACK, - struct GNUNET_CADET_LocalAck, - NULL), - GNUNET_MQ_hd_fixed_size (get_peers, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEERS, - struct GNUNET_MessageHeader, - NULL), - GNUNET_MQ_hd_fixed_size (show_peer, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEER, - struct GNUNET_CADET_LocalInfo, - NULL), - GNUNET_MQ_hd_fixed_size (info_tunnels, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNELS, - struct GNUNET_MessageHeader, - NULL), - GNUNET_MQ_hd_fixed_size (info_tunnel, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNEL, - struct GNUNET_CADET_LocalInfo, - NULL), - GNUNET_MQ_hd_fixed_size (info_dump, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_DUMP, - struct GNUNET_MessageHeader, - NULL), - GNUNET_MQ_handler_end ()); - -/* end of gnunet-service-cadet-new.c */ diff --git a/src/cadet/gnunet-service-cadet-new_channel.c b/src/cadet/gnunet-service-cadet-new_channel.c deleted file mode 100644 index 43c9058167..0000000000 --- a/src/cadet/gnunet-service-cadet-new_channel.c +++ /dev/null @@ -1,2040 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2001-2017 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file cadet/gnunet-service-cadet-new_channel.c - * @brief logical links between CADET clients - * @author Bartlomiej Polot - * @author Christian Grothoff - * - * TODO: - * - Congestion/flow control: - * + estimate max bandwidth using bursts and use to for CONGESTION CONTROL! - * (and figure out how/where to use this!) - * + figure out flow control without ACKs (unreliable traffic!) - * - revisit handling of 'unbuffered' traffic! - * (need to push down through tunnel into connection selection) - * - revisit handling of 'buffered' traffic: 4 is a rather small buffer; maybe - * reserve more bits in 'options' to allow for buffer size control? - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "cadet.h" -#include "gnunet_statistics_service.h" -#include "gnunet-service-cadet-new.h" -#include "gnunet-service-cadet-new_channel.h" -#include "gnunet-service-cadet-new_connection.h" -#include "gnunet-service-cadet-new_tunnels.h" -#include "gnunet-service-cadet-new_peer.h" -#include "gnunet-service-cadet-new_paths.h" - -#define LOG(level,...) GNUNET_log_from (level,"cadet-chn",__VA_ARGS__) - -/** - * How long do we initially wait before retransmitting? - */ -#define CADET_INITIAL_RETRANSMIT_TIME GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 250) - -/** - * How long do we wait before dropping state about incoming - * connection to closed port? - */ -#define TIMEOUT_CLOSED_PORT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30) - -/** - * How long do we wait at least before retransmitting ever? - */ -#define MIN_RTT_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 75) - -/** - * Maximum message ID into the future we accept for out-of-order messages. - * If the message is more than this into the future, we drop it. This is - * important both to detect values that are actually in the past, as well - * as to limit adversarially triggerable memory consumption. - * - * Note that right now we have "max_pending_messages = 4" hard-coded in - * the logic below, so a value of 4 would suffice here. But we plan to - * allow larger windows in the future... - */ -#define MAX_OUT_OF_ORDER_DISTANCE 1024 - - -/** - * All the states a channel can be in. - */ -enum CadetChannelState -{ - /** - * Uninitialized status, should never appear in operation. - */ - CADET_CHANNEL_NEW, - - /** - * Channel is to a port that is not open, we're waiting for the - * port to be opened. - */ - CADET_CHANNEL_LOOSE, - - /** - * CHANNEL_OPEN message sent, waiting for CHANNEL_OPEN_ACK. - */ - CADET_CHANNEL_OPEN_SENT, - - /** - * Connection confirmed, ready to carry traffic. - */ - CADET_CHANNEL_READY -}; - - -/** - * Info needed to retry a message in case it gets lost. - * Note that we DO use this structure also for unreliable - * messages. - */ -struct CadetReliableMessage -{ - /** - * Double linked list, FIFO style - */ - struct CadetReliableMessage *next; - - /** - * Double linked list, FIFO style - */ - struct CadetReliableMessage *prev; - - /** - * Which channel is this message in? - */ - struct CadetChannel *ch; - - /** - * Entry in the tunnels queue for this message, NULL if it has left - * the tunnel. Used to cancel transmission in case we receive an - * ACK in time. - */ - struct CadetTunnelQueueEntry *qe; - - /** - * Data message we are trying to send. - */ - struct GNUNET_CADET_ChannelAppDataMessage *data_message; - - /** - * How soon should we retry if we fail to get an ACK? - * Messages in the queue are sorted by this value. - */ - struct GNUNET_TIME_Absolute next_retry; - - /** - * How long do we wait for an ACK after transmission? - * Use for the back-off calculation. - */ - struct GNUNET_TIME_Relative retry_delay; - - /** - * Time when we first successfully transmitted the message - * (that is, set @e num_transmissions to 1). - */ - struct GNUNET_TIME_Absolute first_transmission_time; - - /** - * Identifier of the connection that this message took when it - * was first transmitted. Only useful if @e num_transmissions is 1. - */ - struct GNUNET_CADET_ConnectionTunnelIdentifier connection_taken; - - /** - * How often was this message transmitted? #GNUNET_SYSERR if there - * was an error transmitting the message, #GNUNET_NO if it was not - * yet transmitted ever, otherwise the number of (re) transmissions. - */ - int num_transmissions; - -}; - - -/** - * List of received out-of-order data messages. - */ -struct CadetOutOfOrderMessage -{ - /** - * Double linked list, FIFO style - */ - struct CadetOutOfOrderMessage *next; - - /** - * Double linked list, FIFO style - */ - struct CadetOutOfOrderMessage *prev; - - /** - * ID of the message (messages up to this point needed - * before we give this one to the client). - */ - struct ChannelMessageIdentifier mid; - - /** - * The envelope with the payload of the out-of-order message - */ - struct GNUNET_MQ_Envelope *env; - -}; - - -/** - * Client endpoint of a `struct CadetChannel`. A channel may be a - * loopback channel, in which case it has two of these endpoints. - * Note that flow control also is required in both directions. - */ -struct CadetChannelClient -{ - /** - * Client handle. Not by itself sufficient to designate - * the client endpoint, as the same client handle may - * be used for both the owner and the destination, and - * we thus also need the channel ID to identify the client. - */ - struct CadetClient *c; - - /** - * Head of DLL of messages received out of order or while client was unready. - */ - struct CadetOutOfOrderMessage *head_recv; - - /** - * Tail DLL of messages received out of order or while client was unready. - */ - struct CadetOutOfOrderMessage *tail_recv; - - /** - * Local tunnel number for this client. - * (if owner >= #GNUNET_CADET_LOCAL_CHANNEL_ID_CLI, - * otherwise < #GNUNET_CADET_LOCAL_CHANNEL_ID_CLI) - */ - struct GNUNET_CADET_ClientChannelNumber ccn; - - /** - * Number of entries currently in @a head_recv DLL. - */ - unsigned int num_recv; - - /** - * Can we send data to the client? - */ - int client_ready; - -}; - - -/** - * Struct containing all information regarding a channel to a remote client. - */ -struct CadetChannel -{ - /** - * Tunnel this channel is in. - */ - struct CadetTunnel *t; - - /** - * Client owner of the tunnel, if any. - * (Used if this channel represends the initiating end of the tunnel.) - */ - struct CadetChannelClient *owner; - - /** - * Client destination of the tunnel, if any. - * (Used if this channel represents the listening end of the tunnel.) - */ - struct CadetChannelClient *dest; - - /** - * Last entry in the tunnel's queue relating to control messages - * (#GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN or - * #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK). Used to cancel - * transmission in case we receive updated information. - */ - struct CadetTunnelQueueEntry *last_control_qe; - - /** - * Head of DLL of messages sent and not yet ACK'd. - */ - struct CadetReliableMessage *head_sent; - - /** - * Tail of DLL of messages sent and not yet ACK'd. - */ - struct CadetReliableMessage *tail_sent; - - /** - * Task to resend/poll in case no ACK is received. - */ - struct GNUNET_SCHEDULER_Task *retry_control_task; - - /** - * Task to resend/poll in case no ACK is received. - */ - struct GNUNET_SCHEDULER_Task *retry_data_task; - - /** - * Last time the channel was used - */ - struct GNUNET_TIME_Absolute timestamp; - - /** - * Destination port of the channel. - */ - struct GNUNET_HashCode port; - - /** - * Counter for exponential backoff. - */ - struct GNUNET_TIME_Relative retry_time; - - /** - * Bitfield of already-received messages past @e mid_recv. - */ - uint64_t mid_futures; - - /** - * Next MID expected for incoming traffic. - */ - struct ChannelMessageIdentifier mid_recv; - - /** - * Next MID to use for outgoing traffic. - */ - struct ChannelMessageIdentifier mid_send; - - /** - * Total (reliable) messages pending ACK for this channel. - */ - unsigned int pending_messages; - - /** - * Maximum (reliable) messages pending ACK for this channel - * before we throttle the client. - */ - unsigned int max_pending_messages; - - /** - * Number identifying this channel in its tunnel. - */ - struct GNUNET_CADET_ChannelTunnelNumber ctn; - - /** - * Channel state. - */ - enum CadetChannelState state; - - /** - * Count how many ACKs we skipped, used to prevent long - * sequences of ACK skipping. - */ - unsigned int skip_ack_series; - - /** - * Is the tunnel bufferless (minimum latency)? - */ - int nobuffer; - - /** - * Is the tunnel reliable? - */ - int reliable; - - /** - * Is the tunnel out-of-order? - */ - int out_of_order; - - /** - * Is this channel a loopback channel, where the destination is us again? - */ - int is_loopback; - - /** - * Flag to signal the destruction of the channel. If this is set to - * #GNUNET_YES the channel will be destroyed once the queue is - * empty. - */ - int destroy; - -}; - - -/** - * Get the static string for identification of the channel. - * - * @param ch Channel. - * - * @return Static string with the channel IDs. - */ -const char * -GCCH_2s (const struct CadetChannel *ch) -{ - static char buf[128]; - - GNUNET_snprintf (buf, - sizeof (buf), - "Channel %s:%s ctn:%X(%X/%X)", - (GNUNET_YES == ch->is_loopback) - ? "loopback" - : GNUNET_i2s (GCP_get_id (GCT_get_destination (ch->t))), - GNUNET_h2s (&ch->port), - ch->ctn, - (NULL == ch->owner) ? 0 : ntohl (ch->owner->ccn.channel_of_client), - (NULL == ch->dest) ? 0 : ntohl (ch->dest->ccn.channel_of_client)); - return buf; -} - - -/** - * Get the channel's public ID. - * - * @param ch Channel. - * - * @return ID used to identify the channel with the remote peer. - */ -struct GNUNET_CADET_ChannelTunnelNumber -GCCH_get_id (const struct CadetChannel *ch) -{ - return ch->ctn; -} - - -/** - * Release memory associated with @a ccc - * - * @param ccc data structure to clean up - */ -static void -free_channel_client (struct CadetChannelClient *ccc) -{ - struct CadetOutOfOrderMessage *com; - - while (NULL != (com = ccc->head_recv)) - { - GNUNET_CONTAINER_DLL_remove (ccc->head_recv, - ccc->tail_recv, - com); - ccc->num_recv--; - GNUNET_MQ_discard (com->env); - GNUNET_free (com); - } - GNUNET_free (ccc); -} - - -/** - * Destroy the given channel. - * - * @param ch channel to destroy - */ -static void -channel_destroy (struct CadetChannel *ch) -{ - struct CadetReliableMessage *crm; - - while (NULL != (crm = ch->head_sent)) - { - GNUNET_assert (ch == crm->ch); - if (NULL != crm->qe) - { - GCT_send_cancel (crm->qe); - crm->qe = NULL; - } - GNUNET_CONTAINER_DLL_remove (ch->head_sent, - ch->tail_sent, - crm); - GNUNET_free (crm->data_message); - GNUNET_free (crm); - } - if (NULL != ch->owner) - { - free_channel_client (ch->owner); - ch->owner = NULL; - } - if (NULL != ch->dest) - { - free_channel_client (ch->dest); - ch->dest = NULL; - } - if (NULL != ch->last_control_qe) - { - GCT_send_cancel (ch->last_control_qe); - ch->last_control_qe = NULL; - } - if (NULL != ch->retry_data_task) - { - GNUNET_SCHEDULER_cancel (ch->retry_data_task); - ch->retry_data_task = NULL; - } - if (NULL != ch->retry_control_task) - { - GNUNET_SCHEDULER_cancel (ch->retry_control_task); - ch->retry_control_task = NULL; - } - if (GNUNET_NO == ch->is_loopback) - { - GCT_remove_channel (ch->t, - ch, - ch->ctn); - ch->t = NULL; - } - GNUNET_free (ch); -} - - -/** - * Send a channel create message. - * - * @param cls Channel for which to send. - */ -static void -send_channel_open (void *cls); - - -/** - * Function called once the tunnel confirms that we sent the - * create message. Delays for a bit until we retry. - * - * @param cls our `struct CadetChannel`. - * @param cid identifier of the connection within the tunnel, NULL - * if transmission failed - */ -static void -channel_open_sent_cb (void *cls, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) -{ - struct CadetChannel *ch = cls; - - GNUNET_assert (NULL != ch->last_control_qe); - ch->last_control_qe = NULL; - ch->retry_time = GNUNET_TIME_STD_BACKOFF (ch->retry_time); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sent CADET_CHANNEL_OPEN on %s, retrying in %s\n", - GCCH_2s (ch), - GNUNET_STRINGS_relative_time_to_string (ch->retry_time, - GNUNET_YES)); - ch->retry_control_task - = GNUNET_SCHEDULER_add_delayed (ch->retry_time, - &send_channel_open, - ch); -} - - -/** - * Send a channel open message. - * - * @param cls Channel for which to send. - */ -static void -send_channel_open (void *cls) -{ - struct CadetChannel *ch = cls; - struct GNUNET_CADET_ChannelOpenMessage msgcc; - uint32_t options; - - ch->retry_control_task = NULL; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending CHANNEL_OPEN message for %s\n", - GCCH_2s (ch)); - options = 0; - if (ch->nobuffer) - options |= GNUNET_CADET_OPTION_NOBUFFER; - if (ch->reliable) - options |= GNUNET_CADET_OPTION_RELIABLE; - if (ch->out_of_order) - options |= GNUNET_CADET_OPTION_OUT_OF_ORDER; - msgcc.header.size = htons (sizeof (msgcc)); - msgcc.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN); - msgcc.opt = htonl (options); - msgcc.port = ch->port; - msgcc.ctn = ch->ctn; - ch->state = CADET_CHANNEL_OPEN_SENT; - if (NULL != ch->last_control_qe) - GCT_send_cancel (ch->last_control_qe); - ch->last_control_qe = GCT_send (ch->t, - &msgcc.header, - &channel_open_sent_cb, - ch); - GNUNET_assert (NULL == ch->retry_control_task); -} - - -/** - * Function called once and only once after a channel was bound - * to its tunnel via #GCT_add_channel() is ready for transmission. - * Note that this is only the case for channels that this peer - * initiates, as for incoming channels we assume that they are - * ready for transmission immediately upon receiving the open - * message. Used to bootstrap the #GCT_send() process. - * - * @param ch the channel for which the tunnel is now ready - */ -void -GCCH_tunnel_up (struct CadetChannel *ch) -{ - GNUNET_assert (NULL == ch->retry_control_task); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Tunnel up, sending CHANNEL_OPEN on %s now\n", - GCCH_2s (ch)); - ch->retry_control_task - = GNUNET_SCHEDULER_add_now (&send_channel_open, - ch); -} - - -/** - * Create a new channel. - * - * @param owner local client owning the channel - * @param ccn local number of this channel at the @a owner - * @param destination peer to which we should build the channel - * @param port desired port at @a destination - * @param options options for the channel - * @return handle to the new channel - */ -struct CadetChannel * -GCCH_channel_local_new (struct CadetClient *owner, - struct GNUNET_CADET_ClientChannelNumber ccn, - struct CadetPeer *destination, - const struct GNUNET_HashCode *port, - uint32_t options) -{ - struct CadetChannel *ch; - struct CadetChannelClient *ccco; - - ccco = GNUNET_new (struct CadetChannelClient); - ccco->c = owner; - ccco->ccn = ccn; - ccco->client_ready = GNUNET_YES; - - ch = GNUNET_new (struct CadetChannel); - ch->mid_recv.mid = htonl (1); /* The OPEN_ACK counts as message 0! */ - ch->nobuffer = (0 != (options & GNUNET_CADET_OPTION_NOBUFFER)); - ch->reliable = (0 != (options & GNUNET_CADET_OPTION_RELIABLE)); - ch->out_of_order = (0 != (options & GNUNET_CADET_OPTION_OUT_OF_ORDER)); - ch->max_pending_messages = (ch->nobuffer) ? 1 : 4; /* FIXME: 4!? Do not hardcode! */ - ch->owner = ccco; - ch->port = *port; - if (0 == memcmp (&my_full_id, - GCP_get_id (destination), - sizeof (struct GNUNET_PeerIdentity))) - { - struct CadetClient *c; - - ch->is_loopback = GNUNET_YES; - c = GNUNET_CONTAINER_multihashmap_get (open_ports, - port); - if (NULL == c) - { - /* port closed, wait for it to possibly open */ - ch->state = CADET_CHANNEL_LOOSE; - (void) GNUNET_CONTAINER_multihashmap_put (loose_channels, - port, - ch, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Created loose incoming loopback channel to port %s\n", - GNUNET_h2s (&ch->port)); - } - else - { - GCCH_bind (ch, - c); - } - } - else - { - ch->t = GCP_get_tunnel (destination, - GNUNET_YES); - ch->retry_time = CADET_INITIAL_RETRANSMIT_TIME; - ch->ctn = GCT_add_channel (ch->t, - ch); - } - GNUNET_STATISTICS_update (stats, - "# channels", - 1, - GNUNET_NO); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Created channel to port %s at peer %s for %s using %s\n", - GNUNET_h2s (port), - GCP_2s (destination), - GSC_2s (owner), - (GNUNET_YES == ch->is_loopback) ? "loopback" : GCT_2s (ch->t)); - return ch; -} - - -/** - * We had an incoming channel to a port that is closed. - * It has not been opened for a while, drop it. - * - * @param cls the channel to drop - */ -static void -timeout_closed_cb (void *cls) -{ - struct CadetChannel *ch = cls; - - ch->retry_control_task = NULL; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Closing incoming channel to port %s from peer %s due to timeout\n", - GNUNET_h2s (&ch->port), - GCP_2s (GCT_get_destination (ch->t))); - channel_destroy (ch); -} - - -/** - * Create a new channel based on a request coming in over the network. - * - * @param t tunnel to the remote peer - * @param ctn identifier of this channel in the tunnel - * @param port desired local port - * @param options options for the channel - * @return handle to the new channel - */ -struct CadetChannel * -GCCH_channel_incoming_new (struct CadetTunnel *t, - struct GNUNET_CADET_ChannelTunnelNumber ctn, - const struct GNUNET_HashCode *port, - uint32_t options) -{ - struct CadetChannel *ch; - struct CadetClient *c; - - ch = GNUNET_new (struct CadetChannel); - ch->port = *port; - ch->t = t; - ch->ctn = ctn; - ch->retry_time = CADET_INITIAL_RETRANSMIT_TIME; - ch->nobuffer = (0 != (options & GNUNET_CADET_OPTION_NOBUFFER)); - ch->reliable = (0 != (options & GNUNET_CADET_OPTION_RELIABLE)); - ch->out_of_order = (0 != (options & GNUNET_CADET_OPTION_OUT_OF_ORDER)); - ch->max_pending_messages = (ch->nobuffer) ? 1 : 4; /* FIXME: 4!? Do not hardcode! */ - GNUNET_STATISTICS_update (stats, - "# channels", - 1, - GNUNET_NO); - - c = GNUNET_CONTAINER_multihashmap_get (open_ports, - port); - if (NULL == c) - { - /* port closed, wait for it to possibly open */ - ch->state = CADET_CHANNEL_LOOSE; - (void) GNUNET_CONTAINER_multihashmap_put (loose_channels, - port, - ch, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); - GNUNET_assert (NULL == ch->retry_control_task); - ch->retry_control_task - = GNUNET_SCHEDULER_add_delayed (TIMEOUT_CLOSED_PORT, - &timeout_closed_cb, - ch); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Created loose incoming channel to port %s from peer %s\n", - GNUNET_h2s (&ch->port), - GCP_2s (GCT_get_destination (ch->t))); - } - else - { - GCCH_bind (ch, - c); - } - GNUNET_STATISTICS_update (stats, - "# channels", - 1, - GNUNET_NO); - return ch; -} - - -/** - * Function called once the tunnel confirms that we sent the - * ACK message. Just remembers it was sent, we do not expect - * ACKs for ACKs ;-). - * - * @param cls our `struct CadetChannel`. - * @param cid identifier of the connection within the tunnel, NULL - * if transmission failed - */ -static void -send_ack_cb (void *cls, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) -{ - struct CadetChannel *ch = cls; - - GNUNET_assert (NULL != ch->last_control_qe); - ch->last_control_qe = NULL; -} - - -/** - * Compute and send the current #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK to the other peer. - * - * @param ch channel to send the #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK for - */ -static void -send_channel_data_ack (struct CadetChannel *ch) -{ - struct GNUNET_CADET_ChannelDataAckMessage msg; - - if (GNUNET_NO == ch->reliable) - return; /* no ACKs */ - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK); - msg.header.size = htons (sizeof (msg)); - msg.ctn = ch->ctn; - msg.mid.mid = htonl (ntohl (ch->mid_recv.mid)); - msg.futures = GNUNET_htonll (ch->mid_futures); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending DATA_ACK %u:%llX via %s\n", - (unsigned int) ntohl (msg.mid.mid), - (unsigned long long) ch->mid_futures, - GCCH_2s (ch)); - if (NULL != ch->last_control_qe) - GCT_send_cancel (ch->last_control_qe); - ch->last_control_qe = GCT_send (ch->t, - &msg.header, - &send_ack_cb, - ch); -} - - -/** - * Send our initial #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK to the client confirming that the - * connection is up. - * - * @param cls the `struct CadetChannel` - */ -static void -send_open_ack (void *cls) -{ - struct CadetChannel *ch = cls; - struct GNUNET_CADET_ChannelManageMessage msg; - - ch->retry_control_task = NULL; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending CHANNEL_OPEN_ACK on %s\n", - GCCH_2s (ch)); - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK); - msg.header.size = htons (sizeof (msg)); - msg.reserved = htonl (0); - msg.ctn = ch->ctn; - if (NULL != ch->last_control_qe) - GCT_send_cancel (ch->last_control_qe); - ch->last_control_qe = GCT_send (ch->t, - &msg.header, - &send_ack_cb, - ch); -} - - -/** - * We got a #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN message again for - * this channel. If the binding was successful, (re)transmit the - * #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK. - * - * @param ch channel that got the duplicate open - * @param cti identifier of the connection that delivered the message - */ -void -GCCH_handle_duplicate_open (struct CadetChannel *ch, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti) -{ - if (NULL == ch->dest) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Ignoring duplicate CHANNEL_OPEN on %s: port is closed\n", - GCCH_2s (ch)); - return; - } - if (NULL != ch->retry_control_task) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Ignoring duplicate CHANNEL_OPEN on %s: control message is pending\n", - GCCH_2s (ch)); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Retransmitting CHANNEL_OPEN_ACK on %s\n", - GCCH_2s (ch)); - ch->retry_control_task - = GNUNET_SCHEDULER_add_now (&send_open_ack, - ch); -} - - -/** - * Send a #GNUNET_MESSAGE_TYPE_CADET_LOCAL_ACK to the client to solicit more messages. - * - * @param ch channel the ack is for - * @param to_owner #GNUNET_YES to send to owner, - * #GNUNET_NO to send to dest - */ -static void -send_ack_to_client (struct CadetChannel *ch, - int to_owner) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_CADET_LocalAck *ack; - struct CadetChannelClient *ccc; - - ccc = (GNUNET_YES == to_owner) ? ch->owner : ch->dest; - if (NULL == ccc) - { - /* This can happen if we are just getting ACKs after - our local client already disconnected. */ - GNUNET_assert (GNUNET_YES == ch->destroy); - return; - } - env = GNUNET_MQ_msg (ack, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_ACK); - ack->ccn = ccc->ccn; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending CADET_LOCAL_ACK to %s (%s) at ccn %X (%u/%u pending)\n", - GSC_2s (ccc->c), - (GNUNET_YES == to_owner) ? "owner" : "dest", - ntohl (ack->ccn.channel_of_client), - ch->pending_messages, - ch->max_pending_messages); - GSC_send_to_client (ccc->c, - env); -} - - -/** - * A client is bound to the port that we have a channel - * open to. Send the acknowledgement for the connection - * request and establish the link with the client. - * - * @param ch open incoming channel - * @param c client listening on the respective port - */ -void -GCCH_bind (struct CadetChannel *ch, - struct CadetClient *c) -{ - uint32_t options; - struct CadetChannelClient *cccd; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Binding %s from %s to port %s of %s\n", - GCCH_2s (ch), - GCT_2s (ch->t), - GNUNET_h2s (&ch->port), - GSC_2s (c)); - if (NULL != ch->retry_control_task) - { - /* there might be a timeout task here */ - GNUNET_SCHEDULER_cancel (ch->retry_control_task); - ch->retry_control_task = NULL; - } - options = 0; - if (ch->nobuffer) - options |= GNUNET_CADET_OPTION_NOBUFFER; - if (ch->reliable) - options |= GNUNET_CADET_OPTION_RELIABLE; - if (ch->out_of_order) - options |= GNUNET_CADET_OPTION_OUT_OF_ORDER; - cccd = GNUNET_new (struct CadetChannelClient); - GNUNET_assert (NULL == ch->dest); - ch->dest = cccd; - cccd->c = c; - cccd->client_ready = GNUNET_YES; - cccd->ccn = GSC_bind (c, - ch, - (GNUNET_YES == ch->is_loopback) - ? GCP_get (&my_full_id, - GNUNET_YES) - : GCT_get_destination (ch->t), - &ch->port, - options); - GNUNET_assert (ntohl (cccd->ccn.channel_of_client) < - GNUNET_CADET_LOCAL_CHANNEL_ID_CLI); - ch->mid_recv.mid = htonl (1); /* The OPEN counts as message 0! */ - if (GNUNET_YES == ch->is_loopback) - { - ch->state = CADET_CHANNEL_OPEN_SENT; - GCCH_handle_channel_open_ack (ch, - NULL); - } - else - { - /* notify other peer that we accepted the connection */ - ch->state = CADET_CHANNEL_READY; - ch->retry_control_task - = GNUNET_SCHEDULER_add_now (&send_open_ack, - ch); - } - /* give client it's initial supply of ACKs */ - GNUNET_assert (ntohl (cccd->ccn.channel_of_client) < - GNUNET_CADET_LOCAL_CHANNEL_ID_CLI); - for (unsigned int i=0;i<ch->max_pending_messages;i++) - send_ack_to_client (ch, - GNUNET_NO); -} - - -/** - * One of our clients has disconnected, tell the other one that we - * are finished. Done asynchronously to avoid concurrent modification - * issues if this is the same client. - * - * @param cls the `struct CadetChannel` where one of the ends is now dead - */ -static void -signal_remote_destroy_cb (void *cls) -{ - struct CadetChannel *ch = cls; - struct CadetChannelClient *ccc; - - /* Find which end is left... */ - ch->retry_control_task = NULL; - ccc = (NULL != ch->owner) ? ch->owner : ch->dest; - GSC_handle_remote_channel_destroy (ccc->c, - ccc->ccn, - ch); - channel_destroy (ch); -} - - -/** - * Destroy locally created channel. Called by the local client, so no - * need to tell the client. - * - * @param ch channel to destroy - * @param c client that caused the destruction - * @param ccn client number of the client @a c - */ -void -GCCH_channel_local_destroy (struct CadetChannel *ch, - struct CadetClient *c, - struct GNUNET_CADET_ClientChannelNumber ccn) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "%s asks for destruction of %s\n", - GSC_2s (c), - GCCH_2s (ch)); - GNUNET_assert (NULL != c); - if ( (NULL != ch->owner) && - (c == ch->owner->c) && - (ccn.channel_of_client == ch->owner->ccn.channel_of_client) ) - { - free_channel_client (ch->owner); - ch->owner = NULL; - } - else if ( (NULL != ch->dest) && - (c == ch->dest->c) && - (ccn.channel_of_client == ch->dest->ccn.channel_of_client) ) - { - free_channel_client (ch->dest); - ch->dest = NULL; - } - else - { - GNUNET_assert (0); - } - - if (GNUNET_YES == ch->destroy) - { - /* other end already destroyed, with the local client gone, no need - to finish transmissions, just destroy immediately. */ - channel_destroy (ch); - return; - } - if ( (NULL != ch->head_sent) && - ( (NULL != ch->owner) || - (NULL != ch->dest) ) ) - { - /* Wait for other end to destroy us as well, - and otherwise allow send queue to be transmitted first */ - ch->destroy = GNUNET_YES; - return; - } - if ( (GNUNET_YES == ch->is_loopback) && - ( (NULL != ch->owner) || - (NULL != ch->dest) ) ) - { - if (NULL != ch->retry_control_task) - GNUNET_SCHEDULER_cancel (ch->retry_control_task); - ch->retry_control_task - = GNUNET_SCHEDULER_add_now (&signal_remote_destroy_cb, - ch); - return; - } - if (GNUNET_NO == ch->is_loopback) - { - /* If the we ever sent the CHANNEL_CREATE, we need to send a destroy message. */ - switch (ch->state) - { - case CADET_CHANNEL_NEW: - /* We gave up on a channel that we created as a client to a remote - target, but that never went anywhere. Nothing to do here. */ - break; - case CADET_CHANNEL_LOOSE: - GSC_drop_loose_channel (&ch->port, - ch); - break; - default: - GCT_send_channel_destroy (ch->t, - ch->ctn); - } - } - /* Nothing left to do, just finish destruction */ - channel_destroy (ch); -} - - -/** - * We got an acknowledgement for the creation of the channel - * (the port is open on the other side). Begin transmissions. - * - * @param ch channel to destroy - * @param cti identifier of the connection that delivered the message - */ -void -GCCH_handle_channel_open_ack (struct CadetChannel *ch, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti) -{ - switch (ch->state) - { - case CADET_CHANNEL_NEW: - /* this should be impossible */ - GNUNET_break (0); - break; - case CADET_CHANNEL_LOOSE: - /* This makes no sense. */ - GNUNET_break_op (0); - break; - case CADET_CHANNEL_OPEN_SENT: - if (NULL == ch->owner) - { - /* We're not the owner, wrong direction! */ - GNUNET_break_op (0); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received CHANNEL_OPEN_ACK for waiting %s, entering READY state\n", - GCCH_2s (ch)); - if (NULL != ch->retry_control_task) /* can be NULL if ch->is_loopback */ - { - GNUNET_SCHEDULER_cancel (ch->retry_control_task); - ch->retry_control_task = NULL; - } - ch->state = CADET_CHANNEL_READY; - /* On first connect, send client as many ACKs as we allow messages - to be buffered! */ - for (unsigned int i=0;i<ch->max_pending_messages;i++) - send_ack_to_client (ch, - GNUNET_YES); - break; - case CADET_CHANNEL_READY: - /* duplicate ACK, maybe we retried the CREATE. Ignore. */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received duplicate channel OPEN_ACK for %s\n", - GCCH_2s (ch)); - GNUNET_STATISTICS_update (stats, - "# duplicate CREATE_ACKs", - 1, - GNUNET_NO); - break; - } -} - - -/** - * Test if element @a e1 comes before element @a e2. - * - * @param cls closure, to a flag where we indicate duplicate packets - * @param m1 a message of to sort - * @param m2 another message to sort - * @return #GNUNET_YES if @e1 < @e2, otherwise #GNUNET_NO - */ -static int -is_before (void *cls, - struct CadetOutOfOrderMessage *m1, - struct CadetOutOfOrderMessage *m2) -{ - int *duplicate = cls; - uint32_t v1 = ntohl (m1->mid.mid); - uint32_t v2 = ntohl (m2->mid.mid); - uint32_t delta; - - delta = v2 - v1; - if (0 == delta) - *duplicate = GNUNET_YES; - if (delta > (uint32_t) INT_MAX) - { - /* in overflow range, we can safely assume we wrapped around */ - return GNUNET_NO; - } - else - { - /* result is small, thus v2 > v1, thus m1 < m2 */ - return GNUNET_YES; - } -} - - -/** - * We got payload data for a channel. Pass it on to the client - * and send an ACK to the other end (once flow control allows it!) - * - * @param ch channel that got data - * @param cti identifier of the connection that delivered the message - * @param msg message that was received - */ -void -GCCH_handle_channel_plaintext_data (struct CadetChannel *ch, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti, - const struct GNUNET_CADET_ChannelAppDataMessage *msg) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_CADET_LocalData *ld; - struct CadetChannelClient *ccc; - size_t payload_size; - struct CadetOutOfOrderMessage *com; - int duplicate; - uint32_t mid_min; - uint32_t mid_max; - uint32_t mid_msg; - uint32_t delta; - - GNUNET_assert (GNUNET_NO == ch->is_loopback); - if ( (GNUNET_YES == ch->destroy) && - (NULL == ch->owner) && - (NULL == ch->dest) ) - { - /* This client is gone, but we still have messages to send to - the other end (which is why @a ch is not yet dead). However, - we cannot pass messages to our client anymore. */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Dropping incoming payload on %s as this end is already closed\n", - GCCH_2s (ch)); - /* send back DESTROY notification to stop further retransmissions! */ - GCT_send_channel_destroy (ch->t, - ch->ctn); - return; - } - payload_size = ntohs (msg->header.size) - sizeof (*msg); - env = GNUNET_MQ_msg_extra (ld, - payload_size, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA); - ld->ccn = (NULL == ch->dest) ? ch->owner->ccn : ch->dest->ccn; - GNUNET_memcpy (&ld[1], - &msg[1], - payload_size); - ccc = (NULL != ch->owner) ? ch->owner : ch->dest; - if ( (GNUNET_YES == ccc->client_ready) && - ( (GNUNET_YES == ch->out_of_order) || - (msg->mid.mid == ch->mid_recv.mid) ) ) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Giving %u bytes of payload with MID %u from %s to client %s\n", - (unsigned int) payload_size, - ntohl (msg->mid.mid), - GCCH_2s (ch), - GSC_2s (ccc->c)); - ccc->client_ready = GNUNET_NO; - GSC_send_to_client (ccc->c, - env); - ch->mid_recv.mid = htonl (1 + ntohl (ch->mid_recv.mid)); - ch->mid_futures >>= 1; - send_channel_data_ack (ch); - return; - } - - if (GNUNET_YES == ch->reliable) - { - /* check if message ought to be dropped because it is ancient/too distant/duplicate */ - mid_min = ntohl (ch->mid_recv.mid); - mid_max = mid_min + ch->max_pending_messages; - mid_msg = ntohl (msg->mid.mid); - if ( ( (uint32_t) (mid_msg - mid_min) > ch->max_pending_messages) || - ( (uint32_t) (mid_max - mid_msg) > ch->max_pending_messages) ) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "%s at %u drops ancient or far-future message %u\n", - GCCH_2s (ch), - (unsigned int) mid_min, - ntohl (msg->mid.mid)); - - GNUNET_STATISTICS_update (stats, - "# duplicate DATA (ancient or future)", - 1, - GNUNET_NO); - GNUNET_MQ_discard (env); - send_channel_data_ack (ch); - return; - } - /* mark bit for future ACKs */ - delta = mid_msg - mid_min - 1; /* overflow/underflow are OK here */ - if (delta < 64) - { - if (0 != (ch->mid_futures & (1LLU << delta))) - { - /* Duplicate within the queue, drop also */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Duplicate payload of %u bytes on %s (mid %u) dropped\n", - (unsigned int) payload_size, - GCCH_2s (ch), - ntohl (msg->mid.mid)); - GNUNET_STATISTICS_update (stats, - "# duplicate DATA", - 1, - GNUNET_NO); - GNUNET_MQ_discard (env); - send_channel_data_ack (ch); - return; - } - ch->mid_futures |= (1LLU << delta); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Marked bit %llX for mid %u (base: %u); now: %llX\n", - (1LLU << delta), - mid_msg, - mid_min, - ch->mid_futures); - } - } - else /* ! ch->reliable */ - { - /* Channel is unreliable, so we do not ACK. But we also cannot - allow buffering everything, so check if we have space... */ - if (ccc->num_recv >= ch->max_pending_messages) - { - struct CadetOutOfOrderMessage *drop; - - /* Yep, need to drop. Drop the oldest message in - the buffer. */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Queue full due slow client on %s, dropping oldest message\n", - GCCH_2s (ch)); - GNUNET_STATISTICS_update (stats, - "# messages dropped due to slow client", - 1, - GNUNET_NO); - drop = ccc->head_recv; - GNUNET_CONTAINER_DLL_remove (ccc->head_recv, - ccc->tail_recv, - drop); - ccc->num_recv--; - GNUNET_MQ_discard (drop->env); - GNUNET_free (drop); - } - } - - /* Insert message into sorted out-of-order queue */ - com = GNUNET_new (struct CadetOutOfOrderMessage); - com->mid = msg->mid; - com->env = env; - duplicate = GNUNET_NO; - GNUNET_CONTAINER_DLL_insert_sorted (struct CadetOutOfOrderMessage, - is_before, - &duplicate, - ccc->head_recv, - ccc->tail_recv, - com); - ccc->num_recv++; - if (GNUNET_YES == duplicate) - { - /* Duplicate within the queue, drop also (this is not covered by - the case above if "delta" >= 64, which could be the case if - max_pending_messages is also >= 64 or if our client is unready - and we are seeing retransmissions of the message our client is - blocked on. */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Duplicate payload of %u bytes on %s (mid %u) dropped\n", - (unsigned int) payload_size, - GCCH_2s (ch), - ntohl (msg->mid.mid)); - GNUNET_STATISTICS_update (stats, - "# duplicate DATA", - 1, - GNUNET_NO); - GNUNET_CONTAINER_DLL_remove (ccc->head_recv, - ccc->tail_recv, - com); - ccc->num_recv--; - GNUNET_MQ_discard (com->env); - GNUNET_free (com); - send_channel_data_ack (ch); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Queued %s payload of %u bytes on %s-%X(%p) (mid %u, need %u first)\n", - (GNUNET_YES == ccc->client_ready) - ? "out-of-order" - : "client-not-ready", - (unsigned int) payload_size, - GCCH_2s (ch), - ntohl (ccc->ccn.channel_of_client), - ccc, - ntohl (msg->mid.mid), - ntohl (ch->mid_recv.mid)); - /* NOTE: this ACK we _could_ skip, as the packet is out-of-order and - the sender may already be transmitting the previous one. Needs - experimental evaluation to see if/when this ACK helps or - hurts. (We might even want another option.) */ - send_channel_data_ack (ch); -} - - -/** - * Function called once the tunnel has sent one of our messages. - * If the message is unreliable, simply frees the `crm`. If the - * message was reliable, calculate retransmission time and - * wait for ACK (or retransmit). - * - * @param cls the `struct CadetReliableMessage` that was sent - * @param cid identifier of the connection within the tunnel, NULL - * if transmission failed - */ -static void -data_sent_cb (void *cls, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid); - - -/** - * We need to retry a transmission, the last one took too long to - * be acknowledged. - * - * @param cls the `struct CadetChannel` where we need to retransmit - */ -static void -retry_transmission (void *cls) -{ - struct CadetChannel *ch = cls; - struct CadetReliableMessage *crm = ch->head_sent; - - ch->retry_data_task = NULL; - GNUNET_assert (NULL == crm->qe); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Retrying transmission on %s of message %u\n", - GCCH_2s (ch), - (unsigned int) ntohl (crm->data_message->mid.mid)); - crm->qe = GCT_send (ch->t, - &crm->data_message->header, - &data_sent_cb, - crm); - GNUNET_assert (NULL == ch->retry_data_task); -} - - -/** - * We got an PLAINTEXT_DATA_ACK for a message in our queue, remove it from - * the queue and tell our client that it can send more. - * - * @param ch the channel that got the PLAINTEXT_DATA_ACK - * @param cti identifier of the connection that delivered the message - * @param crm the message that got acknowledged - */ -static void -handle_matching_ack (struct CadetChannel *ch, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti, - struct CadetReliableMessage *crm) -{ - GNUNET_CONTAINER_DLL_remove (ch->head_sent, - ch->tail_sent, - crm); - ch->pending_messages--; - GNUNET_assert (ch->pending_messages < ch->max_pending_messages); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received DATA_ACK on %s for message %u (%u ACKs pending)\n", - GCCH_2s (ch), - (unsigned int) ntohl (crm->data_message->mid.mid), - ch->pending_messages); - if (NULL != crm->qe) - { - GCT_send_cancel (crm->qe); - crm->qe = NULL; - } - if ( (1 == crm->num_transmissions) && - (NULL != cti) ) - { - GCC_ack_observed (cti); - if (0 == memcmp (cti, - &crm->connection_taken, - sizeof (struct GNUNET_CADET_ConnectionTunnelIdentifier))) - { - GCC_latency_observed (cti, - GNUNET_TIME_absolute_get_duration (crm->first_transmission_time)); - } - } - GNUNET_free (crm->data_message); - GNUNET_free (crm); - send_ack_to_client (ch, - (NULL == ch->owner) - ? GNUNET_NO - : GNUNET_YES); -} - - -/** - * We got an acknowledgement for payload data for a channel. - * Possibly resume transmissions. - * - * @param ch channel that got the ack - * @param cti identifier of the connection that delivered the message - * @param ack details about what was received - */ -void -GCCH_handle_channel_plaintext_data_ack (struct CadetChannel *ch, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti, - const struct GNUNET_CADET_ChannelDataAckMessage *ack) -{ - struct CadetReliableMessage *crm; - struct CadetReliableMessage *crmn; - int found; - uint32_t mid_base; - uint64_t mid_mask; - unsigned int delta; - - GNUNET_break (GNUNET_NO == ch->is_loopback); - if (GNUNET_NO == ch->reliable) - { - /* not expecting ACKs on unreliable channel, odd */ - GNUNET_break_op (0); - return; - } - /* mid_base is the MID of the next message that the - other peer expects (i.e. that is missing!), everything - LOWER (but excluding mid_base itself) was received. */ - mid_base = ntohl (ack->mid.mid); - mid_mask = GNUNET_htonll (ack->futures); - found = GNUNET_NO; - for (crm = ch->head_sent; - NULL != crm; - crm = crmn) - { - crmn = crm->next; - delta = (unsigned int) (ntohl (crm->data_message->mid.mid) - mid_base); - if (delta >= UINT_MAX - ch->max_pending_messages) - { - /* overflow, means crm was a bit in the past, so this ACK counts for it. */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got DATA_ACK with base %u satisfying past message %u on %s\n", - (unsigned int) mid_base, - ntohl (crm->data_message->mid.mid), - GCCH_2s (ch)); - handle_matching_ack (ch, - cti, - crm); - found = GNUNET_YES; - continue; - } - delta--; - if (delta >= 64) - continue; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Testing bit %llX for mid %u (base: %u)\n", - (1LLU << delta), - ntohl (crm->data_message->mid.mid), - mid_base); - if (0 != (mid_mask & (1LLU << delta))) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got DATA_ACK with mask for %u on %s\n", - ntohl (crm->data_message->mid.mid), - GCCH_2s (ch)); - handle_matching_ack (ch, - cti, - crm); - found = GNUNET_YES; - } - } - if (GNUNET_NO == found) - { - /* ACK for message we already dropped, might have been a - duplicate ACK? Ignore. */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Duplicate DATA_ACK on %s, ignoring\n", - GCCH_2s (ch)); - GNUNET_STATISTICS_update (stats, - "# duplicate DATA_ACKs", - 1, - GNUNET_NO); - return; - } - if (NULL != ch->retry_data_task) - { - GNUNET_SCHEDULER_cancel (ch->retry_data_task); - ch->retry_data_task = NULL; - } - if ( (NULL != ch->head_sent) && - (NULL == ch->head_sent->qe) ) - ch->retry_data_task - = GNUNET_SCHEDULER_add_at (ch->head_sent->next_retry, - &retry_transmission, - ch); -} - - -/** - * Destroy channel, based on the other peer closing the - * connection. Also needs to remove this channel from - * the tunnel. - * - * @param ch channel to destroy - * @param cti identifier of the connection that delivered the message, - * NULL if we are simulating receiving a destroy due to shutdown - */ -void -GCCH_handle_remote_destroy (struct CadetChannel *ch, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti) -{ - struct CadetChannelClient *ccc; - - GNUNET_assert (GNUNET_NO == ch->is_loopback); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received remote channel DESTROY for %s\n", - GCCH_2s (ch)); - if (GNUNET_YES == ch->destroy) - { - /* Local client already gone, this is instant-death. */ - channel_destroy (ch); - return; - } - ccc = (NULL != ch->owner) ? ch->owner : ch->dest; - if ( (NULL != ccc) && - (NULL != ccc->head_recv) ) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - "Lost end of transmission due to remote shutdown on %s\n", - GCCH_2s (ch)); - /* FIXME: change API to notify client about truncated transmission! */ - } - ch->destroy = GNUNET_YES; - if (NULL != ccc) - GSC_handle_remote_channel_destroy (ccc->c, - ccc->ccn, - ch); - channel_destroy (ch); -} - - -/** - * Test if element @a e1 comes before element @a e2. - * - * @param cls closure, to a flag where we indicate duplicate packets - * @param crm1 an element of to sort - * @param crm2 another element to sort - * @return #GNUNET_YES if @e1 < @e2, otherwise #GNUNET_NO - */ -static int -cmp_crm_by_next_retry (void *cls, - struct CadetReliableMessage *crm1, - struct CadetReliableMessage *crm2) -{ - if (crm1->next_retry.abs_value_us < - crm2->next_retry.abs_value_us) - return GNUNET_YES; - return GNUNET_NO; -} - - -/** - * Function called once the tunnel has sent one of our messages. - * If the message is unreliable, simply frees the `crm`. If the - * message was reliable, calculate retransmission time and - * wait for ACK (or retransmit). - * - * @param cls the `struct CadetReliableMessage` that was sent - * @param cid identifier of the connection within the tunnel, NULL - * if transmission failed - */ -static void -data_sent_cb (void *cls, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) -{ - struct CadetReliableMessage *crm = cls; - struct CadetChannel *ch = crm->ch; - - GNUNET_assert (GNUNET_NO == ch->is_loopback); - GNUNET_assert (NULL != crm->qe); - crm->qe = NULL; - GNUNET_CONTAINER_DLL_remove (ch->head_sent, - ch->tail_sent, - crm); - if (GNUNET_NO == ch->reliable) - { - GNUNET_free (crm->data_message); - GNUNET_free (crm); - ch->pending_messages--; - send_ack_to_client (ch, - (NULL == ch->owner) - ? GNUNET_NO - : GNUNET_YES); - return; - } - if (NULL == cid) - { - /* There was an error sending. */ - crm->num_transmissions = GNUNET_SYSERR; - } - else if (GNUNET_SYSERR != crm->num_transmissions) - { - /* Increment transmission counter, and possibly store @a cid - if this was the first transmission. */ - crm->num_transmissions++; - if (1 == crm->num_transmissions) - { - crm->first_transmission_time = GNUNET_TIME_absolute_get (); - crm->connection_taken = *cid; - GCC_ack_expected (cid); - } - } - if ( (0 == crm->retry_delay.rel_value_us) && - (NULL != cid) ) - { - struct CadetConnection *cc = GCC_lookup (cid); - - if (NULL != cc) - crm->retry_delay = GCC_get_metrics (cc)->aged_latency; - else - crm->retry_delay = ch->retry_time; - } - crm->retry_delay = GNUNET_TIME_STD_BACKOFF (crm->retry_delay); - crm->retry_delay = GNUNET_TIME_relative_max (crm->retry_delay, - MIN_RTT_DELAY); - crm->next_retry = GNUNET_TIME_relative_to_absolute (crm->retry_delay); - - GNUNET_CONTAINER_DLL_insert_sorted (struct CadetReliableMessage, - cmp_crm_by_next_retry, - NULL, - ch->head_sent, - ch->tail_sent, - crm); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Message %u sent, next transmission on %s in %s\n", - (unsigned int) ntohl (crm->data_message->mid.mid), - GCCH_2s (ch), - GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (ch->head_sent->next_retry), - GNUNET_YES)); - if (NULL == ch->head_sent->qe) - { - if (NULL != ch->retry_data_task) - GNUNET_SCHEDULER_cancel (ch->retry_data_task); - ch->retry_data_task - = GNUNET_SCHEDULER_add_at (ch->head_sent->next_retry, - &retry_transmission, - ch); - } -} - - -/** - * Handle data given by a client. - * - * Check whether the client is allowed to send in this tunnel, save if - * channel is reliable and send an ACK to the client if there is still - * buffer space in the tunnel. - * - * @param ch Channel. - * @param sender_ccn ccn of the sender - * @param buf payload to transmit. - * @param buf_len number of bytes in @a buf - * @return #GNUNET_OK if everything goes well, - * #GNUNET_SYSERR in case of an error. - */ -int -GCCH_handle_local_data (struct CadetChannel *ch, - struct GNUNET_CADET_ClientChannelNumber sender_ccn, - const char *buf, - size_t buf_len) -{ - struct CadetReliableMessage *crm; - - if (ch->pending_messages > ch->max_pending_messages) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (GNUNET_YES == ch->destroy) - { - /* we are going down, drop messages */ - return GNUNET_OK; - } - ch->pending_messages++; - - if (GNUNET_YES == ch->is_loopback) - { - struct CadetChannelClient *receiver; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_CADET_LocalData *ld; - int ack_to_owner; - - env = GNUNET_MQ_msg_extra (ld, - buf_len, - GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA); - if ( (NULL != ch->owner) && - (sender_ccn.channel_of_client == - ch->owner->ccn.channel_of_client) ) - { - receiver = ch->dest; - ack_to_owner = GNUNET_YES; - } - else if ( (NULL != ch->dest) && - (sender_ccn.channel_of_client == - ch->dest->ccn.channel_of_client) ) - { - receiver = ch->owner; - ack_to_owner = GNUNET_NO; - } - else - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - GNUNET_assert (NULL != receiver); - ld->ccn = receiver->ccn; - GNUNET_memcpy (&ld[1], - buf, - buf_len); - if (GNUNET_YES == receiver->client_ready) - { - ch->pending_messages--; - GSC_send_to_client (receiver->c, - env); - send_ack_to_client (ch, - ack_to_owner); - } - else - { - struct CadetOutOfOrderMessage *oom; - - oom = GNUNET_new (struct CadetOutOfOrderMessage); - oom->env = env; - GNUNET_CONTAINER_DLL_insert_tail (receiver->head_recv, - receiver->tail_recv, - oom); - receiver->num_recv++; - } - return GNUNET_OK; - } - - /* Everything is correct, send the message. */ - crm = GNUNET_malloc (sizeof (*crm)); - crm->ch = ch; - crm->data_message = GNUNET_malloc (sizeof (struct GNUNET_CADET_ChannelAppDataMessage) - + buf_len); - crm->data_message->header.size = htons (sizeof (struct GNUNET_CADET_ChannelAppDataMessage) + buf_len); - crm->data_message->header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA); - ch->mid_send.mid = htonl (ntohl (ch->mid_send.mid) + 1); - crm->data_message->mid = ch->mid_send; - crm->data_message->ctn = ch->ctn; - GNUNET_memcpy (&crm->data_message[1], - buf, - buf_len); - GNUNET_CONTAINER_DLL_insert_tail (ch->head_sent, - ch->tail_sent, - crm); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending message %u from local client to %s with %u bytes\n", - ntohl (crm->data_message->mid.mid), - GCCH_2s (ch), - buf_len); - if (NULL != ch->retry_data_task) - { - GNUNET_SCHEDULER_cancel (ch->retry_data_task); - ch->retry_data_task = NULL; - } - crm->qe = GCT_send (ch->t, - &crm->data_message->header, - &data_sent_cb, - crm); - GNUNET_assert (NULL == ch->retry_data_task); - return GNUNET_OK; -} - - -/** - * Handle ACK from client on local channel. Means the client is ready - * for more data, see if we have any for it. - * - * @param ch channel to destroy - * @param client_ccn ccn of the client sending the ack - */ -void -GCCH_handle_local_ack (struct CadetChannel *ch, - struct GNUNET_CADET_ClientChannelNumber client_ccn) -{ - struct CadetChannelClient *ccc; - struct CadetOutOfOrderMessage *com; - - if ( (NULL != ch->owner) && - (ch->owner->ccn.channel_of_client == client_ccn.channel_of_client) ) - ccc = ch->owner; - else if ( (NULL != ch->dest) && - (ch->dest->ccn.channel_of_client == client_ccn.channel_of_client) ) - ccc = ch->dest; - else - GNUNET_assert (0); - ccc->client_ready = GNUNET_YES; - com = ccc->head_recv; - if (NULL == com) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got LOCAL_ACK, %s-%X ready to receive more data, but none pending on %s-%X(%p)!\n", - GSC_2s (ccc->c), - ntohl (client_ccn.channel_of_client), - GCCH_2s (ch), - ntohl (ccc->ccn.channel_of_client), - ccc); - return; /* none pending */ - } - if (GNUNET_YES == ch->is_loopback) - { - int to_owner; - - /* Messages are always in-order, just send */ - GNUNET_CONTAINER_DLL_remove (ccc->head_recv, - ccc->tail_recv, - com); - ccc->num_recv--; - GSC_send_to_client (ccc->c, - com->env); - /* Notify sender that we can receive more */ - if ( (NULL != ch->owner) && - (ccc->ccn.channel_of_client == - ch->owner->ccn.channel_of_client) ) - { - to_owner = GNUNET_NO; - } - else - { - GNUNET_assert ( (NULL != ch->dest) && - (ccc->ccn.channel_of_client == - ch->dest->ccn.channel_of_client) ); - to_owner = GNUNET_YES; - } - send_ack_to_client (ch, - to_owner); - GNUNET_free (com); - return; - } - - if ( (com->mid.mid != ch->mid_recv.mid) && - (GNUNET_NO == ch->out_of_order) && - (GNUNET_YES == ch->reliable) ) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got LOCAL_ACK, %s-%X ready to receive more data (but next one is out-of-order %u vs. %u)!\n", - GSC_2s (ccc->c), - ntohl (ccc->ccn.channel_of_client), - ntohl (com->mid.mid), - ntohl (ch->mid_recv.mid)); - return; /* missing next one in-order */ - } - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got LOCAL_ACK, giving payload message %u to %s-%X on %s\n", - ntohl (com->mid.mid), - GSC_2s (ccc->c), - ntohl (ccc->ccn.channel_of_client), - GCCH_2s (ch)); - - /* all good, pass next message to client */ - GNUNET_CONTAINER_DLL_remove (ccc->head_recv, - ccc->tail_recv, - com); - ccc->num_recv--; - /* FIXME: if unreliable, this is not aggressive - enough, as it would be OK to have lost some! */ - - ch->mid_recv.mid = htonl (1 + ntohl (com->mid.mid)); - ch->mid_futures >>= 1; /* equivalent to division by 2 */ - ccc->client_ready = GNUNET_NO; - GSC_send_to_client (ccc->c, - com->env); - GNUNET_free (com); - send_channel_data_ack (ch); - if (NULL != ccc->head_recv) - return; - if (GNUNET_NO == ch->destroy) - return; - GCT_send_channel_destroy (ch->t, - ch->ctn); - channel_destroy (ch); -} - - -#define LOG2(level, ...) GNUNET_log_from_nocheck(level,"cadet-chn",__VA_ARGS__) - - -/** - * Log channel info. - * - * @param ch Channel. - * @param level Debug level to use. - */ -void -GCCH_debug (struct CadetChannel *ch, - enum GNUNET_ErrorType level) -{ - int do_log; - - do_log = GNUNET_get_log_call_status (level & (~GNUNET_ERROR_TYPE_BULK), - "cadet-chn", - __FILE__, __FUNCTION__, __LINE__); - if (0 == do_log) - return; - - if (NULL == ch) - { - LOG2 (level, "CHN *** DEBUG NULL CHANNEL ***\n"); - return; - } - LOG2 (level, - "CHN %s:%X (%p)\n", - GCT_2s (ch->t), - ch->ctn, - ch); - if (NULL != ch->owner) - { - LOG2 (level, - "CHN origin %s ready %s local-id: %u\n", - GSC_2s (ch->owner->c), - ch->owner->client_ready ? "YES" : "NO", - ntohl (ch->owner->ccn.channel_of_client)); - } - if (NULL != ch->dest) - { - LOG2 (level, - "CHN destination %s ready %s local-id: %u\n", - GSC_2s (ch->dest->c), - ch->dest->client_ready ? "YES" : "NO", - ntohl (ch->dest->ccn.channel_of_client)); - } - LOG2 (level, - "CHN Message IDs recv: %d (%LLX), send: %d\n", - ntohl (ch->mid_recv.mid), - (unsigned long long) ch->mid_futures, - ntohl (ch->mid_send.mid)); -} - - - -/* end of gnunet-service-cadet-new_channel.c */ diff --git a/src/cadet/gnunet-service-cadet-new_channel.h b/src/cadet/gnunet-service-cadet-new_channel.h deleted file mode 100644 index 5167305a6c..0000000000 --- a/src/cadet/gnunet-service-cadet-new_channel.h +++ /dev/null @@ -1,262 +0,0 @@ - -/* - This file is part of GNUnet. - Copyright (C) 2001-2017 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file cadet/gnunet-service-cadet-new_channel.h - * @brief GNUnet CADET service with encryption - * @author Bartlomiej Polot - * @author Christian Grothoff - */ -#ifndef GNUNET_SERVICE_CADET_CHANNEL_H -#define GNUNET_SERVICE_CADET_CHANNEL_H - -#include "gnunet-service-cadet-new.h" -#include "gnunet-service-cadet-new_peer.h" -#include "cadet_protocol.h" - - -/** - * A channel is a bidirectional connection between two CADET - * clients. Communiation can be reliable, unreliable, in-order - * or out-of-order. One client is the "local" client, this - * one initiated the connection. The other client is the - * "incoming" client, this one listened on a port to accept - * the connection from the "local" client. - */ -struct CadetChannel; - - -/** - * Get the static string for identification of the channel. - * - * @param ch Channel. - * - * @return Static string with the channel IDs. - */ -const char * -GCCH_2s (const struct CadetChannel *ch); - - -/** - * Log channel info. - * - * @param ch Channel. - * @param level Debug level to use. - */ -void -GCCH_debug (struct CadetChannel *ch, - enum GNUNET_ErrorType level); - - -/** - * Get the channel's public ID. - * - * @param ch Channel. - * - * @return ID used to identify the channel with the remote peer. - */ -struct GNUNET_CADET_ChannelTunnelNumber -GCCH_get_id (const struct CadetChannel *ch); - - -/** - * Create a new channel. - * - * @param owner local client owning the channel - * @param owner_id local chid of this channel at the @a owner - * @param destination peer to which we should build the channel - * @param port desired port at @a destination - * @param options options for the channel - * @return handle to the new channel - */ -struct CadetChannel * -GCCH_channel_local_new (struct CadetClient *owner, - struct GNUNET_CADET_ClientChannelNumber owner_id, - struct CadetPeer *destination, - const struct GNUNET_HashCode *port, - uint32_t options); - - -/** - * A client is bound to the port that we have a channel - * open to. Send the acknowledgement for the connection - * request and establish the link with the client. - * - * @param ch open incoming channel - * @param c client listening on the respective port - */ -void -GCCH_bind (struct CadetChannel *ch, - struct CadetClient *c); - - -/** - * Destroy locally created channel. Called by the - * local client, so no need to tell the client. - * - * @param ch channel to destroy - * @param c client that caused the destruction - * @param ccn client number of the client @a c - */ -void -GCCH_channel_local_destroy (struct CadetChannel *ch, - struct CadetClient *c, - struct GNUNET_CADET_ClientChannelNumber ccn); - - -/** - * Function called once and only once after a channel was bound - * to its tunnel via #GCT_add_channel() is ready for transmission. - * Note that this is only the case for channels that this peer - * initiates, as for incoming channels we assume that they are - * ready for transmission immediately upon receiving the open - * message. Used to bootstrap the #GCT_send() process. - * - * @param ch the channel for which the tunnel is now ready - */ -void -GCCH_tunnel_up (struct CadetChannel *ch); - - -/** - * Create a new channel based on a request coming in over the network. - * - * @param t tunnel to the remote peer - * @param chid identifier of this channel in the tunnel - * @param origin peer to who initiated the channel - * @param port desired local port - * @param options options for the channel - * @return handle to the new channel - */ -struct CadetChannel * -GCCH_channel_incoming_new (struct CadetTunnel *t, - struct GNUNET_CADET_ChannelTunnelNumber chid, - const struct GNUNET_HashCode *port, - uint32_t options); - - -/** - * We got a #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN message again for - * this channel. If the binding was successful, (re)transmit the - * #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK. - * - * @param ch channel that got the duplicate open - * @param cti identifier of the connection that delivered the message - */ -void -GCCH_handle_duplicate_open (struct CadetChannel *ch, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti); - - - -/** - * We got payload data for a channel. Pass it on to the client. - * - * @param ch channel that got data - * @param cti identifier of the connection that delivered the message - * @param msg message that was received - */ -void -GCCH_handle_channel_plaintext_data (struct CadetChannel *ch, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti, - const struct GNUNET_CADET_ChannelAppDataMessage *msg); - - -/** - * We got an acknowledgement for payload data for a channel. - * Possibly resume transmissions. - * - * @param ch channel that got the ack - * @param cti identifier of the connection that delivered the message - * @param ack details about what was received - */ -void -GCCH_handle_channel_plaintext_data_ack (struct CadetChannel *ch, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti, - const struct GNUNET_CADET_ChannelDataAckMessage *ack); - - -/** - * We got an acknowledgement for the creation of the channel - * (the port is open on the other side). Begin transmissions. - * - * @param ch channel to destroy - * @param cti identifier of the connection that delivered the message, - * NULL if the ACK was inferred because we got payload or are on loopback - */ -void -GCCH_handle_channel_open_ack (struct CadetChannel *ch, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti); - - -/** - * Destroy channel, based on the other peer closing the - * connection. Also needs to remove this channel from - * the tunnel. - * - * FIXME: need to make it possible to defer destruction until we have - * received all messages up to the destroy, and right now the destroy - * message (and this API) fails to give is the information we need! - * - * FIXME: also need to know if the other peer got a destroy from - * us before! - * - * @param ch channel to destroy - * @param cti identifier of the connection that delivered the message, - * NULL during shutdown - */ -void -GCCH_handle_remote_destroy (struct CadetChannel *ch, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti); - - -/** - * Handle data given by a client. - * - * Check whether the client is allowed to send in this tunnel, save if - * channel is reliable and send an ACK to the client if there is still - * buffer space in the tunnel. - * - * @param ch Channel. - * @param sender_ccn ccn of the sender - * @param buf payload to transmit. - * @param buf_len number of bytes in @a buf - * @return #GNUNET_OK if everything goes well, - * #GNUNET_SYSERR in case of an error. - */ -int -GCCH_handle_local_data (struct CadetChannel *ch, - struct GNUNET_CADET_ClientChannelNumber sender_ccn, - const char *buf, - size_t buf_len); - - -/** - * Handle ACK from client on local channel. - * - * @param ch channel to destroy - * @param client_ccn ccn of the client sending the ack - */ -void -GCCH_handle_local_ack (struct CadetChannel *ch, - struct GNUNET_CADET_ClientChannelNumber client_ccn); - -#endif diff --git a/src/cadet/gnunet-service-cadet-new_connection.c b/src/cadet/gnunet-service-cadet-new_connection.c deleted file mode 100644 index 6976e66e4a..0000000000 --- a/src/cadet/gnunet-service-cadet-new_connection.c +++ /dev/null @@ -1,1093 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2001-2017 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file cadet/gnunet-service-cadet-new_connection.c - * @brief management of CORE-level end-to-end connections; establishes - * end-to-end routes and transmits messages along the route - * @author Bartlomiej Polot - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet-service-cadet-new.h" -#include "gnunet-service-cadet-new_channel.h" -#include "gnunet-service-cadet-new_connection.h" -#include "gnunet-service-cadet-new_paths.h" -#include "gnunet-service-cadet-new_peer.h" -#include "gnunet-service-cadet-new_tunnels.h" -#include "gnunet_cadet_service.h" -#include "gnunet_statistics_service.h" -#include "cadet_protocol.h" - - -#define LOG(level, ...) GNUNET_log_from(level,"cadet-con",__VA_ARGS__) - - -/** - * All the states a connection can be in. - */ -enum CadetConnectionState -{ - /** - * Uninitialized status, we have not yet even gotten the message queue. - */ - CADET_CONNECTION_NEW, - - /** - * Connection create message in queue, awaiting transmission by CORE. - */ - CADET_CONNECTION_SENDING_CREATE, - - /** - * Connection create message sent, waiting for ACK. - */ - CADET_CONNECTION_SENT, - - /** - * We are an inbound connection, and received a CREATE. Need to - * send an CREATE_ACK back. - */ - CADET_CONNECTION_CREATE_RECEIVED, - - /** - * Connection confirmed, ready to carry traffic. - */ - CADET_CONNECTION_READY - -}; - - -/** - * Low-level connection to a destination. - */ -struct CadetConnection -{ - - /** - * ID of the connection. - */ - struct GNUNET_CADET_ConnectionTunnelIdentifier cid; - - /** - * To which peer does this connection go? - */ - struct CadetPeer *destination; - - /** - * Which tunnel is using this connection? - */ - struct CadetTConnection *ct; - - /** - * Path we are using to our destination. - */ - struct CadetPeerPath *path; - - /** - * Pending message, NULL if we are ready to transmit. - */ - struct GNUNET_MQ_Envelope *env; - - /** - * Handle for calling #GCP_request_mq_cancel() once we are finished. - */ - struct GCP_MessageQueueManager *mq_man; - - /** - * Task for connection maintenance. - */ - struct GNUNET_SCHEDULER_Task *task; - - /** - * Queue entry for keepalive messages. - */ - struct CadetTunnelQueueEntry *keepalive_qe; - - /** - * Function to call once we are ready to transmit. - */ - GCC_ReadyCallback ready_cb; - - /** - * Closure for @e ready_cb. - */ - void *ready_cb_cls; - - /** - * How long do we wait before we try again with a CREATE message? - */ - struct GNUNET_TIME_Relative retry_delay; - - /** - * Performance metrics for this connection. - */ - struct CadetConnectionMetrics metrics; - - /** - * State of the connection. - */ - enum CadetConnectionState state; - - /** - * Options for the route, control buffering. - */ - enum GNUNET_CADET_ChannelOption options; - - /** - * How many latency observations did we make for this connection? - */ - unsigned int latency_datapoints; - - /** - * Offset of our @e destination in @e path. - */ - unsigned int off; - - /** - * Are we ready to transmit via @e mq_man right now? - */ - int mqm_ready; - -}; - - -/** - * Lookup a connection by its identifier. - * - * @param cid identifier to resolve - * @return NULL if connection was not found - */ -struct CadetConnection * -GCC_lookup (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) -{ - return GNUNET_CONTAINER_multishortmap_get (connections, - &cid->connection_of_tunnel); -} - - -/** - * Update the connection state. Also triggers the necessary - * MQM notifications. - * - * @param cc connection to update the state for - * @param new_state new state for @a cc - * @param new_mqm_ready new `mqm_ready` state for @a cc - */ -static void -update_state (struct CadetConnection *cc, - enum CadetConnectionState new_state, - int new_mqm_ready) -{ - int old_ready; - int new_ready; - - if ( (new_state == cc->state) && - (new_mqm_ready == cc->mqm_ready) ) - return; /* no change, nothing to do */ - old_ready = ( (CADET_CONNECTION_READY == cc->state) && - (GNUNET_YES == cc->mqm_ready) ); - new_ready = ( (CADET_CONNECTION_READY == new_state) && - (GNUNET_YES == new_mqm_ready) ); - cc->state = new_state; - cc->mqm_ready = new_mqm_ready; - if (old_ready != new_ready) - cc->ready_cb (cc->ready_cb_cls, - new_ready); -} - - -/** - * Destroy a connection, part of the internal implementation. Called - * only from #GCC_destroy_from_core() or #GCC_destroy_from_tunnel(). - * - * @param cc connection to destroy - */ -static void -GCC_destroy (struct CadetConnection *cc) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Destroying %s\n", - GCC_2s (cc)); - if (NULL != cc->mq_man) - { - GCP_request_mq_cancel (cc->mq_man, - NULL); - cc->mq_man = NULL; - } - if (NULL != cc->task) - { - GNUNET_SCHEDULER_cancel (cc->task); - cc->task = NULL; - } - if (NULL != cc->keepalive_qe) - { - GCT_send_cancel (cc->keepalive_qe); - cc->keepalive_qe = NULL; - } - GCPP_del_connection (cc->path, - cc->off, - cc); - for (unsigned int i=0;i<cc->off;i++) - GCP_remove_connection (GCPP_get_peer_at_offset (cc->path, - i), - cc); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multishortmap_remove (connections, - &GCC_get_id (cc)->connection_of_tunnel, - cc)); - GNUNET_free (cc); -} - - - -/** - * Destroy a connection, called when the CORE layer is already done - * (i.e. has received a BROKEN message), but if we still have to - * communicate the destruction of the connection to the tunnel (if one - * exists). - * - * @param cc connection to destroy - */ -void -GCC_destroy_without_core (struct CadetConnection *cc) -{ - if (NULL != cc->ct) - { - GCT_connection_lost (cc->ct); - cc->ct = NULL; - } - GCC_destroy (cc); -} - - -/** - * Destroy a connection, called if the tunnel association with the - * connection was already broken, but we still need to notify the CORE - * layer about the breakage. - * - * @param cc connection to destroy - */ -void -GCC_destroy_without_tunnel (struct CadetConnection *cc) -{ - cc->ct = NULL; - if ( (CADET_CONNECTION_SENDING_CREATE != cc->state) && - (NULL != cc->mq_man) ) - { - struct GNUNET_MQ_Envelope *env; - struct GNUNET_CADET_ConnectionDestroyMessage *destroy_msg; - - /* Need to notify next hop that we are down. */ - env = GNUNET_MQ_msg (destroy_msg, - GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY); - destroy_msg->cid = cc->cid; - GCP_request_mq_cancel (cc->mq_man, - env); - cc->mq_man = NULL; - } - GCC_destroy (cc); -} - - -/** - * Return the tunnel associated with this connection. - * - * @param cc connection to query - * @return corresponding entry in the tunnel's connection list - */ -struct CadetTConnection * -GCC_get_ct (struct CadetConnection *cc) -{ - return cc->ct; -} - - -/** - * Obtain performance @a metrics from @a cc. - * - * @param cc connection to query - * @return the metrics - */ -const struct CadetConnectionMetrics * -GCC_get_metrics (struct CadetConnection *cc) -{ - return &cc->metrics; -} - - -/** - * Send a #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE through the - * tunnel to prevent it from timing out. - * - * @param cls the `struct CadetConnection` to keep alive. - */ -static void -send_keepalive (void *cls); - - -/** - * Keepalive was transmitted. Remember this, and possibly - * schedule the next one. - * - * @param cls the `struct CadetConnection` to keep alive. - * @param cid identifier of the connection within the tunnel, NULL - * if transmission failed - */ -static void -keepalive_done (void *cls, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) -{ - struct CadetConnection *cc = cls; - - cc->keepalive_qe = NULL; - if ( (GNUNET_YES == cc->mqm_ready) && - (NULL == cc->task) ) - cc->task = GNUNET_SCHEDULER_add_delayed (keepalive_period, - &send_keepalive, - cc); -} - - -/** - * Send a #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE through the - * tunnel to prevent it from timing out. - * - * @param cls the `struct CadetConnection` to keep alive. - */ -static void -send_keepalive (void *cls) -{ - struct CadetConnection *cc = cls; - struct GNUNET_MessageHeader msg; - - cc->task = NULL; - if (CADET_TUNNEL_KEY_OK != GCT_get_estate (cc->ct->t)) - { - /* Tunnel not yet ready, wait with keepalives... */ - cc->task = GNUNET_SCHEDULER_add_delayed (keepalive_period, - &send_keepalive, - cc); - return; - } - GNUNET_assert (NULL != cc->ct); - GNUNET_assert (GNUNET_YES == cc->mqm_ready); - GNUNET_assert (NULL == cc->keepalive_qe); - LOG (GNUNET_ERROR_TYPE_INFO, - "Sending KEEPALIVE on behalf of %s via %s\n", - GCC_2s (cc), - GCT_2s (cc->ct->t)); - GNUNET_STATISTICS_update (stats, - "# keepalives sent", - 1, - GNUNET_NO); - msg.size = htons (sizeof (msg)); - msg.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE); - - cc->keepalive_qe - = GCT_send (cc->ct->t, - &msg, - &keepalive_done, - cc); -} - - -/** - * We sent a message for which we expect to receive an ACK via - * the connection identified by @a cti. - * - * @param cid connection identifier where we expect an ACK - */ -void -GCC_ack_expected (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) -{ - struct CadetConnection *cc; - - cc = GCC_lookup (cid); - if (NULL == cc) - return; /* whopise, connection alredy down? */ - cc->metrics.num_acked_transmissions++; -} - - -/** - * We observed an ACK for a message that was originally sent via - * the connection identified by @a cti. - * - * @param cti connection identifier where we got an ACK for a message - * that was originally sent via this connection (the ACK - * may have gotten back to us via a different connection). - */ -void -GCC_ack_observed (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) -{ - struct CadetConnection *cc; - - cc = GCC_lookup (cid); - if (NULL == cc) - return; /* whopise, connection alredy down? */ - cc->metrics.num_successes++; -} - - -/** - * We observed some the given @a latency on the connection - * identified by @a cti. (The same connection was taken - * in both directions.) - * - * @param cid connection identifier where we measured latency - * @param latency the observed latency - */ -void -GCC_latency_observed (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid, - struct GNUNET_TIME_Relative latency) -{ - struct CadetConnection *cc; - double weight; - double result; - - cc = GCC_lookup (cid); - if (NULL == cc) - return; /* whopise, connection alredy down? */ - GNUNET_STATISTICS_update (stats, - "# latencies observed", - 1, - GNUNET_NO); - cc->latency_datapoints++; - if (cc->latency_datapoints >= 7) - weight = 7.0; - else - weight = cc->latency_datapoints; - /* Compute weighted average, giving at MOST weight 7 to the - existing values, or less if that value is based on fewer than 7 - measurements. */ - result = (weight * cc->metrics.aged_latency.rel_value_us) + 1.0 * latency.rel_value_us; - result /= (weight + 1.0); - cc->metrics.aged_latency.rel_value_us = (uint64_t) result; -} - - -/** - * A #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK was received for this connection, implying - * that the end-to-end connection is up. Process it. - * - * @param cc the connection that got the ACK. - */ -void -GCC_handle_connection_create_ack (struct CadetConnection *cc) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received CADET_CONNECTION_CREATE_ACK for %s in state %d (%s)\n", - GCC_2s (cc), - cc->state, - (GNUNET_YES == cc->mqm_ready) ? "MQM ready" : "MQM busy"); - if (CADET_CONNECTION_READY == cc->state) - return; /* Duplicate ACK, ignore */ - if (NULL != cc->task) - { - GNUNET_SCHEDULER_cancel (cc->task); - cc->task = NULL; - } - cc->metrics.age = GNUNET_TIME_absolute_get (); - update_state (cc, - CADET_CONNECTION_READY, - cc->mqm_ready); - if ( (NULL == cc->keepalive_qe) && - (GNUNET_YES == cc->mqm_ready) && - (NULL == cc->task) ) - cc->task = GNUNET_SCHEDULER_add_delayed (keepalive_period, - &send_keepalive, - cc); -} - - -/** - * Handle KX message. - * - * @param cc connection that received encrypted message - * @param msg the key exchange message - */ -void -GCC_handle_kx (struct CadetConnection *cc, - const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg) -{ - if (CADET_CONNECTION_SENT == cc->state) - { - /* We didn't get the CADET_CONNECTION_CREATE_ACK, but instead got payload. That's fine, - clearly something is working, so pretend we got an ACK. */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Faking connection CADET_CONNECTION_CREATE_ACK for %s due to KX\n", - GCC_2s (cc)); - GCC_handle_connection_create_ack (cc); - } - GCT_handle_kx (cc->ct, - msg); -} - - -/** - * Handle KX_AUTH message. - * - * @param cc connection that received encrypted message - * @param msg the key exchange message - */ -void -GCC_handle_kx_auth (struct CadetConnection *cc, - const struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg) -{ - if (CADET_CONNECTION_SENT == cc->state) - { - /* We didn't get the CADET_CONNECTION_CREATE_ACK, but instead got payload. That's fine, - clearly something is working, so pretend we got an ACK. */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Faking connection CADET_CONNECTION_CREATE_ACK for %s due to KX\n", - GCC_2s (cc)); - GCC_handle_connection_create_ack (cc); - } - GCT_handle_kx_auth (cc->ct, - msg); -} - - -/** - * Handle encrypted message. - * - * @param cc connection that received encrypted message - * @param msg the encrypted message to decrypt - */ -void -GCC_handle_encrypted (struct CadetConnection *cc, - const struct GNUNET_CADET_TunnelEncryptedMessage *msg) -{ - if (CADET_CONNECTION_SENT == cc->state) - { - /* We didn't get the CREATE_ACK, but instead got payload. That's fine, - clearly something is working, so pretend we got an ACK. */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Faking connection ACK for %s due to ENCRYPTED payload\n", - GCC_2s (cc)); - GCC_handle_connection_create_ack (cc); - } - cc->metrics.last_use = GNUNET_TIME_absolute_get (); - GCT_handle_encrypted (cc->ct, - msg); -} - - -/** - * Send a #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE message to the - * first hop. - * - * @param cls the `struct CadetConnection` to initiate - */ -static void -send_create (void *cls) -{ - struct CadetConnection *cc = cls; - struct GNUNET_CADET_ConnectionCreateMessage *create_msg; - struct GNUNET_PeerIdentity *pids; - struct GNUNET_MQ_Envelope *env; - unsigned int path_length; - - cc->task = NULL; - GNUNET_assert (GNUNET_YES == cc->mqm_ready); - path_length = GCPP_get_length (cc->path); - env = GNUNET_MQ_msg_extra (create_msg, - (1 + path_length) * sizeof (struct GNUNET_PeerIdentity), - GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE); - create_msg->options = htonl ((uint32_t) cc->options); - create_msg->cid = cc->cid; - pids = (struct GNUNET_PeerIdentity *) &create_msg[1]; - pids[0] = my_full_id; - for (unsigned int i=0;i<path_length;i++) - pids[i + 1] = *GCP_get_id (GCPP_get_peer_at_offset (cc->path, - i)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending CADET_CONNECTION_CREATE message for %s\n", - GCC_2s (cc)); - cc->env = env; - update_state (cc, - CADET_CONNECTION_SENT, - GNUNET_NO); - GCP_send (cc->mq_man, - env); -} - - -/** - * Send a CREATE_ACK message towards the origin. - * - * @param cls the `struct CadetConnection` to initiate - */ -static void -send_create_ack (void *cls) -{ - struct CadetConnection *cc = cls; - struct GNUNET_CADET_ConnectionCreateAckMessage *ack_msg; - struct GNUNET_MQ_Envelope *env; - - cc->task = NULL; - GNUNET_assert (CADET_CONNECTION_CREATE_RECEIVED == cc->state); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending CONNECTION_CREATE_ACK message for %s\n", - GCC_2s (cc)); - GNUNET_assert (GNUNET_YES == cc->mqm_ready); - env = GNUNET_MQ_msg (ack_msg, - GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK); - ack_msg->cid = cc->cid; - cc->env = env; - update_state (cc, - CADET_CONNECTION_READY, - GNUNET_NO); - GCP_send (cc->mq_man, - env); -} - - -/** - * We got a #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE for a - * connection that we already have. Either our ACK got lost - * or something is fishy. Consider retransmitting the ACK. - * - * @param cc connection that got the duplicate CREATE - */ -void -GCC_handle_duplicate_create (struct CadetConnection *cc) -{ - if (GNUNET_YES == cc->mqm_ready) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got duplicate CREATE for %s, scheduling another ACK (%s)\n", - GCC_2s (cc), - (GNUNET_YES == cc->mqm_ready) ? "MQM ready" : "MQM busy"); - /* Revert back to the state of having only received the 'CREATE', - and immediately proceed to send the CREATE_ACK. */ - update_state (cc, - CADET_CONNECTION_CREATE_RECEIVED, - cc->mqm_ready); - if (NULL != cc->task) - GNUNET_SCHEDULER_cancel (cc->task); - cc->task = GNUNET_SCHEDULER_add_now (&send_create_ack, - cc); - } - else - { - /* We are currently sending something else back, which - can only be an ACK or payload, either of which would - do. So actually no need to do anything. */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got duplicate CREATE for %s. MQ is busy, not queueing another ACK\n", - GCC_2s (cc)); - } -} - - -/** - * There has been a change in the message queue existence for our - * peer at the first hop. Adjust accordingly. - * - * @param cls the `struct CadetConnection` - * @param available #GNUNET_YES if sending is now possible, - * #GNUNET_NO if sending is no longer possible - * #GNUNET_SYSERR if sending is no longer possible - * and the last envelope was discarded - */ -static void -manage_first_hop_mq (void *cls, - int available) -{ - struct CadetConnection *cc = cls; - - if (GNUNET_YES != available) - { - /* Connection is down, for now... */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Core MQ for %s went down\n", - GCC_2s (cc)); - update_state (cc, - CADET_CONNECTION_NEW, - GNUNET_NO); - cc->retry_delay = GNUNET_TIME_UNIT_ZERO; - if (NULL != cc->task) - { - GNUNET_SCHEDULER_cancel (cc->task); - cc->task = NULL; - } - return; - } - - update_state (cc, - cc->state, - GNUNET_YES); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Core MQ for %s became available in state %d\n", - GCC_2s (cc), - cc->state); - switch (cc->state) - { - case CADET_CONNECTION_NEW: - /* Transmit immediately */ - cc->task = GNUNET_SCHEDULER_add_now (&send_create, - cc); - break; - case CADET_CONNECTION_SENDING_CREATE: - /* Should not be possible to be called in this state. */ - GNUNET_assert (0); - break; - case CADET_CONNECTION_SENT: - /* Retry a bit later... */ - cc->retry_delay = GNUNET_TIME_STD_BACKOFF (cc->retry_delay); - cc->task = GNUNET_SCHEDULER_add_delayed (cc->retry_delay, - &send_create, - cc); - break; - case CADET_CONNECTION_CREATE_RECEIVED: - /* We got the 'CREATE' (incoming connection), should send the CREATE_ACK */ - cc->metrics.age = GNUNET_TIME_absolute_get (); - cc->task = GNUNET_SCHEDULER_add_now (&send_create_ack, - cc); - break; - case CADET_CONNECTION_READY: - if ( (NULL == cc->keepalive_qe) && - (GNUNET_YES == cc->mqm_ready) && - (NULL == cc->task) ) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Scheduling keepalive for %s in %s\n", - GCC_2s (cc), - GNUNET_STRINGS_relative_time_to_string (keepalive_period, - GNUNET_YES)); - cc->task = GNUNET_SCHEDULER_add_delayed (keepalive_period, - &send_keepalive, - cc); - } - break; - } -} - - -/** - * Create a connection to @a destination via @a path and notify @a cb - * whenever we are ready for more data. Shared logic independent of - * who is initiating the connection. - * - * @param destination where to go - * @param path which path to take (may not be the full path) - * @param off offset of @a destination on @a path - * @param options options for the connection - * @param ct which tunnel uses this connection - * @param init_state initial state for the connection - * @param ready_cb function to call when ready to transmit - * @param ready_cb_cls closure for @a cb - * @return handle to the connection - */ -static struct CadetConnection * -connection_create (struct CadetPeer *destination, - struct CadetPeerPath *path, - unsigned int off, - enum GNUNET_CADET_ChannelOption options, - struct CadetTConnection *ct, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid, - enum CadetConnectionState init_state, - GCC_ReadyCallback ready_cb, - void *ready_cb_cls) -{ - struct CadetConnection *cc; - struct CadetPeer *first_hop; - - cc = GNUNET_new (struct CadetConnection); - cc->options = options; - cc->state = init_state; - cc->ct = ct; - cc->cid = *cid; - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multishortmap_put (connections, - &GCC_get_id (cc)->connection_of_tunnel, - cc, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - cc->ready_cb = ready_cb; - cc->ready_cb_cls = ready_cb_cls; - cc->path = path; - cc->off = off; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Creating %s using path %s\n", - GCC_2s (cc), - GCPP_2s (path)); - GCPP_add_connection (path, - off, - cc); - for (unsigned int i=0;i<off;i++) - GCP_add_connection (GCPP_get_peer_at_offset (path, - i), - cc); - - first_hop = GCPP_get_peer_at_offset (path, - 0); - cc->mq_man = GCP_request_mq (first_hop, - &manage_first_hop_mq, - cc); - return cc; -} - - -/** - * Create a connection to @a destination via @a path and - * notify @a cb whenever we are ready for more data. This - * is an inbound tunnel, so we must use the existing @a cid - * - * @param destination where to go - * @param path which path to take (may not be the full path) - * @param options options for the connection - * @param ct which tunnel uses this connection - * @param ready_cb function to call when ready to transmit - * @param ready_cb_cls closure for @a cb - * @return handle to the connection, NULL if we already have - * a connection that takes precedence on @a path - */ -struct CadetConnection * -GCC_create_inbound (struct CadetPeer *destination, - struct CadetPeerPath *path, - enum GNUNET_CADET_ChannelOption options, - struct CadetTConnection *ct, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid, - GCC_ReadyCallback ready_cb, - void *ready_cb_cls) -{ - struct CadetConnection *cc; - unsigned int off; - - off = GCPP_find_peer (path, - destination); - GNUNET_assert (UINT_MAX != off); - cc = GCPP_get_connection (path, - destination, - off); - if (NULL != cc) - { - int cmp; - - cmp = memcmp (cid, - &cc->cid, - sizeof (*cid)); - if (0 == cmp) - { - /* Two peers picked the SAME random connection identifier at the - same time for the same path? Must be malicious. Drop - connection (existing and inbound), even if it is the only - one. */ - GNUNET_break_op (0); - GCT_connection_lost (cc->ct); - GCC_destroy_without_tunnel (cc); - return NULL; - } - if (0 < cmp) - { - /* drop existing */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got two connections on %s, dropping my existing %s\n", - GCPP_2s (path), - GCC_2s (cc)); - GCT_connection_lost (cc->ct); - GCC_destroy_without_tunnel (cc); - } - else - { - /* keep existing */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got two connections on %s, keeping my existing %s\n", - GCPP_2s (path), - GCC_2s (cc)); - return NULL; - } - } - - return connection_create (destination, - path, - off, - options, - ct, - cid, - CADET_CONNECTION_CREATE_RECEIVED, - ready_cb, - ready_cb_cls); -} - - -/** - * Create a connection to @a destination via @a path and - * notify @a cb whenever we are ready for more data. - * - * @param destination where to go - * @param path which path to take (may not be the full path) - * @param off offset of @a destination on @a path - * @param options options for the connection - * @param ct tunnel that uses the connection - * @param ready_cb function to call when ready to transmit - * @param ready_cb_cls closure for @a cb - * @return handle to the connection - */ -struct CadetConnection * -GCC_create (struct CadetPeer *destination, - struct CadetPeerPath *path, - unsigned int off, - enum GNUNET_CADET_ChannelOption options, - struct CadetTConnection *ct, - GCC_ReadyCallback ready_cb, - void *ready_cb_cls) -{ - struct GNUNET_CADET_ConnectionTunnelIdentifier cid; - - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &cid, - sizeof (cid)); - return connection_create (destination, - path, - off, - options, - ct, - &cid, - CADET_CONNECTION_NEW, - ready_cb, - ready_cb_cls); -} - - -/** - * Transmit message @a msg via connection @a cc. Must only be called - * (once) after the connection has signalled that it is ready via the - * `ready_cb`. Clients can also use #GCC_is_ready() to check if the - * connection is right now ready for transmission. - * - * @param cc connection identification - * @param env envelope with message to transmit; must NOT - * yet have a #GNUNET_MQ_notify_sent() callback attached to it - */ -void -GCC_transmit (struct CadetConnection *cc, - struct GNUNET_MQ_Envelope *env) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Scheduling message for transmission on %s\n", - GCC_2s (cc)); - GNUNET_assert (GNUNET_YES == cc->mqm_ready); - GNUNET_assert (CADET_CONNECTION_READY == cc->state); - cc->metrics.last_use = GNUNET_TIME_absolute_get (); - cc->mqm_ready = GNUNET_NO; - if (NULL != cc->task) - { - GNUNET_SCHEDULER_cancel (cc->task); - cc->task = NULL; - } - GCP_send (cc->mq_man, - env); -} - - -/** - * Obtain the path used by this connection. - * - * @param cc connection - * @return path to @a cc - */ -struct CadetPeerPath * -GCC_get_path (struct CadetConnection *cc) -{ - return cc->path; -} - - -/** - * Obtain unique ID for the connection. - * - * @param cc connection. - * @return unique number of the connection - */ -const struct GNUNET_CADET_ConnectionTunnelIdentifier * -GCC_get_id (struct CadetConnection *cc) -{ - return &cc->cid; -} - - -/** - * Get a (static) string for a connection. - * - * @param cc Connection. - */ -const char * -GCC_2s (const struct CadetConnection *cc) -{ - static char buf[128]; - - if (NULL == cc) - return "Connection(NULL)"; - - if (NULL != cc->ct) - { - GNUNET_snprintf (buf, - sizeof (buf), - "Connection %s (%s)", - GNUNET_sh2s (&cc->cid.connection_of_tunnel), - GCT_2s (cc->ct->t)); - return buf; - } - GNUNET_snprintf (buf, - sizeof (buf), - "Connection %s", - GNUNET_sh2s (&cc->cid.connection_of_tunnel)); - return buf; -} - - -#define LOG2(level, ...) GNUNET_log_from_nocheck(level,"cadet-con",__VA_ARGS__) - - -/** - * Log connection info. - * - * @param cc connection - * @param level Debug level to use. - */ -void -GCC_debug (struct CadetConnection *cc, - enum GNUNET_ErrorType level) -{ - int do_log; - - do_log = GNUNET_get_log_call_status (level & (~GNUNET_ERROR_TYPE_BULK), - "cadet-con", - __FILE__, __FUNCTION__, __LINE__); - if (0 == do_log) - return; - if (NULL == cc) - { - LOG2 (level, - "Connection (NULL)\n"); - return; - } - LOG2 (level, - "%s to %s via path %s in state %d is %s\n", - GCC_2s (cc), - GCP_2s (cc->destination), - GCPP_2s (cc->path), - cc->state, - (GNUNET_YES == cc->mqm_ready) ? "ready" : "busy"); -} - -/* end of gnunet-service-cadet-new_connection.c */ diff --git a/src/cadet/gnunet-service-cadet-new_connection.h b/src/cadet/gnunet-service-cadet-new_connection.h deleted file mode 100644 index e48b208fda..0000000000 --- a/src/cadet/gnunet-service-cadet-new_connection.h +++ /dev/null @@ -1,339 +0,0 @@ - -/* - This file is part of GNUnet. - Copyright (C) 2001-2017 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file cadet/gnunet-service-cadet-new_connection.h - * @brief A connection is a live end-to-end messaging mechanism - * where the peers are identified by a path and know how - * to forward along the route using a connection identifier - * for routing the data. - * @author Bartlomiej Polot - * @author Christian Grothoff - */ -#ifndef GNUNET_SERVICE_CADET_CONNECTION_H -#define GNUNET_SERVICE_CADET_CONNECTION_H - -#include "gnunet_util_lib.h" -#include "gnunet-service-cadet-new.h" -#include "gnunet-service-cadet-new_peer.h" -#include "cadet_protocol.h" - - -/** - * Function called to notify tunnel about change in our readyness. - * - * @param cls closure - * @param is_ready #GNUNET_YES if the connection is now ready for transmission, - * #GNUNET_NO if the connection is no longer ready for transmission - */ -typedef void -(*GCC_ReadyCallback)(void *cls, - int is_ready); - - -/** - * Destroy a connection, called when the CORE layer is already done - * (i.e. has received a BROKEN message), but if we still have to - * communicate the destruction of the connection to the tunnel (if one - * exists). - * - * @param cc connection to destroy - */ -void -GCC_destroy_without_core (struct CadetConnection *cc); - - -/** - * Destroy a connection, called if the tunnel association with the - * connection was already broken, but we still need to notify the CORE - * layer about the breakage. - * - * @param cc connection to destroy - */ -void -GCC_destroy_without_tunnel (struct CadetConnection *cc); - - -/** - * Lookup a connection by its identifier. - * - * @param cid identifier to resolve - * @return NULL if connection was not found - */ -struct CadetConnection * -GCC_lookup (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid); - - -/** - * Create a connection to @a destination via @a path and - * notify @a cb whenever we are ready for more data. - * - * @param destination where to go - * @param path which path to take (may not be the full path) - * @param off offset of @a destination on @a path - * @param options options for the connection - * @param ct which tunnel uses this connection - * @param ready_cb function to call when ready to transmit - * @param ready_cb_cls closure for @a cb - * @return handle to the connection - */ -struct CadetConnection * -GCC_create (struct CadetPeer *destination, - struct CadetPeerPath *path, - unsigned int off, - enum GNUNET_CADET_ChannelOption options, - struct CadetTConnection *ct, - GCC_ReadyCallback ready_cb, - void *ready_cb_cls); - - -/** - * Create a connection to @a destination via @a path and - * notify @a cb whenever we are ready for more data. This - * is an inbound tunnel, so we must use the existing @a cid - * - * @param destination where to go - * @param path which path to take (may not be the full path) - * @param options options for the connection - * @param ct which tunnel uses this connection - * @param ready_cb function to call when ready to transmit - * @param ready_cb_cls closure for @a cb - * @return handle to the connection, NULL if we already have - * a connection that takes precedence on @a path - */ -struct CadetConnection * -GCC_create_inbound (struct CadetPeer *destination, - struct CadetPeerPath *path, - enum GNUNET_CADET_ChannelOption options, - struct CadetTConnection *ct, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid, - GCC_ReadyCallback ready_cb, - void *ready_cb_cls); - - -/** - * Transmit message @a msg via connection @a cc. Must only be called - * (once) after the connection has signalled that it is ready via the - * `ready_cb`. Clients can also use #GCC_is_ready() to check if the - * connection is right now ready for transmission. - * - * @param cc connection identification - * @param env envelope with message to transmit; - * the #GNUNET_MQ_notify_send() must not have yet been used - * for the envelope. Also, the message better match the - * connection identifier of this connection... - */ -void -GCC_transmit (struct CadetConnection *cc, - struct GNUNET_MQ_Envelope *env); - - -/** - * A CREATE_ACK was received for this connection, process it. - * - * @param cc the connection that got the ACK. - */ -void -GCC_handle_connection_create_ack (struct CadetConnection *cc); - - -/** - * We got a #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE for a - * connection that we already have. Either our ACK got lost - * or something is fishy. Consider retransmitting the ACK. - * - * @param cc connection that got the duplicate CREATE - */ -void -GCC_handle_duplicate_create (struct CadetConnection *cc); - - -/** - * Handle KX message. - * - * @param cc connection that received encrypted message - * @param msg the key exchange message - */ -void -GCC_handle_kx (struct CadetConnection *cc, - const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg); - - -/** - * Handle KX_AUTH message. - * - * @param cc connection that received encrypted message - * @param msg the key exchange message - */ -void -GCC_handle_kx_auth (struct CadetConnection *cc, - const struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg); - - -/** - * Performance metrics for a connection. - */ -struct CadetConnectionMetrics -{ - - /** - * Our current best estimate of the latency, based on a weighted - * average of at least @a latency_datapoints values. - */ - struct GNUNET_TIME_Relative aged_latency; - - /** - * When was this connection first established? (by us sending or - * receiving the CREATE_ACK for the first time) - */ - struct GNUNET_TIME_Absolute age; - - /** - * When was this connection last used? (by us sending or - * receiving a PAYLOAD message on it) - */ - struct GNUNET_TIME_Absolute last_use; - - /** - * How many packets that ought to generate an ACK did we send via - * this connection? - */ - unsigned long long num_acked_transmissions; - - /** - * Number of packets that were sent via this connection did actually - * receive an ACK? (Note: ACKs may be transmitted and lost via - * other connections, so this value should only be interpreted - * relative to @e num_acked_transmissions and in relation to other - * connections.) - */ - unsigned long long num_successes; - -}; - - -/** - * Obtain performance @a metrics from @a cc. - * - * @param cc connection to query - * @return the metrics - */ -const struct CadetConnectionMetrics * -GCC_get_metrics (struct CadetConnection *cc); - - -/** - * Handle encrypted message. - * - * @param cc connection that received encrypted message - * @param msg the encrypted message to decrypt - */ -void -GCC_handle_encrypted (struct CadetConnection *cc, - const struct GNUNET_CADET_TunnelEncryptedMessage *msg); - - -/** - * We sent a message for which we expect to receive an ACK via - * the connection identified by @a cti. - * - * @param cid connection identifier where we expect an ACK - */ -void -GCC_ack_expected (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid); - - -/** - * We observed an ACK for a message that was originally sent via - * the connection identified by @a cti. - * - * @param cid connection identifier where we got an ACK for a message - * that was originally sent via this connection (the ACK - * may have gotten back to us via a different connection). - */ -void -GCC_ack_observed (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid); - - -/** - * We observed some the given @a latency on the connection - * identified by @a cti. (The same connection was taken - * in both directions.) - * - * @param cti connection identifier where we measured latency - * @param latency the observed latency - */ -void -GCC_latency_observed (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti, - struct GNUNET_TIME_Relative latency); - - -/** - * Return the tunnel associated with this connection. - * - * @param cc connection to query - * @return corresponding entry in the tunnel's connection list - */ -struct CadetTConnection * -GCC_get_ct (struct CadetConnection *cc); - - -/** - * Obtain the path used by this connection. - * - * @param cc connection - * @return path to @a cc - */ -struct CadetPeerPath * -GCC_get_path (struct CadetConnection *cc); - - -/** - * Obtain unique ID for the connection. - * - * @param cc connection. - * @return unique number of the connection - */ -const struct GNUNET_CADET_ConnectionTunnelIdentifier * -GCC_get_id (struct CadetConnection *cc); - - -/** - * Get a (static) string for a connection. - * - * @param cc Connection. - */ -const char * -GCC_2s (const struct CadetConnection *cc); - - -/** - * Log connection info. - * - * @param cc connection - * @param level Debug level to use. - */ -void -GCC_debug (struct CadetConnection *cc, - enum GNUNET_ErrorType level); - - -#endif diff --git a/src/cadet/gnunet-service-cadet-new_dht.c b/src/cadet/gnunet-service-cadet-new_dht.c deleted file mode 100644 index 849562f237..0000000000 --- a/src/cadet/gnunet-service-cadet-new_dht.c +++ /dev/null @@ -1,351 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013, 2017 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file cadet/gnunet-service-cadet-new_dht.c - * @brief Information we track per peer. - * @author Bartlomiej Polot - * @author Christian Grothoff - */ - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_dht_service.h" -#include "gnunet_statistics_service.h" -#include "gnunet-service-cadet-new.h" -#include "gnunet-service-cadet-new_dht.h" -#include "gnunet-service-cadet-new_hello.h" -#include "gnunet-service-cadet-new_peer.h" -#include "gnunet-service-cadet-new_paths.h" - -/** - * How long do we wait before first announcing our presence to the DHT. - * Used to wait for our HELLO to be available. Note that we also get - * notifications when our HELLO is ready, so this is just the maximum - * we wait for the first notification. - */ -#define STARTUP_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500) - -/** - * How long do we wait after we get an updated HELLO before publishing? - * Allows for the HELLO to be updated again quickly, for example in - * case multiple addresses changed and we got a partial update. - */ -#define CHANGE_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100) - - -#define LOG(level, ...) GNUNET_log_from (level,"cadet-dht",__VA_ARGS__) - - -/** - * Handle for DHT searches. - */ -struct GCD_search_handle -{ - /** - * DHT_GET handle. - */ - struct GNUNET_DHT_GetHandle *dhtget; - -}; - - -/** - * Handle to use DHT. - */ -static struct GNUNET_DHT_Handle *dht_handle; - -/** - * How often to PUT own ID in the DHT. - */ -static struct GNUNET_TIME_Relative id_announce_time; - -/** - * DHT replication level, see DHT API: #GNUNET_DHT_get_start(), #GNUNET_DHT_put(). - */ -static unsigned long long dht_replication_level; - -/** - * Task to periodically announce itself in the network. - */ -static struct GNUNET_SCHEDULER_Task *announce_id_task; - -/** - * Delay for the next ID announce. - */ -static struct GNUNET_TIME_Relative announce_delay; - - -/** - * 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 @a get_path - * @param put_path path of the put request - * @param put_path_length length of the @a 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_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) -{ - const struct GNUNET_HELLO_Message *hello = data; - struct CadetPeer *peer; - - GCPP_try_path_from_dht (get_path, - get_path_length, - put_path, - put_path_length); - if ( (size >= sizeof (struct GNUNET_HELLO_Message)) && - (ntohs (hello->header.size) == size) && - (size == GNUNET_HELLO_size (hello)) ) - { - peer = GCP_get (&put_path[0], - GNUNET_YES); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got HELLO for %s\n", - GCP_2s (peer)); - GCP_set_hello (peer, - hello); - } -} - - -/** - * Periodically announce self id in the DHT - * - * @param cls closure - */ -static void -announce_id (void *cls) -{ - struct GNUNET_HashCode phash; - const struct GNUNET_HELLO_Message *hello; - size_t size; - struct GNUNET_TIME_Absolute expiration; - struct GNUNET_TIME_Relative next_put; - - hello = GCH_get_mine (); - size = (NULL != hello) ? GNUNET_HELLO_size (hello) : 0; - if (0 == size) - { - expiration = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), - announce_delay); - announce_delay = GNUNET_TIME_STD_BACKOFF (announce_delay); - } - else - { - expiration = GNUNET_HELLO_get_last_expiration (hello); - announce_delay = GNUNET_TIME_UNIT_SECONDS; - } - - /* Call again in id_announce_time, unless HELLO expires first, - * but wait at least 1s. */ - next_put - = GNUNET_TIME_absolute_get_remaining (expiration); - next_put - = GNUNET_TIME_relative_min (next_put, - id_announce_time); - next_put - = GNUNET_TIME_relative_max (next_put, - GNUNET_TIME_UNIT_SECONDS); - announce_id_task - = GNUNET_SCHEDULER_add_delayed (next_put, - &announce_id, - cls); - GNUNET_STATISTICS_update (stats, - "# DHT announce", - 1, - GNUNET_NO); - memset (&phash, - 0, - sizeof (phash)); - GNUNET_memcpy (&phash, - &my_full_id, - sizeof (my_full_id)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Announcing my HELLO (%u bytes) in the DHT\n", - size); - GNUNET_DHT_put (dht_handle, /* DHT handle */ - &phash, /* Key to use */ - dht_replication_level, /* Replication level */ - GNUNET_DHT_RO_RECORD_ROUTE - | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, /* DHT options */ - GNUNET_BLOCK_TYPE_DHT_HELLO, /* Block type */ - size, /* Size of the data */ - (const char *) hello, /* Data itself */ - expiration, /* Data expiration */ - NULL, /* Continuation */ - NULL); /* Continuation closure */ -} - - -/** - * Function called by the HELLO subsystem whenever OUR hello - * changes. Re-triggers the DHT PUT immediately. - */ -void -GCD_hello_update () -{ - if (NULL == announce_id_task) - return; /* too early */ - GNUNET_SCHEDULER_cancel (announce_id_task); - announce_id_task - = GNUNET_SCHEDULER_add_delayed (CHANGE_DELAY, - &announce_id, - NULL); -} - - -/** - * Initialize the DHT subsystem. - * - * @param c Configuration. - */ -void -GCD_init (const struct GNUNET_CONFIGURATION_Handle *c) -{ - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (c, - "CADET", - "DHT_REPLICATION_LEVEL", - &dht_replication_level)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, - "CADET", - "DHT_REPLICATION_LEVEL", - "USING DEFAULT"); - dht_replication_level = 3; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (c, - "CADET", - "ID_ANNOUNCE_TIME", - &id_announce_time)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "CADET", - "ID_ANNOUNCE_TIME", - "MISSING"); - GNUNET_SCHEDULER_shutdown (); - return; - } - - dht_handle = GNUNET_DHT_connect (c, - 64); - GNUNET_break (NULL != dht_handle); - announce_delay = GNUNET_TIME_UNIT_SECONDS; - announce_id_task = GNUNET_SCHEDULER_add_delayed (STARTUP_DELAY, - &announce_id, - NULL); -} - - -/** - * Shut down the DHT subsystem. - */ -void -GCD_shutdown (void) -{ - if (NULL != dht_handle) - { - GNUNET_DHT_disconnect (dht_handle); - dht_handle = NULL; - } - if (NULL != announce_id_task) - { - GNUNET_SCHEDULER_cancel (announce_id_task); - announce_id_task = NULL; - } -} - - -/** - * Search DHT for paths to @a peeR_id - * - * @param peer_id peer to search for - * @return handle to abort search - */ -struct GCD_search_handle * -GCD_search (const struct GNUNET_PeerIdentity *peer_id) -{ - struct GNUNET_HashCode phash; - struct GCD_search_handle *h; - - GNUNET_STATISTICS_update (stats, - "# DHT search", - 1, - GNUNET_NO); - memset (&phash, - 0, - sizeof (phash)); - GNUNET_memcpy (&phash, - peer_id, - sizeof (*peer_id)); - - h = GNUNET_new (struct GCD_search_handle); - h->dhtget = GNUNET_DHT_get_start (dht_handle, /* handle */ - GNUNET_BLOCK_TYPE_DHT_HELLO, /* type */ - &phash, /* key to search */ - dht_replication_level, /* replication level */ - GNUNET_DHT_RO_RECORD_ROUTE | - GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, - NULL, /* xquery */ - 0, /* xquery bits */ - &dht_get_id_handler, - h); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Starting DHT GET for peer %s (%p)\n", - GNUNET_i2s (peer_id), - h); - return h; -} - - -/** - * Stop DHT search started with #GCD_search(). - * - * @param h handle to search to stop - */ -void -GCD_search_stop (struct GCD_search_handle *h) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Stopping DHT GET %p\n", - h); - GNUNET_DHT_get_stop (h->dhtget); - GNUNET_free (h); -} - -/* end of gnunet-service-cadet_dht.c */ diff --git a/src/cadet/gnunet-service-cadet-new_dht.h b/src/cadet/gnunet-service-cadet-new_dht.h deleted file mode 100644 index 5d7ab29a06..0000000000 --- a/src/cadet/gnunet-service-cadet-new_dht.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013, 2017 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file cadet/gnunet-service-cadet_dht.h - * @brief cadet service; dealing with DHT requests and results - * @author Bartlomiej Polot - * @author Christian Grothoff - * - * All functions in this file should use the prefix GCD (Gnunet Cadet Dht) - */ -#ifndef GNUNET_SERVICE_CADET_DHT_H -#define GNUNET_SERVICE_CADET_DHT_H - -#ifdef __cplusplus -extern "C" -{ -#if 0 /* keep Emacsens' auto-indent happy */ -} -#endif -#endif - -#include "platform.h" -#include "gnunet_util_lib.h" - -/** - * Handle for DHT search operation. - */ -struct GCD_search_handle; - - -/** - * Initialize the DHT subsystem. - * - * @param c Configuration. - */ -void -GCD_init (const struct GNUNET_CONFIGURATION_Handle *c); - - -/** - * Shut down the DHT subsystem. - */ -void -GCD_shutdown (void); - - -/** - * Function called by the HELLO subsystem whenever OUR hello - * changes. Re-triggers the DHT PUT immediately. - */ -void -GCD_hello_update (void); - -/** - * Search DHT for paths to @a peeR_id - * - * @param peer_id peer to search for - * @return handle to abort search - */ -struct GCD_search_handle * -GCD_search (const struct GNUNET_PeerIdentity *peer_id); - - -/** - * Stop DHT search started with #GCD_search(). - * - * @param h handle to search to stop - */ -void -GCD_search_stop (struct GCD_search_handle *h); - - -#if 0 /* keep Emacsens' auto-indent happy */ -{ -#endif -#ifdef __cplusplus -} -#endif - -/* ifndef GNUNET_CADET_SERVICE_DHT_H */ -#endif -/* end of gnunet-service-cadet_dht.h */ diff --git a/src/cadet/gnunet-service-cadet-new_hello.c b/src/cadet/gnunet-service-cadet-new_hello.c deleted file mode 100644 index a24325adac..0000000000 --- a/src/cadet/gnunet-service-cadet-new_hello.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2014, 2017 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file cadet/gnunet-service-cadet-new_hello.c - * @brief spread knowledge about how to contact other peers from PEERINFO - * @author Bartlomiej Polot - * @author Christian Grothoff - * - * TODO: - * - is most of this necessary/helpful? - * - should we not simply restrict this to OUR hello? - */ -#include "platform.h" -#include "gnunet_util_lib.h" - -#include "gnunet_statistics_service.h" -#include "gnunet_peerinfo_service.h" -#include "cadet_protocol.h" -#include "gnunet-service-cadet-new.h" -#include "gnunet-service-cadet-new_dht.h" -#include "gnunet-service-cadet-new_hello.h" -#include "gnunet-service-cadet-new_peer.h" - -#define LOG(level, ...) GNUNET_log_from(level,"cadet-hll",__VA_ARGS__) - -/** - * Hello message of local peer. - */ -static struct GNUNET_HELLO_Message *mine; - -/** - * Handle to peerinfo service. - */ -static struct GNUNET_PEERINFO_Handle *peerinfo; - -/** - * Iterator context. - */ -static struct GNUNET_PEERINFO_NotifyContext *nc; - - -/** - * Process each hello message received from peerinfo. - * - * @param cls Closure (unused). - * @param peer Identity of the peer. - * @param hello Hello of the peer. - * @param err_msg Error message. - */ -static void -got_hello (void *cls, - const struct GNUNET_PeerIdentity *id, - const struct GNUNET_HELLO_Message *hello, - const char *err_msg) -{ - struct CadetPeer *peer; - - if ( (NULL == id) || - (NULL == hello) ) - return; - if (0 == memcmp (id, - &my_full_id, - sizeof (struct GNUNET_PeerIdentity))) - { - GNUNET_free_non_null (mine); - mine = (struct GNUNET_HELLO_Message *) GNUNET_copy_message (&hello->header); - GCD_hello_update (); - return; - } - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Hello for %s (%d bytes), expires on %s\n", - GNUNET_i2s (id), - GNUNET_HELLO_size (hello), - GNUNET_STRINGS_absolute_time_to_string (GNUNET_HELLO_get_last_expiration (hello))); - peer = GCP_get (id, - GNUNET_YES); - GCP_set_hello (peer, - hello); -} - - -/** - * Initialize the hello subsystem. - * - * @param c Configuration. - */ -void -GCH_init (const struct GNUNET_CONFIGURATION_Handle *c) -{ - GNUNET_assert (NULL == nc); - peerinfo = GNUNET_PEERINFO_connect (c); - nc = GNUNET_PEERINFO_notify (c, - GNUNET_NO, - &got_hello, - NULL); -} - - -/** - * Shut down the hello subsystem. - */ -void -GCH_shutdown () -{ - if (NULL != nc) - { - GNUNET_PEERINFO_notify_cancel (nc); - nc = NULL; - } - if (NULL != peerinfo) - { - GNUNET_PEERINFO_disconnect (peerinfo); - peerinfo = NULL; - } - if (NULL != mine) - { - GNUNET_free (mine); - mine = NULL; - } -} - - -/** - * Get own hello message. - * - * @return Own hello message. - */ -const struct GNUNET_HELLO_Message * -GCH_get_mine (void) -{ - return mine; -} - -/* end of gnunet-service-cadet-new_hello.c */ diff --git a/src/cadet/gnunet-service-cadet-new_hello.h b/src/cadet/gnunet-service-cadet-new_hello.h deleted file mode 100644 index 4291ae985d..0000000000 --- a/src/cadet/gnunet-service-cadet-new_hello.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2014, 2017 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file cadet/gnunet-service-cadet_hello.h - * @brief cadet service; dealing with hello messages - * @author Bartlomiej Polot - * @author Christian Grothoff - * - * All functions in this file should use the prefix GCH (Gnunet Cadet Hello) - */ - -#ifndef GNUNET_SERVICE_CADET_HELLO_H -#define GNUNET_SERVICE_CADET_HELLO_H - -#ifdef __cplusplus -extern "C" -{ -#if 0 /* keep Emacsens' auto-indent happy */ -} -#endif -#endif - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_hello_lib.h" - - -/** - * Initialize the hello subsystem. - * - * @param c Configuration. - */ -void -GCH_init (const struct GNUNET_CONFIGURATION_Handle *c); - - -/** - * Shut down the hello subsystem. - */ -void -GCH_shutdown (void); - - -/** - * Get own hello message. - * - * @return Own hello message. - */ -const struct GNUNET_HELLO_Message * -GCH_get_mine (void); - - -#if 0 /* keep Emacsens' auto-indent happy */ -{ -#endif -#ifdef __cplusplus -} -#endif - -/* ifndef GNUNET_CADET_SERVICE_HELLO_H */ -#endif -/* end of gnunet-cadet-service_hello.h */ diff --git a/src/cadet/gnunet-service-cadet-new_peer.c b/src/cadet/gnunet-service-cadet-new_peer.c deleted file mode 100644 index 29aef6895a..0000000000 --- a/src/cadet/gnunet-service-cadet-new_peer.c +++ /dev/null @@ -1,1478 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2001-2017 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file cadet/gnunet-service-cadet-new_peer.c - * @brief Information we track per peer. - * @author Bartlomiej Polot - * @author Christian Grothoff - * - * TODO: - * - optimize stopping/restarting DHT search to situations - * where we actually need it (i.e. not if we have a direct connection, - * or if we already have plenty of good short ones, or maybe even - * to take a break if we have some connections and have searched a lot (?)) - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_hello_lib.h" -#include "gnunet_signatures.h" -#include "gnunet_transport_service.h" -#include "gnunet_ats_service.h" -#include "gnunet_core_service.h" -#include "gnunet_statistics_service.h" -#include "cadet_protocol.h" -#include "gnunet-service-cadet-new.h" -#include "gnunet-service-cadet-new_connection.h" -#include "gnunet-service-cadet-new_dht.h" -#include "gnunet-service-cadet-new_peer.h" -#include "gnunet-service-cadet-new_paths.h" -#include "gnunet-service-cadet-new_tunnels.h" - - -#define LOG(level, ...) GNUNET_log_from(level,"cadet-per",__VA_ARGS__) - - -/** - * How long do we wait until tearing down an idle peer? - */ -#define IDLE_PEER_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 5) - -/** - * How long do we keep paths around if we no longer care about the peer? - */ -#define IDLE_PATH_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 2) - - - - -/** - * Data structure used to track whom we have to notify about changes - * to our message queue. - */ -struct GCP_MessageQueueManager -{ - - /** - * Kept in a DLL. - */ - struct GCP_MessageQueueManager *next; - - /** - * Kept in a DLL. - */ - struct GCP_MessageQueueManager *prev; - - /** - * Function to call with updated message queue object. - */ - GCP_MessageQueueNotificationCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * The peer this is for. - */ - struct CadetPeer *cp; - - /** - * Envelope this manager would like to transmit once it is its turn. - */ - struct GNUNET_MQ_Envelope *env; - -}; - - -/** - * Struct containing all information regarding a given peer - */ -struct CadetPeer -{ - /** - * ID of the peer - */ - struct GNUNET_PeerIdentity pid; - - /** - * Last time we heard from this peer (currently not used!) - */ - struct GNUNET_TIME_Absolute last_contactXXX; - - /** - * Array of DLLs of paths traversing the peer, organized by the - * offset of the peer on the larger path. - */ - struct CadetPeerPathEntry **path_heads; - - /** - * Array of DLL of paths traversing the peer, organized by the - * offset of the peer on the larger path. - */ - struct CadetPeerPathEntry **path_tails; - - /** - * Notifications to call when @e core_mq changes. - */ - struct GCP_MessageQueueManager *mqm_head; - - /** - * Notifications to call when @e core_mq changes. - */ - struct GCP_MessageQueueManager *mqm_tail; - - /** - * Pointer to first "ready" entry in @e mqm_head. - */ - struct GCP_MessageQueueManager *mqm_ready_ptr; - - /** - * MIN-heap of paths owned by this peer (they also end at this - * peer). Ordered by desirability. - */ - struct GNUNET_CONTAINER_Heap *path_heap; - - /** - * Handle to stop the DHT search for paths to this peer - */ - struct GCD_search_handle *search_h; - - /** - * Task to clean up @e path_heap asynchronously. - */ - struct GNUNET_SCHEDULER_Task *heap_cleanup_task; - - /** - * Task to destroy this entry. - */ - struct GNUNET_SCHEDULER_Task *destroy_task; - - /** - * Tunnel to this peer, if any. - */ - struct CadetTunnel *t; - - /** - * Connections that go through this peer; indexed by tid. - */ - struct GNUNET_CONTAINER_MultiShortmap *connections; - - /** - * Handle for core transmissions. - */ - struct GNUNET_MQ_Handle *core_mq; - - /** - * Hello message of the peer. - */ - struct GNUNET_HELLO_Message *hello; - - /** - * Handle to us offering the HELLO to the transport. - */ - struct GNUNET_TRANSPORT_OfferHelloHandle *hello_offer; - - /** - * Handle to our ATS request asking ATS to suggest an address - * to TRANSPORT for this peer (to establish a direct link). - */ - struct GNUNET_ATS_ConnectivitySuggestHandle *connectivity_suggestion; - - /** - * How many messages are in the queue to this peer. - */ - unsigned int queue_n; - - /** - * How many paths do we have to this peer (in all @e path_heads DLLs combined). - */ - unsigned int num_paths; - - /** - * Sum over all of the offsets of all of the paths in the @a path_heads DLLs. - * Used to speed-up @GCP_get_desirability_of_path() calculation. - */ - unsigned int off_sum; - - /** - * Number of message queue managers of this peer that have a message in waiting. - * - * Used to quickly see if we need to bother scanning the @e msm_head DLL. - * TODO: could be replaced by another DLL that would then allow us to avoid - * the O(n)-scan of the DLL for ready entries! - */ - unsigned int mqm_ready_counter; - - /** - * Current length of the @e path_heads and @path_tails arrays. - * The arrays should be grown as needed. - */ - unsigned int path_dll_length; - -}; - - -/** - * Get the static string for a peer ID. - * - * @param cp Peer. - * @return Static string for it's ID. - */ -const char * -GCP_2s (const struct CadetPeer *cp) -{ - static char buf[32]; - - GNUNET_snprintf (buf, - sizeof (buf), - "P(%s)", - GNUNET_i2s (&cp->pid)); - return buf; -} - - -/** - * Calculate how desirable a path is for @a cp if @a cp - * is at offset @a off. - * - * The 'desirability_table.c' program can be used to compute a list of - * sample outputs for different scenarios. Basically, we score paths - * lower if there are many alternatives, and higher if they are - * shorter than average, and very high if they are much shorter than - * average and without many alternatives. - * - * @param cp a peer reachable via a path - * @param off offset of @a cp in the path - * @return score how useful a path is to reach @a cp, - * positive scores mean path is more desirable - */ -double -GCP_get_desirability_of_path (struct CadetPeer *cp, - unsigned int off) -{ - unsigned int num_alts = cp->num_paths; - unsigned int off_sum; - double avg_sum; - double path_delta; - double weight_alts; - - GNUNET_assert (num_alts >= 1); /* 'path' should be in there! */ - GNUNET_assert (0 != cp->path_dll_length); - - /* We maintain 'off_sum' in 'peer' and thereby - avoid the SLOW recalculation each time. Kept here - just to document what is going on. */ -#if SLOW - off_sum = 0; - for (unsigned int j=0;j<cp->path_dll_length;j++) - for (struct CadetPeerPathEntry *pe = cp->path_heads[j]; - NULL != pe; - pe = pe->next) - off_sum += j; - GNUNET_assert (off_sum == cp->off_sum); -#else - off_sum = cp->off_sum; -#endif - avg_sum = off_sum * 1.0 / cp->path_dll_length; - path_delta = off - avg_sum; - /* path_delta positiv: path off of peer above average (bad path for peer), - path_delta negativ: path off of peer below average (good path for peer) */ - if (path_delta <= - 1.0) - weight_alts = - num_alts / path_delta; /* discount alternative paths */ - else if (path_delta >= 1.0) - weight_alts = num_alts * path_delta; /* overcount alternative paths */ - else - weight_alts = num_alts; /* count alternative paths normally */ - - - /* off+1: long paths are generally harder to find and thus count - a bit more as they get longer. However, above-average paths - still need to count less, hence the squaring of that factor. */ - return (off + 1.0) / (weight_alts * weight_alts); -} - - -/** - * This peer is no longer be needed, clean it up now. - * - * @param cls peer to clean up - */ -static void -destroy_peer (void *cls) -{ - struct CadetPeer *cp = cls; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Destroying state about peer %s\n", - GCP_2s (cp)); - cp->destroy_task = NULL; - GNUNET_assert (NULL == cp->t); - GNUNET_assert (NULL == cp->core_mq); - GNUNET_assert (0 == cp->num_paths); - for (unsigned int i=0;i<cp->path_dll_length;i++) - GNUNET_assert (NULL == cp->path_heads[i]); - GNUNET_assert (0 == GNUNET_CONTAINER_multishortmap_size (cp->connections)); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multipeermap_remove (peers, - &cp->pid, - cp)); - GNUNET_free_non_null (cp->path_heads); - GNUNET_free_non_null (cp->path_tails); - cp->path_dll_length = 0; - if (NULL != cp->search_h) - { - GCD_search_stop (cp->search_h); - cp->search_h = NULL; - } - /* FIXME: clean up search_delayedXXX! */ - - if (NULL != cp->hello_offer) - { - GNUNET_TRANSPORT_offer_hello_cancel (cp->hello_offer); - cp->hello_offer = NULL; - } - if (NULL != cp->connectivity_suggestion) - { - GNUNET_ATS_connectivity_suggest_cancel (cp->connectivity_suggestion); - cp->connectivity_suggestion = NULL; - } - GNUNET_CONTAINER_multishortmap_destroy (cp->connections); - if (NULL != cp->path_heap) - { - GNUNET_CONTAINER_heap_destroy (cp->path_heap); - cp->path_heap = NULL; - } - if (NULL != cp->heap_cleanup_task) - { - GNUNET_SCHEDULER_cancel (cp->heap_cleanup_task); - cp->heap_cleanup_task = NULL; - } - GNUNET_free_non_null (cp->hello); - /* Peer should not be freed if paths exist; if there are no paths, - there ought to be no connections, and without connections, no - notifications. Thus we can assert that mqm_head is empty at this - point. */ - GNUNET_assert (NULL == cp->mqm_head); - GNUNET_assert (NULL == cp->mqm_ready_ptr); - GNUNET_free (cp); -} - - -/** - * This peer is now on more "active" duty, activate processes related to it. - * - * @param cp the more-active peer - */ -static void -consider_peer_activate (struct CadetPeer *cp) -{ - uint32_t strength; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Updating peer %s activation state (%u connections)%s%s\n", - GCP_2s (cp), - GNUNET_CONTAINER_multishortmap_size (cp->connections), - (NULL == cp->t) ? "" : " with tunnel", - (NULL == cp->core_mq) ? "" : " with CORE link"); - if (NULL != cp->destroy_task) - { - /* It's active, do not destory! */ - GNUNET_SCHEDULER_cancel (cp->destroy_task); - cp->destroy_task = NULL; - } - if ( (0 == GNUNET_CONTAINER_multishortmap_size (cp->connections)) && - (NULL == cp->t) ) - { - /* We're just on a path or directly connected; don't bother too much */ - if (NULL != cp->connectivity_suggestion) - { - GNUNET_ATS_connectivity_suggest_cancel (cp->connectivity_suggestion); - cp->connectivity_suggestion = NULL; - } - if (NULL != cp->search_h) - { - GCD_search_stop (cp->search_h); - cp->search_h = NULL; - } - return; - } - if (NULL == cp->core_mq) - { - /* Lacks direct connection, try to create one by querying the DHT */ - if ( (NULL == cp->search_h) && - (DESIRED_CONNECTIONS_PER_TUNNEL > cp->num_paths) ) - cp->search_h - = GCD_search (&cp->pid); - } - else - { - /* Have direct connection, stop DHT search if active */ - if (NULL != cp->search_h) - { - GCD_search_stop (cp->search_h); - cp->search_h = NULL; - } - } - - /* If we have a tunnel, our urge for connections is much bigger */ - strength = (NULL != cp->t) ? 32 : 1; - if (NULL != cp->connectivity_suggestion) - GNUNET_ATS_connectivity_suggest_cancel (cp->connectivity_suggestion); - cp->connectivity_suggestion - = GNUNET_ATS_connectivity_suggest (ats_ch, - &cp->pid, - strength); -} - - -/** - * This peer may no longer be needed, consider cleaning it up. - * - * @param cp peer to clean up - */ -static void -consider_peer_destroy (struct CadetPeer *cp); - - -/** - * We really no longere care about a peer, stop hogging memory with paths to it. - * Afterwards, see if there is more to be cleaned up about this peer. - * - * @param cls a `struct CadetPeer`. - */ -static void -drop_paths (void *cls) -{ - struct CadetPeer *cp = cls; - struct CadetPeerPath *path; - - cp->destroy_task = NULL; - while (NULL != (path = GNUNET_CONTAINER_heap_remove_root (cp->path_heap))) - GCPP_release (path); - consider_peer_destroy (cp); -} - - -/** - * This peer may no longer be needed, consider cleaning it up. - * - * @param cp peer to clean up - */ -static void -consider_peer_destroy (struct CadetPeer *cp) -{ - struct GNUNET_TIME_Relative exp; - - if (NULL != cp->destroy_task) - { - GNUNET_SCHEDULER_cancel (cp->destroy_task); - cp->destroy_task = NULL; - } - if (NULL != cp->t) - return; /* still relevant! */ - if (NULL != cp->core_mq) - return; /* still relevant! */ - if (0 != GNUNET_CONTAINER_multishortmap_size (cp->connections)) - return; /* still relevant! */ - if ( (NULL != cp->path_heap) && - (0 < GNUNET_CONTAINER_heap_get_size (cp->path_heap)) ) - { - cp->destroy_task = GNUNET_SCHEDULER_add_delayed (IDLE_PATH_TIMEOUT, - &drop_paths, - cp); - return; - } - if (0 != cp->num_paths) - return; /* still relevant! */ - if (NULL != cp->hello) - { - /* relevant only until HELLO expires */ - exp = GNUNET_TIME_absolute_get_remaining (GNUNET_HELLO_get_last_expiration (cp->hello)); - cp->destroy_task = GNUNET_SCHEDULER_add_delayed (exp, - &destroy_peer, - cp); - return; - } - cp->destroy_task = GNUNET_SCHEDULER_add_delayed (IDLE_PEER_TIMEOUT, - &destroy_peer, - cp); -} - - -/** - * Set the message queue to @a mq for peer @a cp and notify watchers. - * - * @param cp peer to modify - * @param mq message queue to set (can be NULL) - */ -void -GCP_set_mq (struct CadetPeer *cp, - struct GNUNET_MQ_Handle *mq) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Message queue for peer %s is now %p\n", - GCP_2s (cp), - mq); - cp->core_mq = mq; - for (struct GCP_MessageQueueManager *mqm = cp->mqm_head, *next; - NULL != mqm; - mqm = next) - { - /* Save next pointer in case mqm gets freed by the callback */ - next = mqm->next; - if (NULL == mq) - { - if (NULL != mqm->env) - { - GNUNET_MQ_discard (mqm->env); - mqm->env = NULL; - mqm->cb (mqm->cb_cls, - GNUNET_SYSERR); - } - else - { - mqm->cb (mqm->cb_cls, - GNUNET_NO); - } - } - else - { - GNUNET_assert (NULL == mqm->env); - mqm->cb (mqm->cb_cls, - GNUNET_YES); - } - } - if ( (NULL != mq) || - (NULL != cp->t) ) - consider_peer_activate (cp); - else - consider_peer_destroy (cp); - - if ( (NULL != mq) && - (NULL != cp->t) ) - { - /* have a new, direct path to the target, notify tunnel */ - struct CadetPeerPath *path; - - path = GCPP_get_path_from_route (1, - &cp->pid); - GCT_consider_path (cp->t, - path, - 0); - } -} - - -/** - * Debug function should NEVER return true in production code, useful to - * simulate losses for testcases. - * - * @return #GNUNET_YES or #GNUNET_NO with the decision to drop. - */ -static int -should_I_drop (void) -{ - if (0 == drop_percent) - return GNUNET_NO; - if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, - 101) < drop_percent) - return GNUNET_YES; - return GNUNET_NO; -} - - -/** - * Function called when CORE took one of the messages from - * a message queue manager and transmitted it. - * - * @param cls the `struct CadetPeeer` where we made progress - */ -static void -mqm_send_done (void *cls); - - -/** - * Transmit current envelope from this @a mqm. - * - * @param mqm mqm to transmit message for now - */ -static void -mqm_execute (struct GCP_MessageQueueManager *mqm) -{ - struct CadetPeer *cp = mqm->cp; - - /* Move ready pointer to the next entry that might be ready. */ - if ( (mqm == cp->mqm_ready_ptr) && - (NULL != mqm->next) ) - cp->mqm_ready_ptr = mqm->next; - /* Move entry to the end of the DLL, to be fair. */ - if (mqm != cp->mqm_tail) - { - GNUNET_CONTAINER_DLL_remove (cp->mqm_head, - cp->mqm_tail, - mqm); - GNUNET_CONTAINER_DLL_insert_tail (cp->mqm_head, - cp->mqm_tail, - mqm); - } - cp->mqm_ready_counter--; - if (GNUNET_YES == should_I_drop ()) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "DROPPING message to peer %s from MQM %p\n", - GCP_2s (cp), - mqm); - GNUNET_MQ_discard (mqm->env); - mqm->env = NULL; - mqm_send_done (cp); - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending to peer %s from MQM %p\n", - GCP_2s (cp), - mqm); - GNUNET_MQ_send (cp->core_mq, - mqm->env); - mqm->env = NULL; - } - mqm->cb (mqm->cb_cls, - GNUNET_YES); -} - - -/** - * Find the next ready message in the queue (starting - * the search from the `cp->mqm_ready_ptr`) and if possible - * execute the transmission. - * - * @param cp peer to try to send the next ready message to - */ -static void -send_next_ready (struct CadetPeer *cp) -{ - struct GCP_MessageQueueManager *mqm; - - if (0 == cp->mqm_ready_counter) - return; - while ( (NULL != (mqm = cp->mqm_ready_ptr)) && - (NULL == mqm->env) ) - cp->mqm_ready_ptr = mqm->next; - if (NULL == mqm) - return; /* nothing to do */ - mqm_execute (mqm); -} - - -/** - * Function called when CORE took one of the messages from - * a message queue manager and transmitted it. - * - * @param cls the `struct CadetPeeer` where we made progress - */ -static void -mqm_send_done (void *cls) -{ - struct CadetPeer *cp = cls; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending to peer %s completed\n", - GCP_2s (cp)); - send_next_ready (cp); -} - - -/** - * Send the message in @a env to @a cp. - * - * @param mqm the message queue manager to use for transmission - * @param env envelope with the message to send; must NOT - * yet have a #GNUNET_MQ_notify_sent() callback attached to it - */ -void -GCP_send (struct GCP_MessageQueueManager *mqm, - struct GNUNET_MQ_Envelope *env) -{ - struct CadetPeer *cp = mqm->cp; - - GNUNET_assert (NULL != env); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Queueing message to peer %s in MQM %p\n", - GCP_2s (cp), - mqm); - GNUNET_assert (NULL != cp->core_mq); - GNUNET_assert (NULL == mqm->env); - GNUNET_MQ_notify_sent (env, - &mqm_send_done, - cp); - mqm->env = env; - cp->mqm_ready_counter++; - if (mqm != cp->mqm_ready_ptr) - cp->mqm_ready_ptr = cp->mqm_head; - if (1 == cp->mqm_ready_counter) - cp->mqm_ready_ptr = mqm; - if (0 != GNUNET_MQ_get_length (cp->core_mq)) - return; - send_next_ready (cp); -} - - -/** - * Function called to destroy a peer now. - * - * @param cls NULL - * @param pid identity of the peer (unused) - * @param value the `struct CadetPeer` to clean up - * @return #GNUNET_OK (continue to iterate) - */ -static int -destroy_iterator_cb (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct CadetPeer *cp = value; - - if (NULL != cp->destroy_task) - { - GNUNET_SCHEDULER_cancel (cp->destroy_task); - cp->destroy_task = NULL; - } - destroy_peer (cp); - return GNUNET_OK; -} - - -/** - * Clean up all entries about all peers. - * Must only be called after all tunnels, CORE-connections and - * connections are down. - */ -void -GCP_destroy_all_peers () -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Destroying all peers now\n"); - GNUNET_CONTAINER_multipeermap_iterate (peers, - &destroy_iterator_cb, - NULL); -} - - -/** - * Drop all paths owned by this peer, and do not - * allow new ones to be added: We are shutting down. - * - * @param cp peer to drop paths to - */ -void -GCP_drop_owned_paths (struct CadetPeer *cp) -{ - struct CadetPeerPath *path; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Destroying all paths to %s\n", - GCP_2s (cp)); - while (NULL != (path = - GNUNET_CONTAINER_heap_remove_root (cp->path_heap))) - GCPP_release (path); - GNUNET_CONTAINER_heap_destroy (cp->path_heap); - cp->path_heap = NULL; -} - - -/** - * Add an entry to the DLL of all of the paths that this peer is on. - * - * @param cp peer to modify - * @param entry an entry on a path - * @param off offset of this peer on the path - */ -void -GCP_path_entry_add (struct CadetPeer *cp, - struct CadetPeerPathEntry *entry, - unsigned int off) -{ - GNUNET_assert (cp == GCPP_get_peer_at_offset (entry->path, - off)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Discovered that peer %s is on path %s at offset %u\n", - GCP_2s (cp), - GCPP_2s (entry->path), - off); - if (off >= cp->path_dll_length) - { - unsigned int len = cp->path_dll_length; - - GNUNET_array_grow (cp->path_heads, - len, - off + 4); - GNUNET_array_grow (cp->path_tails, - cp->path_dll_length, - off + 4); - } - GNUNET_CONTAINER_DLL_insert (cp->path_heads[off], - cp->path_tails[off], - entry); - cp->off_sum += off; - cp->num_paths++; - - /* If we have a tunnel to this peer, tell the tunnel that there is a - new path available. */ - if (NULL != cp->t) - GCT_consider_path (cp->t, - entry->path, - off); - - if ( (NULL != cp->search_h) && - (DESIRED_CONNECTIONS_PER_TUNNEL <= cp->num_paths) ) - { - /* Now I have enough paths, stop search */ - GCD_search_stop (cp->search_h); - cp->search_h = NULL; - } - if (NULL != cp->destroy_task) - { - /* paths changed, this resets the destroy timeout counter - and aborts a destroy task that may no longer be valid - to have (as we now have more paths via this peer). */ - consider_peer_destroy (cp); - } -} - - -/** - * Remove an entry from the DLL of all of the paths that this peer is on. - * - * @param cp peer to modify - * @param entry an entry on a path - * @param off offset of this peer on the path - */ -void -GCP_path_entry_remove (struct CadetPeer *cp, - struct CadetPeerPathEntry *entry, - unsigned int off) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Removing knowledge about peer %s beging on path %s at offset %u\n", - GCP_2s (cp), - GCPP_2s (entry->path), - off); - GNUNET_CONTAINER_DLL_remove (cp->path_heads[off], - cp->path_tails[off], - entry); - GNUNET_assert (0 < cp->num_paths); - cp->off_sum -= off; - cp->num_paths--; - if ( (NULL == cp->core_mq) && - (NULL != cp->t) && - (NULL == cp->search_h) && - (DESIRED_CONNECTIONS_PER_TUNNEL > cp->num_paths) ) - cp->search_h - = GCD_search (&cp->pid); - if (NULL == cp->destroy_task) - { - /* paths changed, we might now be ready for destruction, check again */ - consider_peer_destroy (cp); - } -} - - -/** - * Prune down the number of paths to this peer, we seem to - * have way too many. - * - * @param cls the `struct CadetPeer` to maintain the path heap for - */ -static void -path_heap_cleanup (void *cls) -{ - struct CadetPeer *cp = cls; - struct CadetPeerPath *root; - - cp->heap_cleanup_task = NULL; - while (GNUNET_CONTAINER_heap_get_size (cp->path_heap) >= - 2 * DESIRED_CONNECTIONS_PER_TUNNEL) - { - /* Now we have way too many, drop least desirable UNLESS it is in use! - (Note that this intentionally keeps highly desireable, but currently - unused paths around in the hope that we might be able to switch, even - if the number of paths exceeds the threshold.) */ - root = GNUNET_CONTAINER_heap_peek (cp->path_heap); - GNUNET_assert (NULL != root); - if (NULL != - GCPP_get_connection (root, - cp, - GCPP_get_length (root) - 1)) - break; /* can't fix */ - /* Got plenty of paths to this destination, and this is a low-quality - one that we don't care about. Allow it to die. */ - GNUNET_assert (root == - GNUNET_CONTAINER_heap_remove_root (cp->path_heap)); - GCPP_release (root); - } -} - - -/** - * Try adding a @a path to this @a peer. If the peer already - * has plenty of paths, return NULL. - * - * @param cp peer to which the @a path leads to - * @param path a path looking for an owner; may not be fully initialized yet! - * @param off offset of @a cp in @a path - * @param force force attaching the path - * @return NULL if this peer does not care to become a new owner, - * otherwise the node in the peer's path heap for the @a path. - */ -struct GNUNET_CONTAINER_HeapNode * -GCP_attach_path (struct CadetPeer *cp, - struct CadetPeerPath *path, - unsigned int off, - int force) -{ - GNUNET_CONTAINER_HeapCostType desirability; - struct CadetPeerPath *root; - GNUNET_CONTAINER_HeapCostType root_desirability; - struct GNUNET_CONTAINER_HeapNode *hn; - - GNUNET_assert (off == GCPP_get_length (path) - 1); - GNUNET_assert (cp == GCPP_get_peer_at_offset (path, - off)); - if (NULL == cp->path_heap) - { - /* #GCP_drop_owned_paths() was already called, we cannot take new ones! */ - GNUNET_assert (GNUNET_NO == force); - return NULL; - } - desirability = GCPP_get_desirability (path); - if (GNUNET_NO == force) - { - /* FIXME: desirability is not yet initialized; tricky! */ - if (GNUNET_NO == - GNUNET_CONTAINER_heap_peek2 (cp->path_heap, - (void **) &root, - &root_desirability)) - { - root = NULL; - root_desirability = 0; - } - - if ( (DESIRED_CONNECTIONS_PER_TUNNEL > cp->num_paths) && - (desirability < root_desirability) ) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Decided to not attach path %p to peer %s due to undesirability\n", - GCPP_2s (path), - GCP_2s (cp)); - return NULL; - } - } - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Attaching path %s to peer %s (%s)\n", - GCPP_2s (path), - GCP_2s (cp), - (GNUNET_NO == force) ? "desirable" : "forced"); - - /* Yes, we'd like to add this path, add to our heap */ - hn = GNUNET_CONTAINER_heap_insert (cp->path_heap, - path, - desirability); - - /* Consider maybe dropping other paths because of the new one */ - if ( (GNUNET_CONTAINER_heap_get_size (cp->path_heap) >= - 2 * DESIRED_CONNECTIONS_PER_TUNNEL) && - (NULL != cp->heap_cleanup_task) ) - cp->heap_cleanup_task = GNUNET_SCHEDULER_add_now (&path_heap_cleanup, - cp); - return hn; -} - - -/** - * This peer can no longer own @a path as the path - * has been extended and a peer further down the line - * is now the new owner. - * - * @param cp old owner of the @a path - * @param path path where the ownership is lost - * @param hn note in @a cp's path heap that must be deleted - */ -void -GCP_detach_path (struct CadetPeer *cp, - struct CadetPeerPath *path, - struct GNUNET_CONTAINER_HeapNode *hn) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Detatching path %s from peer %s\n", - GCPP_2s (path), - GCP_2s (cp)); - GNUNET_assert (path == - GNUNET_CONTAINER_heap_remove_node (hn)); -} - - -/** - * Add a @a connection to this @a cp. - * - * @param cp peer via which the @a connection goes - * @param cc the connection to add - */ -void -GCP_add_connection (struct CadetPeer *cp, - struct CadetConnection *cc) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Adding connection %s to peer %s\n", - GCC_2s (cc), - GCP_2s (cp)); - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multishortmap_put (cp->connections, - &GCC_get_id (cc)->connection_of_tunnel, - cc, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - if (NULL != cp->destroy_task) - { - GNUNET_SCHEDULER_cancel (cp->destroy_task); - cp->destroy_task = NULL; - } -} - - -/** - * Remove a @a connection that went via this @a cp. - * - * @param cp peer via which the @a connection went - * @param cc the connection to remove - */ -void -GCP_remove_connection (struct CadetPeer *cp, - struct CadetConnection *cc) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Removing connection %s from peer %s\n", - GCC_2s (cc), - GCP_2s (cp)); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multishortmap_remove (cp->connections, - &GCC_get_id (cc)->connection_of_tunnel, - cc)); - consider_peer_destroy (cp); -} - - -/** - * Retrieve the CadetPeer stucture associated with the - * peer. Optionally create one and insert it in the appropriate - * structures if the peer is not known yet. - * - * @param peer_id Full identity of the peer. - * @param create #GNUNET_YES if a new peer should be created if unknown. - * #GNUNET_NO to return NULL if peer is unknown. - * @return Existing or newly created peer structure. - * NULL if unknown and not requested @a create - */ -struct CadetPeer * -GCP_get (const struct GNUNET_PeerIdentity *peer_id, - int create) -{ - struct CadetPeer *cp; - - cp = GNUNET_CONTAINER_multipeermap_get (peers, - peer_id); - if (NULL != cp) - return cp; - if (GNUNET_NO == create) - return NULL; - cp = GNUNET_new (struct CadetPeer); - cp->pid = *peer_id; - cp->connections = GNUNET_CONTAINER_multishortmap_create (32, - GNUNET_YES); - cp->path_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multipeermap_put (peers, - &cp->pid, - cp, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Creating peer %s\n", - GCP_2s (cp)); - return cp; -} - - -/** - * Obtain the peer identity for a `struct CadetPeer`. - * - * @param cp our peer handle - * @return the peer identity - */ -const struct GNUNET_PeerIdentity * -GCP_get_id (struct CadetPeer *cp) -{ - return &cp->pid; -} - - -/** - * Iterate over all known peers. - * - * @param iter Iterator. - * @param cls Closure for @c iter. - */ -void -GCP_iterate_all (GNUNET_CONTAINER_PeerMapIterator iter, - void *cls) -{ - GNUNET_CONTAINER_multipeermap_iterate (peers, - iter, - cls); -} - - -/** - * Count the number of known paths toward the peer. - * - * @param cp Peer to get path info. - * @return Number of known paths. - */ -unsigned int -GCP_count_paths (const struct CadetPeer *cp) -{ - return cp->num_paths; -} - - -/** - * Iterate over the paths to a peer. - * - * @param cp Peer to get path info. - * @param callback Function to call for every path. - * @param callback_cls Closure for @a callback. - * @return Number of iterated paths. - */ -unsigned int -GCP_iterate_paths (struct CadetPeer *cp, - GCP_PathIterator callback, - void *callback_cls) -{ - unsigned int ret = 0; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Iterating over paths to peer %s%s\n", - GCP_2s (cp), - (NULL == cp->core_mq) ? "" : " including direct link"); - if (NULL != cp->core_mq) - { - struct CadetPeerPath *path; - - path = GCPP_get_path_from_route (1, - &cp->pid); - ret++; - if (GNUNET_NO == - callback (callback_cls, - path, - 0)) - return ret; - } - for (unsigned int i=0;i<cp->path_dll_length;i++) - { - for (struct CadetPeerPathEntry *pe = cp->path_heads[i]; - NULL != pe; - pe = pe->next) - { - ret++; - if (GNUNET_NO == - callback (callback_cls, - pe->path, - i)) - return ret; - } - } - return ret; -} - - -/** - * Iterate over the paths to @a cp where - * @a cp is at distance @a dist from us. - * - * @param cp Peer to get path info. - * @param dist desired distance of @a cp to us on the path - * @param callback Function to call for every path. - * @param callback_cls Closure for @a callback. - * @return Number of iterated paths. - */ -unsigned int -GCP_iterate_paths_at (struct CadetPeer *cp, - unsigned int dist, - GCP_PathIterator callback, - void *callback_cls) -{ - unsigned int ret = 0; - - if (dist >= cp->path_dll_length) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Asked to look for paths at distance %u, but maximum for me is < %u\n", - dist, - cp->path_dll_length); - return 0; - } - for (struct CadetPeerPathEntry *pe = cp->path_heads[dist]; - NULL != pe; - pe = pe->next) - { - if (GNUNET_NO == - callback (callback_cls, - pe->path, - dist)) - return ret; - ret++; - } - return ret; -} - - -/** - * Get the tunnel towards a peer. - * - * @param cp Peer to get from. - * @param create #GNUNET_YES to create a tunnel if we do not have one - * @return Tunnel towards peer. - */ -struct CadetTunnel * -GCP_get_tunnel (struct CadetPeer *cp, - int create) -{ - if (NULL == cp) - return NULL; - if ( (NULL != cp->t) || - (GNUNET_NO == create) ) - return cp->t; - cp->t = GCT_create_tunnel (cp); - consider_peer_activate (cp); - return cp->t; -} - - -/** - * Hello offer was passed to the transport service. Mark it - * as done. - * - * @param cls the `struct CadetPeer` where the offer completed - */ -static void -hello_offer_done (void *cls) -{ - struct CadetPeer *cp = cls; - - cp->hello_offer = NULL; -} - - -/** - * We got a HELLO for a @a peer, remember it, and possibly - * trigger adequate actions (like trying to connect). - * - * @param cp the peer we got a HELLO for - * @param hello the HELLO to remember - */ -void -GCP_set_hello (struct CadetPeer *cp, - const struct GNUNET_HELLO_Message *hello) -{ - struct GNUNET_HELLO_Message *mrg; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got %u byte HELLO for peer %s\n", - (unsigned int) GNUNET_HELLO_size (hello), - GCP_2s (cp)); - if (NULL != cp->hello_offer) - { - GNUNET_TRANSPORT_offer_hello_cancel (cp->hello_offer); - cp->hello_offer = NULL; - } - if (NULL != cp->hello) - { - mrg = GNUNET_HELLO_merge (hello, - cp->hello); - GNUNET_free (cp->hello); - cp->hello = mrg; - } - else - { - cp->hello = GNUNET_memdup (hello, - GNUNET_HELLO_size (hello)); - } - cp->hello_offer - = GNUNET_TRANSPORT_offer_hello (cfg, - GNUNET_HELLO_get_header (cp->hello) , - &hello_offer_done, - cp); - /* New HELLO means cp's destruction time may change... */ - consider_peer_destroy (cp); -} - - -/** - * The tunnel to the given peer no longer exists, remove it from our - * data structures, and possibly clean up the peer itself. - * - * @param cp the peer affected - * @param t the dead tunnel - */ -void -GCP_drop_tunnel (struct CadetPeer *cp, - struct CadetTunnel *t) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Dropping tunnel %s to peer %s\n", - GCT_2s (t), - GCP_2s (cp)); - GNUNET_assert (cp->t == t); - cp->t = NULL; - consider_peer_destroy (cp); -} - - -/** - * Test if @a cp has a core-level connection - * - * @param cp peer to test - * @return #GNUNET_YES if @a cp has a core-level connection - */ -int -GCP_has_core_connection (struct CadetPeer *cp) -{ - return (NULL != cp->core_mq) ? GNUNET_YES : GNUNET_NO; -} - - -/** - * Start message queue change notifications. - * - * @param cp peer to notify for - * @param cb function to call if mq becomes available or unavailable - * @param cb_cls closure for @a cb - * @return handle to cancel request - */ -struct GCP_MessageQueueManager * -GCP_request_mq (struct CadetPeer *cp, - GCP_MessageQueueNotificationCallback cb, - void *cb_cls) -{ - struct GCP_MessageQueueManager *mqm; - - mqm = GNUNET_new (struct GCP_MessageQueueManager); - mqm->cb = cb; - mqm->cb_cls = cb_cls; - mqm->cp = cp; - GNUNET_CONTAINER_DLL_insert (cp->mqm_head, - cp->mqm_tail, - mqm); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Creating MQM %p for peer %s\n", - mqm, - GCP_2s (cp)); - if (NULL != cp->core_mq) - cb (cb_cls, - GNUNET_YES); - return mqm; -} - - -/** - * Stops message queue change notifications. - * - * @param mqm handle matching request to cancel - * @param last_env final message to transmit, or NULL - */ -void -GCP_request_mq_cancel (struct GCP_MessageQueueManager *mqm, - struct GNUNET_MQ_Envelope *last_env) -{ - struct CadetPeer *cp = mqm->cp; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Destroying MQM %p for peer %s%s\n", - mqm, - GCP_2s (cp), - (NULL == last_env) ? "" : " with last ditch transmission"); - if (NULL != mqm->env) - GNUNET_MQ_discard (mqm->env); - if (NULL != last_env) - { - if (NULL != cp->core_mq) - { - GNUNET_MQ_notify_sent (last_env, - &mqm_send_done, - cp); - GNUNET_MQ_send (cp->core_mq, - last_env); - } - else - { - GNUNET_MQ_discard (last_env); - } - } - if (cp->mqm_ready_ptr == mqm) - cp->mqm_ready_ptr = mqm->next; - GNUNET_CONTAINER_DLL_remove (cp->mqm_head, - cp->mqm_tail, - mqm); - GNUNET_free (mqm); -} - - -/** - * Send the message in @a env to @a cp, overriding queueing logic. - * This function should only be used to send error messages outside - * of flow and congestion control, similar to ICMP. Note that - * the envelope may be silently discarded as well. - * - * @param cp peer to send the message to - * @param env envelope with the message to send - */ -void -GCP_send_ooo (struct CadetPeer *cp, - struct GNUNET_MQ_Envelope *env) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending message to %s out of management\n", - GCP_2s (cp)); - if (NULL == cp->core_mq) - { - GNUNET_MQ_discard (env); - return; - } - GNUNET_MQ_notify_sent (env, - &mqm_send_done, - cp); - GNUNET_MQ_send (cp->core_mq, - env); -} - - - - -/* end of gnunet-service-cadet-new_peer.c */ diff --git a/src/cadet/gnunet-service-cadet-new_peer.h b/src/cadet/gnunet-service-cadet-new_peer.h deleted file mode 100644 index e1d6fc33a5..0000000000 --- a/src/cadet/gnunet-service-cadet-new_peer.h +++ /dev/null @@ -1,394 +0,0 @@ - -/* - This file is part of GNUnet. - Copyright (C) 2001-2017 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file cadet/gnunet-service-cadet-new_peer.h - * @brief Information we track per peer. - * @author Bartlomiej Polot - * @author Christian Grothoff - */ -#ifndef GNUNET_SERVICE_CADET_PEER_H -#define GNUNET_SERVICE_CADET_PEER_H - -#include "gnunet-service-cadet-new.h" -#include "gnunet_hello_lib.h" - - -/** - * Get the static string for a peer ID. - * - * @param peer Peer. - * - * @return Static string for it's ID. - */ -const char * -GCP_2s (const struct CadetPeer *peer); - - -/** - * Retrieve the CadetPeer stucture associated with the - * peer. Optionally create one and insert it in the appropriate - * structures if the peer is not known yet. - * - * @param peer_id Full identity of the peer. - * @param create #GNUNET_YES if a new peer should be created if unknown. - * #GNUNET_NO to return NULL if peer is unknown. - * @return Existing or newly created peer structure. - * NULL if unknown and not requested @a create - */ -struct CadetPeer * -GCP_get (const struct GNUNET_PeerIdentity *peer_id, - int create); - - -/** - * Calculate how desirable a path is for @a cp if - * @a cp is at offset @a off in the path. - * - * @param cp a peer reachable via a path - * @param off offset of @a cp in a path - * @return score how useful a path is to reach @a cp, - * positive scores mean path is more desirable - */ -double -GCP_get_desirability_of_path (struct CadetPeer *cp, - unsigned int off); - - -/** - * Obtain the peer identity for a `struct CadetPeer`. - * - * @param cp our peer handle - * @return the peer identity - */ -const struct GNUNET_PeerIdentity * -GCP_get_id (struct CadetPeer *cp); - - -/** - * Iterate over all known peers. - * - * @param iter Iterator. - * @param cls Closure for @c iter. - */ -void -GCP_iterate_all (GNUNET_CONTAINER_PeerMapIterator iter, - void *cls); - - -/** - * Count the number of known paths toward the peer. - * - * @param cp Peer to get path info. - * @return Number of known paths. - */ -unsigned int -GCP_count_paths (const struct CadetPeer *cp); - - -/** - * Drop all paths owned by this peer, and do not - * allow new ones to be added: We are shutting down. - * - * @param cp peer to drop paths to - */ -void -GCP_drop_owned_paths (struct CadetPeer *cp); - - -/** - * Peer path iterator. - * - * @param cls Closure. - * @param path Path itself - * @param off offset of the target peer in @a path - * @return #GNUNET_YES if should keep iterating. - * #GNUNET_NO otherwise. - */ -typedef int -(*GCP_PathIterator) (void *cls, - struct CadetPeerPath *path, - unsigned int off); - - -/** - * Iterate over the paths to a peer. - * - * @param cp Peer to get path info. - * @param callback Function to call for every path. - * @param callback_cls Closure for @a callback. - * @return Number of iterated paths. - */ -unsigned int -GCP_iterate_paths (struct CadetPeer *cp, - GCP_PathIterator callback, - void *callback_cls); - - -/** - * Iterate over the paths to @a peer where - * @a peer is at distance @a dist from us. - * - * @param cp Peer to get path info. - * @param dist desired distance of @a peer to us on the path - * @param callback Function to call for every path. - * @param callback_cls Closure for @a callback. - * @return Number of iterated paths. - */ -unsigned int -GCP_iterate_paths_at (struct CadetPeer *cp, - unsigned int dist, - GCP_PathIterator callback, - void *callback_cls); - - -/** - * Remove an entry from the DLL of all of the paths that this peer is on. - * - * @param cp peer to modify - * @param entry an entry on a path - * @param off offset of this peer on the path - */ -void -GCP_path_entry_remove (struct CadetPeer *cp, - struct CadetPeerPathEntry *entry, - unsigned int off); - - -/** - * Add an entry to the DLL of all of the paths that this peer is on. - * - * @param cp peer to modify - * @param entry an entry on a path - * @param off offset of this peer on the path - */ -void -GCP_path_entry_add (struct CadetPeer *cp, - struct CadetPeerPathEntry *entry, - unsigned int off); - - -/** - * Get the tunnel towards a peer. - * - * @param cp Peer to get from. - * @param create #GNUNET_YES to create a tunnel if we do not have one - * @return Tunnel towards peer. - */ -struct CadetTunnel * -GCP_get_tunnel (struct CadetPeer *cp, - int create); - - -/** - * The tunnel to the given peer no longer exists, remove it from our - * data structures, and possibly clean up the peer itself. - * - * @param cp the peer affected - * @param t the dead tunnel - */ -void -GCP_drop_tunnel (struct CadetPeer *cp, - struct CadetTunnel *t); - - -/** - * Try adding a @a path to this @a cp. If the peer already - * has plenty of paths, return NULL. - * - * @param cp peer to which the @a path leads to - * @param path a path looking for an owner; may not be fully initialized yet! - * @param off offset of @a cp in @a path - * @param force for attaching the path - * @return NULL if this peer does not care to become a new owner, - * otherwise the node in the peer's path heap for the @a path. - */ -struct GNUNET_CONTAINER_HeapNode * -GCP_attach_path (struct CadetPeer *cp, - struct CadetPeerPath *path, - unsigned int off, - int force); - - -/** - * This peer can no longer own @a path as the path - * has been extended and a peer further down the line - * is now the new owner. - * - * @param cp old owner of the @a path - * @param path path where the ownership is lost - * @param hn note in @a cp's path heap that must be deleted - */ -void -GCP_detach_path (struct CadetPeer *cp, - struct CadetPeerPath *path, - struct GNUNET_CONTAINER_HeapNode *hn); - - -/** - * Add a @a connection to this @a cp. - * - * @param cp peer via which the @a connection goes - * @param cc the connection to add - */ -void -GCP_add_connection (struct CadetPeer *cp, - struct CadetConnection *cc); - - -/** - * Remove a @a connection that went via this @a cp. - * - * @param cp peer via which the @a connection went - * @param cc the connection to remove - */ -void -GCP_remove_connection (struct CadetPeer *cp, - struct CadetConnection *cc); - - -/** - * We got a HELLO for a @a cp, remember it, and possibly - * trigger adequate actions (like trying to connect). - * - * @param cp the peer we got a HELLO for - * @param hello the HELLO to remember - */ -void -GCP_set_hello (struct CadetPeer *cp, - const struct GNUNET_HELLO_Message *hello); - - -/** - * Clean up all entries about all peers. - * Must only be called after all tunnels, CORE-connections and - * connections are down. - */ -void -GCP_destroy_all_peers (void); - - -/** - * Data structure used to track whom we have to notify about changes - * in our ability to transmit to a given peer. - * - * All queue managers will be given equal chance for sending messages - * to @a cp. This construct this guarantees fairness for access to @a - * cp among the different message queues. Each connection or route - * will have its respective message queue managers for each direction. - */ -struct GCP_MessageQueueManager; - - -/** - * Function to call with updated message queue object. - * - * @param cls closure - * @param available #GNUNET_YES if sending is now possible, - * #GNUNET_NO if sending is no longer possible - * #GNUNET_SYSERR if sending is no longer possible - * and the last envelope was discarded - */ -typedef void -(*GCP_MessageQueueNotificationCallback)(void *cls, - int available); - - -/** - * Start message queue change notifications. Will create a new slot - * to manage the message queue to the given @a cp. - * - * @param cp peer to notify for - * @param cb function to call if mq becomes available or unavailable - * @param cb_cls closure for @a cb - * @return handle to cancel request - */ -struct GCP_MessageQueueManager * -GCP_request_mq (struct CadetPeer *cp, - GCP_MessageQueueNotificationCallback cb, - void *cb_cls); - - -/** - * Test if @a cp has a core-level connection - * - * @param cp peer to test - * @return #GNUNET_YES if @a cp has a core-level connection - */ -int -GCP_has_core_connection (struct CadetPeer *cp); - - -/** - * Send the message in @a env via a @a mqm. Must only be called at - * most once after the respective - * #GCP_MessageQueueNotificationCallback was called with `available` - * set to #GNUNET_YES, and not after the callback was called with - * `available` set to #GNUNET_NO or #GNUNET_SYSERR. - * - * @param mqm message queue manager for the transmission - * @param env envelope with the message to send; must NOT - * yet have a #GNUNET_MQ_notify_sent() callback attached to it - */ -void -GCP_send (struct GCP_MessageQueueManager *mqm, - struct GNUNET_MQ_Envelope *env); - - -/** - * Send the message in @a env to @a cp, overriding queueing logic. - * This function should only be used to send error messages outside - * of flow and congestion control, similar to ICMP. Note that - * the envelope may be silently discarded as well. - * - * @param cp peer to send the message to - * @param env envelope with the message to send - */ -void -GCP_send_ooo (struct CadetPeer *cp, - struct GNUNET_MQ_Envelope *env); - - -/** - * Stops message queue change notifications and sends a last message. - * In practice, this is implemented by sending that @a last_env - * message immediately (if any), ignoring queue order. - * - * @param mqm handle matching request to cancel - * @param last_env final message to transmit, or NULL - */ -void -GCP_request_mq_cancel (struct GCP_MessageQueueManager *mqm, - struct GNUNET_MQ_Envelope *last_env); - - -/** - * Set the message queue to @a mq for peer @a cp and notify watchers. - * - * @param cp peer to modify - * @param mq message queue to set (can be NULL) - */ -void -GCP_set_mq (struct CadetPeer *cp, - struct GNUNET_MQ_Handle *mq); - - -#endif diff --git a/src/cadet/gnunet-service-cadet.c b/src/cadet/gnunet-service-cadet.c index 3a07f0ee58..a7e1fca472 100644 --- a/src/cadet/gnunet-service-cadet.c +++ b/src/cadet/gnunet-service-cadet.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2001-2013 GNUnet e.V. + Copyright (C) 2001-2013, 2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -22,38 +22,82 @@ * @file cadet/gnunet-service-cadet.c * @brief GNUnet CADET service with encryption * @author Bartlomiej Polot - * - * FIXME in progress: - * - rekey - reliability interaction - * - channel retransmit timing - * - * TODO: - * - relay corking down to core - * - set ttl relative to path length - * TODO END + * @author Christian Grothoff * * Dictionary: * - peer: other cadet instance. If there is direct connection it's a neighbor. - * - tunnel: encrypted connection to a peer, neighbor or not. - * - channel: connection between two clients, on the same or different peers. - * have properties like reliability. * - path: series of directly connected peer from one peer to another. * - connection: path which is being used in a tunnel. + * - tunnel: encrypted connection to a peer, neighbor or not. + * - channel: logical link between two clients, on the same or different peers. + * have properties like reliability. */ #include "platform.h" #include "gnunet_util_lib.h" #include "cadet.h" #include "gnunet_statistics_service.h" - -#include "gnunet-service-cadet_local.h" +#include "gnunet-service-cadet.h" #include "gnunet-service-cadet_channel.h" #include "gnunet-service-cadet_connection.h" -#include "gnunet-service-cadet_tunnel.h" +#include "gnunet-service-cadet_core.h" #include "gnunet-service-cadet_dht.h" -#include "gnunet-service-cadet_peer.h" #include "gnunet-service-cadet_hello.h" +#include "gnunet-service-cadet_tunnels.h" +#include "gnunet-service-cadet_peer.h" +#include "gnunet-service-cadet_paths.h" + +#define LOG(level, ...) GNUNET_log (level,__VA_ARGS__) + + +/** + * Struct containing information about a client of the service + */ +struct CadetClient +{ + /** + * Linked list next + */ + struct CadetClient *next; + + /** + * Linked list prev + */ + struct CadetClient *prev; + + /** + * Tunnels that belong to this client, indexed by local id, + * value is a `struct CadetChannel`. + */ + struct GNUNET_CONTAINER_MultiHashMap32 *channels; + + /** + * Handle to communicate with the client + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Client handle. + */ + struct GNUNET_SERVICE_Client *client; + + /** + * Ports that this client has declared interest in. + * Indexed by port, contains *Client. + */ + struct GNUNET_CONTAINER_MultiHashMap *ports; + /** + * Channel ID to use for the next incoming channel for this client. + * Wraps around (in theory). + */ + struct GNUNET_CADET_ClientChannelNumber next_ccn; + + /** + * ID of the client, mainly for debug messages. Purely internal to this file. + */ + unsigned int id; +}; /******************************************************************************/ /*********************** GLOBAL VARIABLES ****************************/ @@ -62,37 +106,317 @@ /****************************** Global variables ******************************/ /** + * Handle to our configuration. + */ +const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** * Handle to the statistics service. */ struct GNUNET_STATISTICS_Handle *stats; /** - * Local peer own ID (memory efficient handle). + * Handle to communicate with ATS. */ -GNUNET_PEER_Id myid; +struct GNUNET_ATS_ConnectivityHandle *ats_ch; /** - * Local peer own ID (full value). + * Local peer own ID. */ struct GNUNET_PeerIdentity my_full_id; +/** + * Own private key. + */ +struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; /** - * Signal that shutdown is happening: prevent recover measures. + * Signal that shutdown is happening: prevent recovery measures. */ int shutting_down; -/*************************** Static global variables **************************/ +/** + * DLL with all the clients, head. + */ +static struct CadetClient *clients_head; /** - * Own private key. + * DLL with all the clients, tail. */ -static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; +static struct CadetClient *clients_tail; +/** + * Next ID to assign to a client. + */ +static unsigned int next_client_id; + +/** + * All ports clients of this peer have opened. + */ +struct GNUNET_CONTAINER_MultiHashMap *open_ports; + +/** + * Map from ports to channels where the ports were closed at the + * time we got the inbound connection. + * Indexed by port, contains `struct CadetChannel`. + */ +struct GNUNET_CONTAINER_MultiHashMap *loose_channels; + +/** + * Map from PIDs to `struct CadetPeer` entries. + */ +struct GNUNET_CONTAINER_MultiPeerMap *peers; + +/** + * Map from `struct GNUNET_CADET_ConnectionTunnelIdentifier` + * hash codes to `struct CadetConnection` objects. + */ +struct GNUNET_CONTAINER_MultiShortmap *connections; + +/** + * How many messages are needed to trigger an AXOLOTL ratchet advance. + */ +unsigned long long ratchet_messages; + +/** + * How long until we trigger a ratched advance due to time. + */ +struct GNUNET_TIME_Relative ratchet_time; + +/** + * How frequently do we send KEEPALIVE messages on idle connections? + */ +struct GNUNET_TIME_Relative keepalive_period; + +/** + * Set to non-zero values to create random drops to test retransmissions. + */ +unsigned long long drop_percent; + + +/** + * Send a message to a client. + * + * @param c client to get the message + * @param env envelope with the message + */ +void +GSC_send_to_client (struct CadetClient *c, + struct GNUNET_MQ_Envelope *env) +{ + GNUNET_MQ_send (c->mq, + env); +} + + +/** + * Return identifier for a client as a string. + * + * @param c client to identify + * @return string for debugging + */ +const char * +GSC_2s (struct CadetClient *c) +{ + static char buf[32]; + + GNUNET_snprintf (buf, + sizeof (buf), + "Client(%u)", + c->id); + return buf; +} + + +/** + * Lookup channel of client @a c by @a ccn. + * + * @param c client to look in + * @param ccn channel ID to look up + * @return NULL if no such channel exists + */ +static struct CadetChannel * +lookup_channel (struct CadetClient *c, + struct GNUNET_CADET_ClientChannelNumber ccn) +{ + return GNUNET_CONTAINER_multihashmap32_get (c->channels, + ntohl (ccn.channel_of_client)); +} + + +/** + * Obtain the next LID to use for incoming connections to + * the given client. + * + * @param c client handle + */ +static struct GNUNET_CADET_ClientChannelNumber +client_get_next_ccn (struct CadetClient *c) +{ + struct GNUNET_CADET_ClientChannelNumber ccn = c->next_ccn; + + /* increment until we have a free one... */ + while (NULL != + lookup_channel (c, + ccn)) + { + ccn.channel_of_client + = htonl (1 + (ntohl (ccn.channel_of_client))); + if (ntohl (ccn.channel_of_client) >= + GNUNET_CADET_LOCAL_CHANNEL_ID_CLI) + ccn.channel_of_client = htonl (0); + } + c->next_ccn.channel_of_client + = htonl (1 + (ntohl (ccn.channel_of_client))); + return ccn; +} + + +/** + * Bind incoming channel to this client, and notify client about + * incoming connection. Caller is responsible for notifying the other + * peer about our acceptance of the channel. + * + * @param c client to bind to + * @param ch channel to be bound + * @param dest peer that establishes the connection + * @param port port number + * @param options options + * @return local channel number assigned to the new client + */ +struct GNUNET_CADET_ClientChannelNumber +GSC_bind (struct CadetClient *c, + struct CadetChannel *ch, + struct CadetPeer *dest, + const struct GNUNET_HashCode *port, + uint32_t options) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_CADET_LocalChannelCreateMessage *cm; + struct GNUNET_CADET_ClientChannelNumber ccn; + + ccn = client_get_next_ccn (c); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap32_put (c->channels, + ntohl (ccn.channel_of_client), + ch, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Accepting incoming %s from %s on open port %s (%u), assigning ccn %X\n", + GCCH_2s (ch), + GCP_2s (dest), + GNUNET_h2s (port), + (uint32_t) ntohl (options), + (uint32_t) ntohl (ccn.channel_of_client)); + /* notify local client about incoming connection! */ + env = GNUNET_MQ_msg (cm, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_CREATE); + cm->ccn = ccn; + cm->port = *port; + cm->opt = htonl (options); + cm->peer = *GCP_get_id (dest); + GSC_send_to_client (c, + env); + return ccn; +} + + +/** + * Callback invoked on all peers to destroy all tunnels + * that may still exist. + * + * @param cls NULL + * @param pid identify of a peer + * @param value a `struct CadetPeer` that may still have a tunnel + * @return #GNUNET_OK (iterate over all entries) + */ +static int +destroy_tunnels_now (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct CadetPeer *cp = value; + struct CadetTunnel *t = GCP_get_tunnel (cp, + GNUNET_NO); + + if (NULL != t) + GCT_destroy_tunnel_now (t); + return GNUNET_OK; +} + + +/** + * Callback invoked on all peers to destroy all tunnels + * that may still exist. + * + * @param cls NULL + * @param pid identify of a peer + * @param value a `struct CadetPeer` that may still have a tunnel + * @return #GNUNET_OK (iterate over all entries) + */ +static int +destroy_paths_now (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct CadetPeer *cp = value; + + GCP_drop_owned_paths (cp); + return GNUNET_OK; +} + + +/** + * Shutdown everything once the clients have disconnected. + */ +static void +shutdown_rest () +{ + if (NULL != stats) + { + GNUNET_STATISTICS_destroy (stats, + GNUNET_NO); + stats = NULL; + } + if (NULL != open_ports) + { + GNUNET_CONTAINER_multihashmap_destroy (open_ports); + open_ports = NULL; + } + if (NULL != loose_channels) + { + GNUNET_CONTAINER_multihashmap_destroy (loose_channels); + loose_channels = NULL; + } + /* Destroy tunnels. Note that all channels must be destroyed first! */ + GCP_iterate_all (&destroy_tunnels_now, + NULL); + /* All tunnels, channels, connections and CORE must be down before this point. */ + GCP_iterate_all (&destroy_paths_now, + NULL); + /* All paths, tunnels, channels, connections and CORE must be down before this point. */ + GCP_destroy_all_peers (); + if (NULL != peers) + { + GNUNET_CONTAINER_multipeermap_destroy (peers); + peers = NULL; + } + if (NULL != connections) + { + GNUNET_CONTAINER_multishortmap_destroy (connections); + connections = NULL; + } + if (NULL != ats_ch) + { + GNUNET_ATS_connectivity_done (ats_ch); + ats_ch = NULL; + } + GCD_shutdown (); + GCH_shutdown (); + GNUNET_free_non_null (my_private_key); + my_private_key = NULL; +} -/******************************************************************************/ -/************************ MAIN FUNCTIONS ****************************/ -/******************************************************************************/ /** * Task run during shutdown. @@ -102,83 +426,1071 @@ static struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key; static void shutdown_task (void *cls) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "shutting down\n"); - + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Shutting down\n"); shutting_down = GNUNET_YES; + GCO_shutdown (); + if (NULL == clients_head) + shutdown_rest (); +} - GML_shutdown (); - GCH_shutdown (); - GCC_shutdown (); - GCT_shutdown (); - GCD_shutdown (); - GCP_shutdown (); - GNUNET_STATISTICS_destroy (stats, GNUNET_NO); - stats = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "shut down\n"); +/** + * We had a remote connection @a value to port @a port before + * client @a cls opened port @a port. Bind them now. + * + * @param cls the `struct CadetClient` + * @param port the port + * @param value the `struct CadetChannel` + * @return #GNUNET_YES (iterate over all such channels) + */ +static int +bind_loose_channel (void *cls, + const struct GNUNET_HashCode *port, + void *value) +{ + struct CadetClient *c = cls; + struct CadetChannel *ch = value; + + GCCH_bind (ch, + c); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (loose_channels, + port, + value)); + return GNUNET_YES; } /** - * Process cadet requests. + * Handle port open request. Creates a mapping from the + * port to the respective client and checks whether we have + * loose channels trying to bind to the port. If so, those + * are bound. * - * @param cls closure - * @param server the initialized server - * @param c configuration to use + * @param cls Identification of the client. + * @param pmsg The actual message. */ static void -run (void *cls, struct GNUNET_SERVER_Handle *server, - const struct GNUNET_CONFIGURATION_Handle *c) +handle_port_open (void *cls, + const struct GNUNET_CADET_PortMessage *pmsg) { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "starting to run\n"); + struct CadetClient *c = cls; - stats = GNUNET_STATISTICS_create ("cadet", c); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Open port %s requested by %s\n", + GNUNET_h2s (&pmsg->port), + GSC_2s (c)); + if (NULL == c->ports) + c->ports = GNUNET_CONTAINER_multihashmap_create (4, + GNUNET_NO); + if (GNUNET_OK != + GNUNET_CONTAINER_multihashmap_put (c->ports, + &pmsg->port, + c, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (c->client); + return; + } + (void) GNUNET_CONTAINER_multihashmap_put (open_ports, + &pmsg->port, + c, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + GNUNET_CONTAINER_multihashmap_get_multiple (loose_channels, + &pmsg->port, + &bind_loose_channel, + c); + GNUNET_SERVICE_client_continue (c->client); +} - /* Scheduled the task to clean up when shutdown is called */ - GNUNET_SCHEDULER_add_shutdown (&shutdown_task, - NULL); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "reading key\n"); - my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (c); - GNUNET_assert (NULL != my_private_key); - GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_full_id.public_key); - myid = GNUNET_PEER_intern (&my_full_id); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "STARTING SERVICE (cadet) for peer [%s]\n", - GNUNET_i2s (&my_full_id)); - GML_init (server); /* Local clients */ - GCH_init (c); /* Hellos */ - GCC_init (c); /* Connections */ - GCP_init (c); /* Peers */ - GCD_init (c); /* DHT */ - GCT_init (c, my_private_key); /* Tunnels */ +/** + * Handler for port close requests. Marks this port as closed + * (unless of course we have another client with the same port + * open). Note that existing channels accepted on the port are + * not affected. + * + * @param cls Identification of the client. + * @param pmsg The actual message. + */ +static void +handle_port_close (void *cls, + const struct GNUNET_CADET_PortMessage *pmsg) +{ + struct CadetClient *c = cls; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cadet service running\n"); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Closing port %s as requested by %s\n", + GNUNET_h2s (&pmsg->port), + GSC_2s (c)); + if (GNUNET_YES != + GNUNET_CONTAINER_multihashmap_remove (c->ports, + &pmsg->port, + c)) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (c->client); + return; + } + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (open_ports, + &pmsg->port, + c)); + GNUNET_SERVICE_client_continue (c->client); } /** - * The main function for the cadet service. + * Handler for requests for us creating a new channel to another peer and port. * - * @param argc number of arguments from the command line - * @param argv command line arguments - * @return 0 ok, 1 on error + * @param cls Identification of the client. + * @param tcm The actual message. */ -int -main (int argc, char *const *argv) +static void +handle_channel_create (void *cls, + const struct GNUNET_CADET_LocalChannelCreateMessage *tcm) { - int r; + struct CadetClient *c = cls; + struct CadetChannel *ch; - shutting_down = GNUNET_NO; - r = GNUNET_SERVICE_run (argc, argv, "cadet", GNUNET_SERVICE_OPTION_NONE, &run, - NULL); - GNUNET_free (my_private_key); + if (ntohl (tcm->ccn.channel_of_client) < GNUNET_CADET_LOCAL_CHANNEL_ID_CLI) + { + /* Channel ID not in allowed range. */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (c->client); + return; + } + ch = lookup_channel (c, + tcm->ccn); + if (NULL != ch) + { + /* Channel ID already in use. Not allowed. */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (c->client); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "New channel to %s at port %s requested by %s\n", + GNUNET_i2s (&tcm->peer), + GNUNET_h2s (&tcm->port), + GSC_2s (c)); - if (GNUNET_OK != r) + /* Create channel */ + ch = GCCH_channel_local_new (c, + tcm->ccn, + GCP_get (&tcm->peer, + GNUNET_YES), + &tcm->port, + ntohl (tcm->opt)); + if (NULL == ch) { - FPRINTF (stderr, "GNUNET_SERVICE_run for CADET has failed!\n"); - return 1; + GNUNET_break (0); + GNUNET_SERVICE_client_drop (c->client); + return; } + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap32_put (c->channels, + ntohl (tcm->ccn.channel_of_client), + ch, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - return 0; + GNUNET_SERVICE_client_continue (c->client); } + + +/** + * Handler for requests of destroying an existing channel. + * + * @param cls client identification of the client + * @param msg the actual message + */ +static void +handle_channel_destroy (void *cls, + const struct GNUNET_CADET_LocalChannelDestroyMessage *msg) +{ + struct CadetClient *c = cls; + struct CadetChannel *ch; + + ch = lookup_channel (c, + msg->ccn); + if (NULL == ch) + { + /* Client attempted to destroy unknown channel. + Can happen if the other side went down at the same time.*/ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "%s tried to destroy unknown channel %X\n", + GSC_2s(c), + (uint32_t) ntohl (msg->ccn.channel_of_client)); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "%s is destroying %s\n", + GSC_2s(c), + GCCH_2s (ch)); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap32_remove (c->channels, + ntohl (msg->ccn.channel_of_client), + ch)); + GCCH_channel_local_destroy (ch, + c, + msg->ccn); + GNUNET_SERVICE_client_continue (c->client); +} + + +/** + * Check for client traffic data message is well-formed. + * + * @param cls identification of the client + * @param msg the actual message + * @return #GNUNET_OK if @a msg is OK, #GNUNET_SYSERR if not + */ +static int +check_local_data (void *cls, + const struct GNUNET_CADET_LocalData *msg) +{ + size_t payload_size; + size_t payload_claimed_size; + const char *buf; + struct GNUNET_MessageHeader pa; + + /* FIXME: what is the format we shall allow for @a msg? + ONE payload item or multiple? Seems current cadet_api + at least in theory allows more than one. Next-gen + cadet_api will likely no more, so we could then + simplify this mess again. */ + /* Sanity check for message size */ + payload_size = ntohs (msg->header.size) - sizeof (*msg); + buf = (const char *) &msg[1]; + while (payload_size >= sizeof (struct GNUNET_MessageHeader)) + { + /* need to memcpy() for alignment */ + GNUNET_memcpy (&pa, + buf, + sizeof (pa)); + payload_claimed_size = ntohs (pa.size); + if ( (payload_size < payload_claimed_size) || + (payload_claimed_size < sizeof (struct GNUNET_MessageHeader)) || + (GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE < payload_claimed_size) ) + { + GNUNET_break (0); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Local data of %u total size had sub-message %u at %u with %u bytes\n", + ntohs (msg->header.size), + ntohs (pa.type), + (unsigned int) (buf - (const char *) &msg[1]), + (unsigned int) payload_claimed_size); + return GNUNET_SYSERR; + } + payload_size -= payload_claimed_size; + buf += payload_claimed_size; + } + if (0 != payload_size) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handler for client payload traffic to be send on a channel to + * another peer. + * + * @param cls identification of the client + * @param msg the actual message + */ +static void +handle_local_data (void *cls, + const struct GNUNET_CADET_LocalData *msg) +{ + struct CadetClient *c = cls; + struct CadetChannel *ch; + size_t payload_size; + const char *buf; + + ch = lookup_channel (c, + msg->ccn); + if (NULL == ch) + { + /* Channel does not exist (anymore) */ + LOG (GNUNET_ERROR_TYPE_WARNING, + "Dropping payload for channel %u from client (channel unknown, other endpoint may have disconnected)\n", + (unsigned int) ntohl (msg->ccn.channel_of_client)); + GNUNET_SERVICE_client_continue (c->client); + return; + } + payload_size = ntohs (msg->header.size) - sizeof (*msg); + GNUNET_STATISTICS_update (stats, + "# payload received from clients", + payload_size, + GNUNET_NO); + buf = (const char *) &msg[1]; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received %u bytes payload from %s for %s\n", + (unsigned int) payload_size, + GSC_2s (c), + GCCH_2s (ch)); + if (GNUNET_OK != + GCCH_handle_local_data (ch, + msg->ccn, + buf, + payload_size)) + { + GNUNET_SERVICE_client_drop (c->client); + return; + } + GNUNET_SERVICE_client_continue (c->client); +} + + +/** + * Handler for client's ACKs for payload traffic. + * + * @param cls identification of the client. + * @param msg The actual message. + */ +static void +handle_local_ack (void *cls, + const struct GNUNET_CADET_LocalAck *msg) +{ + struct CadetClient *c = cls; + struct CadetChannel *ch; + + ch = lookup_channel (c, + msg->ccn); + if (NULL == ch) + { + /* Channel does not exist (anymore) */ + LOG (GNUNET_ERROR_TYPE_WARNING, + "Ignoring local ACK for channel %u from client (channel unknown, other endpoint may have disconnected)\n", + (unsigned int) ntohl (msg->ccn.channel_of_client)); + GNUNET_SERVICE_client_continue (c->client); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got a local ACK from %s for %s\n", + GSC_2s(c), + GCCH_2s (ch)); + GCCH_handle_local_ack (ch, + msg->ccn); + GNUNET_SERVICE_client_continue (c->client); +} + + +/** + * Iterator over all peers to send a monitoring client info about each peer. + * + * @param cls Closure (). + * @param peer Peer ID (tunnel remote peer). + * @param value Peer info. + * @return #GNUNET_YES, to keep iterating. + */ +static int +get_all_peers_iterator (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *value) +{ + struct CadetClient *c = cls; + struct CadetPeer *p = value; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_CADET_LocalInfoPeer *msg; + + env = GNUNET_MQ_msg (msg, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEERS); + msg->destination = *peer; + msg->paths = htons (GCP_count_paths (p)); + msg->tunnel = htons (NULL != GCP_get_tunnel (p, + GNUNET_NO)); + GNUNET_MQ_send (c->mq, + env); + return GNUNET_YES; +} + + +/** + * Handler for client's INFO PEERS request. + * + * @param cls Identification of the client. + * @param message The actual message. + */ +static void +handle_get_peers (void *cls, + const struct GNUNET_MessageHeader *message) +{ + struct CadetClient *c = cls; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_MessageHeader *reply; + + GCP_iterate_all (&get_all_peers_iterator, + c); + env = GNUNET_MQ_msg (reply, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEERS); + GNUNET_MQ_send (c->mq, + env); + GNUNET_SERVICE_client_continue (c->client); +} + + +/** + * Iterator over all paths of a peer to build an InfoPeer message. + * Message contains blocks of peers, first not included. + * + * @param cls message queue for transmission + * @param path Path itself + * @param off offset of the peer on @a path + * @return #GNUNET_YES if should keep iterating. + * #GNUNET_NO otherwise. + */ +static int +path_info_iterator (void *cls, + struct CadetPeerPath *path, + unsigned int off) +{ + struct GNUNET_MQ_Handle *mq = cls; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_MessageHeader *resp; + struct GNUNET_PeerIdentity *id; + uint16_t path_size; + unsigned int i; + unsigned int path_length; + + path_length = GCPP_get_length (path); + path_size = sizeof (struct GNUNET_PeerIdentity) * (path_length - 1); + if (sizeof (*resp) + path_size > UINT16_MAX) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Path of %u entries is too long for info message\n", + path_length); + return GNUNET_YES; + } + env = GNUNET_MQ_msg_extra (resp, + path_size, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEER); + id = (struct GNUNET_PeerIdentity *) &resp[1]; + + /* Don't copy first peer. First peer is always the local one. Last + * peer is always the destination (leave as 0, EOL). + */ + for (i = 0; i < off; i++) + id[i] = *GCP_get_id (GCPP_get_peer_at_offset (path, + i + 1)); + GNUNET_MQ_send (mq, + env); + return GNUNET_YES; +} + + +/** + * Handler for client's SHOW_PEER request. + * + * @param cls Identification of the client. + * @param msg The actual message. + */ +static void +handle_show_peer (void *cls, + const struct GNUNET_CADET_LocalInfo *msg) +{ + struct CadetClient *c = cls; + struct CadetPeer *p; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_MessageHeader *resp; + + p = GCP_get (&msg->peer, + GNUNET_NO); + if (NULL != p) + GCP_iterate_paths (p, + &path_info_iterator, + c->mq); + /* Send message with 0/0 to indicate the end */ + env = GNUNET_MQ_msg (resp, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEER_END); + GNUNET_MQ_send (c->mq, + env); + GNUNET_SERVICE_client_continue (c->client); +} + + +/** + * Iterator over all tunnels to send a monitoring client info about each tunnel. + * + * @param cls Closure (). + * @param peer Peer ID (tunnel remote peer). + * @param value a `struct CadetPeer` + * @return #GNUNET_YES, to keep iterating. + */ +static int +get_all_tunnels_iterator (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *value) +{ + struct CadetClient *c = cls; + struct CadetPeer *p = value; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_CADET_LocalInfoTunnel *msg; + struct CadetTunnel *t; + + t = GCP_get_tunnel (p, + GNUNET_NO); + if (NULL == t) + return GNUNET_YES; + env = GNUNET_MQ_msg (msg, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNELS); + msg->destination = *peer; + msg->channels = htonl (GCT_count_channels (t)); + msg->connections = htonl (GCT_count_any_connections (t)); + msg->cstate = htons (0); + msg->estate = htons ((uint16_t) GCT_get_estate (t)); + GNUNET_MQ_send (c->mq, + env); + return GNUNET_YES; +} + + +/** + * Handler for client's #GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNELS request. + * + * @param cls client Identification of the client. + * @param message The actual message. + */ +static void +handle_info_tunnels (void *cls, + const struct GNUNET_MessageHeader *message) +{ + struct CadetClient *c = cls; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_MessageHeader *reply; + + GCP_iterate_all (&get_all_tunnels_iterator, + c); + env = GNUNET_MQ_msg (reply, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNELS); + GNUNET_MQ_send (c->mq, + env); + GNUNET_SERVICE_client_continue (c->client); +} + + +/** + * Update the message with information about the connection. + * + * @param cls a `struct GNUNET_CADET_LocalInfoTunnel` message to update + * @param ct a connection about which we should store information in @a cls + */ +static void +iter_connection (void *cls, + struct CadetTConnection *ct) +{ + struct GNUNET_CADET_LocalInfoTunnel *msg = cls; + struct CadetConnection *cc = ct->cc; + struct GNUNET_CADET_ConnectionTunnelIdentifier *h; + + h = (struct GNUNET_CADET_ConnectionTunnelIdentifier *) &msg[1]; + h[msg->connections++] = *(GCC_get_id (cc)); +} + + +/** + * Update the message with information about the channel. + * + * @param cls a `struct GNUNET_CADET_LocalInfoTunnel` message to update + * @param ch a channel about which we should store information in @a cls + */ +static void +iter_channel (void *cls, + struct CadetChannel *ch) +{ + struct GNUNET_CADET_LocalInfoTunnel *msg = cls; + struct GNUNET_CADET_ConnectionTunnelIdentifier *h = (struct GNUNET_CADET_ConnectionTunnelIdentifier *) &msg[1]; + struct GNUNET_CADET_ChannelTunnelNumber *chn + = (struct GNUNET_CADET_ChannelTunnelNumber *) &h[msg->connections]; + + chn[msg->channels++] = GCCH_get_id (ch); +} + + +/** + * Handler for client's #GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNEL request. + * + * @param cls Identification of the client. + * @param msg The actual message. + */ +static void +handle_info_tunnel (void *cls, + const struct GNUNET_CADET_LocalInfo *msg) +{ + struct CadetClient *c = cls; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_CADET_LocalInfoTunnel *resp; + struct CadetTunnel *t; + struct CadetPeer *p; + unsigned int ch_n; + unsigned int c_n; + + p = GCP_get (&msg->peer, + GNUNET_NO); + t = GCP_get_tunnel (p, + GNUNET_NO); + if (NULL == t) + { + /* We don't know the tunnel */ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_CADET_LocalInfoTunnel *warn; + + LOG (GNUNET_ERROR_TYPE_INFO, + "Tunnel to %s unknown\n", + GNUNET_i2s_full (&msg->peer)); + env = GNUNET_MQ_msg (warn, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNEL); + warn->destination = msg->peer; + GNUNET_MQ_send (c->mq, + env); + GNUNET_SERVICE_client_continue (c->client); + return; + } + + /* Initialize context */ + ch_n = GCT_count_channels (t); + c_n = GCT_count_any_connections (t); + env = GNUNET_MQ_msg_extra (resp, + c_n * sizeof (struct GNUNET_CADET_ConnectionTunnelIdentifier) + + ch_n * sizeof (struct GNUNET_CADET_ChannelTunnelNumber), + GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNEL); + resp->destination = msg->peer; + /* Do not reorder! #iter_channel needs counters in HBO! */ + GCT_iterate_connections (t, + &iter_connection, + resp); + GCT_iterate_channels (t, + &iter_channel, + resp); + resp->connections = htonl (resp->connections); + resp->channels = htonl (resp->channels); + resp->cstate = htons (0); + resp->estate = htons (GCT_get_estate (t)); + GNUNET_MQ_send (c->mq, + env); + GNUNET_SERVICE_client_continue (c->client); +} + + +/** + * Iterator over all peers to dump info for each peer. + * + * @param cls Closure (unused). + * @param peer Peer ID (tunnel remote peer). + * @param value Peer info. + * + * @return #GNUNET_YES, to keep iterating. + */ +static int +show_peer_iterator (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *value) +{ + struct CadetPeer *p = value; + struct CadetTunnel *t; + + t = GCP_get_tunnel (p, + GNUNET_NO); + if (NULL != t) + GCT_debug (t, + GNUNET_ERROR_TYPE_ERROR); + LOG (GNUNET_ERROR_TYPE_ERROR, "\n"); + return GNUNET_YES; +} + + +/** + * Handler for client's INFO_DUMP request. + * + * @param cls Identification of the client. + * @param message The actual message. + */ +static void +handle_info_dump (void *cls, + const struct GNUNET_MessageHeader *message) +{ + struct CadetClient *c = cls; + + LOG (GNUNET_ERROR_TYPE_INFO, + "Received dump info request from client %u\n", + c->id); + + LOG (GNUNET_ERROR_TYPE_ERROR, + "*************************** DUMP START ***************************\n"); + for (struct CadetClient *ci = clients_head; + NULL != ci; + ci = ci->next) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Client %u (%p), handle: %p, ports: %u, channels: %u\n", + ci->id, + ci, + ci->client, + (NULL != c->ports) + ? GNUNET_CONTAINER_multihashmap_size (ci->ports) + : 0, + GNUNET_CONTAINER_multihashmap32_size (ci->channels)); + } + LOG (GNUNET_ERROR_TYPE_ERROR, "***************************\n"); + GCP_iterate_all (&show_peer_iterator, + NULL); + + LOG (GNUNET_ERROR_TYPE_ERROR, + "**************************** DUMP END ****************************\n"); + + GNUNET_SERVICE_client_continue (c->client); +} + + + +/** + * Callback called when a client connects to the service. + * + * @param cls closure for the service + * @param client the new client that connected to the service + * @param mq the message queue used to send messages to the client + * @return @a c + */ +static void * +client_connect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + struct GNUNET_MQ_Handle *mq) +{ + struct CadetClient *c; + + c = GNUNET_new (struct CadetClient); + c->client = client; + c->mq = mq; + c->id = next_client_id++; /* overflow not important: just for debug */ + c->channels + = GNUNET_CONTAINER_multihashmap32_create (32); + GNUNET_CONTAINER_DLL_insert (clients_head, + clients_tail, + c); + GNUNET_STATISTICS_update (stats, + "# clients", + +1, + GNUNET_NO); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "%s connected\n", + GSC_2s (c)); + return c; +} + + +/** + * A channel was destroyed by the other peer. Tell our client. + * + * @param c client that lost a channel + * @param ccn channel identification number for the client + * @param ch the channel object + */ +void +GSC_handle_remote_channel_destroy (struct CadetClient *c, + struct GNUNET_CADET_ClientChannelNumber ccn, + struct CadetChannel *ch) +{ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_CADET_LocalChannelDestroyMessage *tdm; + + env = GNUNET_MQ_msg (tdm, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_DESTROY); + tdm->ccn = ccn; + GSC_send_to_client (c, + env); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap32_remove (c->channels, + ntohl (ccn.channel_of_client), + ch)); +} + + +/** + * A client that created a loose channel that was not bound to a port + * disconnected, drop it from the #loose_channels list. + * + * @param port the port the channel was trying to bind to + * @param ch the channel that was lost + */ +void +GSC_drop_loose_channel (const struct GNUNET_HashCode *port, + struct CadetChannel *ch) +{ + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (loose_channels, + port, + ch)); +} + + +/** + * Iterator for deleting each channel whose client endpoint disconnected. + * + * @param cls Closure (client that has disconnected). + * @param key The local channel id in host byte order + * @param value The value stored at the key (channel to destroy). + * @return #GNUNET_OK, keep iterating. + */ +static int +channel_destroy_iterator (void *cls, + uint32_t key, + void *value) +{ + struct CadetClient *c = cls; + struct GNUNET_CADET_ClientChannelNumber ccn; + struct CadetChannel *ch = value; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Destroying %s, due to %s disconnecting.\n", + GCCH_2s (ch), + GSC_2s (c)); + ccn.channel_of_client = htonl (key); + GCCH_channel_local_destroy (ch, + c, + ccn); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap32_remove (c->channels, + key, + ch)); + return GNUNET_OK; +} + + +/** + * Remove client's ports from the global hashmap on disconnect. + * + * @param cls Closure (unused). + * @param key the port. + * @param value the `struct CadetClient` to remove + * @return #GNUNET_OK, keep iterating. + */ +static int +client_release_ports (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct CadetClient *c = value; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Closing port %s due to %s disconnect.\n", + GNUNET_h2s (key), + GSC_2s (c)); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (open_ports, + key, + value)); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (c->ports, + key, + value)); + return GNUNET_OK; +} + + +/** + * Callback called when a client disconnected from the service + * + * @param cls closure for the service + * @param client the client that disconnected + * @param internal_cls should be equal to @a c + */ +static void +client_disconnect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + void *internal_cls) +{ + struct CadetClient *c = internal_cls; + + GNUNET_assert (c->client == client); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "%s is disconnecting.\n", + GSC_2s (c)); + if (NULL != c->channels) + { + GNUNET_CONTAINER_multihashmap32_iterate (c->channels, + &channel_destroy_iterator, + c); + GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap32_size (c->channels)); + GNUNET_CONTAINER_multihashmap32_destroy (c->channels); + } + if (NULL != c->ports) + { + GNUNET_CONTAINER_multihashmap_iterate (c->ports, + &client_release_ports, + c); + GNUNET_CONTAINER_multihashmap_destroy (c->ports); + } + GNUNET_CONTAINER_DLL_remove (clients_head, + clients_tail, + c); + GNUNET_STATISTICS_update (stats, + "# clients", + -1, + GNUNET_NO); + GNUNET_free (c); + if ( (NULL == clients_head) && + (GNUNET_YES == shutting_down) ) + shutdown_rest (); +} + + +/** + * Setup CADET internals. + * + * @param cls closure + * @param server the initialized server + * @param c configuration to use + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *c, + struct GNUNET_SERVICE_Handle *service) +{ + cfg = c; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, + "CADET", + "RATCHET_MESSAGES", + &ratchet_messages)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, + "CADET", + "RATCHET_MESSAGES", + "needs to be a number"); + ratchet_messages = 64; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (c, + "CADET", + "RATCHET_TIME", + &ratchet_time)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, + "CADET", + "RATCHET_TIME", + "need delay value"); + ratchet_time = GNUNET_TIME_UNIT_HOURS; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (c, + "CADET", + "REFRESH_CONNECTION_TIME", + &keepalive_period)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, + "CADET", + "REFRESH_CONNECTION_TIME", + "need delay value"); + keepalive_period = GNUNET_TIME_UNIT_MINUTES; + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, + "CADET", + "DROP_PERCENT", + &drop_percent)) + { + drop_percent = 0; + } + else + { + LOG (GNUNET_ERROR_TYPE_WARNING, "**************************************\n"); + LOG (GNUNET_ERROR_TYPE_WARNING, "Cadet is running with DROP enabled.\n"); + LOG (GNUNET_ERROR_TYPE_WARNING, "This is NOT a good idea!\n"); + LOG (GNUNET_ERROR_TYPE_WARNING, "Remove DROP_PERCENT from config file.\n"); + LOG (GNUNET_ERROR_TYPE_WARNING, "**************************************\n"); + } + my_private_key = GNUNET_CRYPTO_eddsa_key_create_from_configuration (c); + if (NULL == my_private_key) + { + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, + &my_full_id.public_key); + stats = GNUNET_STATISTICS_create ("cadet", + c); + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, + NULL); + ats_ch = GNUNET_ATS_connectivity_init (c); + /* FIXME: optimize code to allow GNUNET_YES here! */ + open_ports = GNUNET_CONTAINER_multihashmap_create (16, + GNUNET_NO); + loose_channels = GNUNET_CONTAINER_multihashmap_create (16, + GNUNET_NO); + peers = GNUNET_CONTAINER_multipeermap_create (16, + GNUNET_YES); + connections = GNUNET_CONTAINER_multishortmap_create (256, + GNUNET_YES); + GCH_init (c); + GCD_init (c); + GCO_init (c); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "CADET started for peer %s\n", + GNUNET_i2s (&my_full_id)); + +} + + +/** + * Define "main" method using service macro. + */ +GNUNET_SERVICE_MAIN +("cadet", + GNUNET_SERVICE_OPTION_NONE, + &run, + &client_connect_cb, + &client_disconnect_cb, + NULL, + GNUNET_MQ_hd_fixed_size (port_open, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_PORT_OPEN, + struct GNUNET_CADET_PortMessage, + NULL), + GNUNET_MQ_hd_fixed_size (port_close, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_PORT_CLOSE, + struct GNUNET_CADET_PortMessage, + NULL), + GNUNET_MQ_hd_fixed_size (channel_create, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_CREATE, + struct GNUNET_CADET_LocalChannelCreateMessage, + NULL), + GNUNET_MQ_hd_fixed_size (channel_destroy, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_DESTROY, + struct GNUNET_CADET_LocalChannelDestroyMessage, + NULL), + GNUNET_MQ_hd_var_size (local_data, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA, + struct GNUNET_CADET_LocalData, + NULL), + GNUNET_MQ_hd_fixed_size (local_ack, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_ACK, + struct GNUNET_CADET_LocalAck, + NULL), + GNUNET_MQ_hd_fixed_size (get_peers, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEERS, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_hd_fixed_size (show_peer, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEER, + struct GNUNET_CADET_LocalInfo, + NULL), + GNUNET_MQ_hd_fixed_size (info_tunnels, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNELS, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_hd_fixed_size (info_tunnel, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNEL, + struct GNUNET_CADET_LocalInfo, + NULL), + GNUNET_MQ_hd_fixed_size (info_dump, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_DUMP, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_handler_end ()); + +/* end of gnunet-service-cadet-new.c */ diff --git a/src/cadet/gnunet-service-cadet-new.h b/src/cadet/gnunet-service-cadet.h index bee5c67cc0..2f2d7baf35 100644 --- a/src/cadet/gnunet-service-cadet-new.h +++ b/src/cadet/gnunet-service-cadet.h @@ -20,7 +20,7 @@ */ /** - * @file cadet/gnunet-service-cadet-new.h + * @file cadet/gnunet-service-cadet.h * @brief Information we track per peer. * @author Bartlomiej Polot * @author Christian Grothoff diff --git a/src/cadet/gnunet-service-cadet_channel.c b/src/cadet/gnunet-service-cadet_channel.c index 7b7c6e57ce..68e29b66b5 100644 --- a/src/cadet/gnunet-service-cadet_channel.c +++ b/src/cadet/gnunet-service-cadet_channel.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2013 GNUnet e.V. + Copyright (C) 2001-2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -17,31 +17,63 @@ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - - +/** + * @file cadet/gnunet-service-cadet_channel.c + * @brief logical links between CADET clients + * @author Bartlomiej Polot + * @author Christian Grothoff + * + * TODO: + * - Congestion/flow control: + * + estimate max bandwidth using bursts and use to for CONGESTION CONTROL! + * (and figure out how/where to use this!) + * + figure out flow control without ACKs (unreliable traffic!) + * - revisit handling of 'unbuffered' traffic! + * (need to push down through tunnel into connection selection) + * - revisit handling of 'buffered' traffic: 4 is a rather small buffer; maybe + * reserve more bits in 'options' to allow for buffer size control? + */ #include "platform.h" -#include "gnunet_util_lib.h" - +#include "cadet.h" #include "gnunet_statistics_service.h" +#include "gnunet-service-cadet_channel.h" +#include "gnunet-service-cadet_connection.h" +#include "gnunet-service-cadet_tunnels.h" +#include "gnunet-service-cadet_paths.h" -#include "cadet.h" -#include "cadet_protocol.h" +#define LOG(level,...) GNUNET_log_from (level,"cadet-chn",__VA_ARGS__) -#include "gnunet-service-cadet_channel.h" -#include "gnunet-service-cadet_local.h" -#include "gnunet-service-cadet_tunnel.h" -#include "gnunet-service-cadet_peer.h" +/** + * How long do we initially wait before retransmitting? + */ +#define CADET_INITIAL_RETRANSMIT_TIME GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 250) -#define LOG(level, ...) GNUNET_log_from(level,"cadet-chn",__VA_ARGS__) -#define LOG2(level, ...) GNUNET_log_from_nocheck(level,"cadet-chn",__VA_ARGS__) +/** + * How long do we wait before dropping state about incoming + * connection to closed port? + */ +#define TIMEOUT_CLOSED_PORT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 30) -#define CADET_RETRANSMIT_TIME GNUNET_TIME_relative_multiply(\ - GNUNET_TIME_UNIT_MILLISECONDS, 250) -#define CADET_RETRANSMIT_MARGIN 4 +/** + * How long do we wait at least before retransmitting ever? + */ +#define MIN_RTT_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 75) + +/** + * Maximum message ID into the future we accept for out-of-order messages. + * If the message is more than this into the future, we drop it. This is + * important both to detect values that are actually in the past, as well + * as to limit adversarially triggerable memory consumption. + * + * Note that right now we have "max_pending_messages = 4" hard-coded in + * the logic below, so a value of 4 would suffice here. But we plan to + * allow larger windows in the future... + */ +#define MAX_OUT_OF_ORDER_DISTANCE 1024 /** - * All the states a connection can be in. + * All the states a channel can be in. */ enum CadetChannelState { @@ -51,9 +83,15 @@ enum CadetChannelState CADET_CHANNEL_NEW, /** - * Connection create message sent, waiting for ACK. + * Channel is to a port that is not open, we're waiting for the + * port to be opened. + */ + CADET_CHANNEL_LOOSE, + + /** + * CHANNEL_OPEN message sent, waiting for CHANNEL_OPEN_ACK. */ - CADET_CHANNEL_SENT, + CADET_CHANNEL_OPEN_SENT, /** * Connection confirmed, ready to carry traffic. @@ -63,138 +101,144 @@ enum CadetChannelState /** - * Info holder for channel messages in queues. + * Info needed to retry a message in case it gets lost. + * Note that we DO use this structure also for unreliable + * messages. */ -struct CadetChannelQueue +struct CadetReliableMessage { /** - * Tunnel Queue. + * Double linked list, FIFO style */ - struct CadetTunnelQueue *tq; + struct CadetReliableMessage *next; /** - * Message type (DATA/DATA_ACK) + * Double linked list, FIFO style */ - uint16_t type; + struct CadetReliableMessage *prev; /** - * Message copy (for DATAs, to start retransmission timer) + * Which channel is this message in? */ - struct CadetReliableMessage *copy; + struct CadetChannel *ch; /** - * Reliability (for DATA_ACKs, to access rel->ack_q) + * Entry in the tunnels queue for this message, NULL if it has left + * the tunnel. Used to cancel transmission in case we receive an + * ACK in time. */ - struct CadetChannelReliability *rel; -}; - + struct CadetTunnelQueueEntry *qe; -/** - * Info needed to retry a message in case it gets lost. - */ -struct CadetReliableMessage -{ /** - * Double linked list, FIFO style + * Data message we are trying to send. */ - struct CadetReliableMessage *next; - struct CadetReliableMessage *prev; + struct GNUNET_CADET_ChannelAppDataMessage *data_message; /** - * Type of message (payload, channel management). + * How soon should we retry if we fail to get an ACK? + * Messages in the queue are sorted by this value. */ - int16_t type; + struct GNUNET_TIME_Absolute next_retry; /** - * Tunnel Reliability queue this message is in. + * How long do we wait for an ACK after transmission? + * Use for the back-off calculation. */ - struct CadetChannelReliability *rel; + struct GNUNET_TIME_Relative retry_delay; /** - * ID of the message (ACK needed to free) + * Time when we first successfully transmitted the message + * (that is, set @e num_transmissions to 1). */ - uint32_t mid; + struct GNUNET_TIME_Absolute first_transmission_time; /** - * Tunnel Queue. + * Identifier of the connection that this message took when it + * was first transmitted. Only useful if @e num_transmissions is 1. */ - struct CadetChannelQueue *chq; + struct GNUNET_CADET_ConnectionTunnelIdentifier connection_taken; /** - * When was this message issued (to calculate ACK delay) + * How often was this message transmitted? #GNUNET_SYSERR if there + * was an error transmitting the message, #GNUNET_NO if it was not + * yet transmitted ever, otherwise the number of (re) transmissions. */ - struct GNUNET_TIME_Absolute timestamp; + int num_transmissions; - /* struct GNUNET_CADET_ChannelAppDataMessage with payload */ }; /** - * Info about the traffic state for a client in a channel. + * List of received out-of-order data messages. */ -struct CadetChannelReliability +struct CadetOutOfOrderMessage { /** - * Channel this is about. + * Double linked list, FIFO style */ - struct CadetChannel *ch; + struct CadetOutOfOrderMessage *next; /** - * DLL of messages sent and not yet ACK'd. + * Double linked list, FIFO style */ - struct CadetReliableMessage *head_sent; - struct CadetReliableMessage *tail_sent; + struct CadetOutOfOrderMessage *prev; /** - * DLL of messages received out of order. + * ID of the message (messages up to this point needed + * before we give this one to the client). */ - struct CadetReliableMessage *head_recv; - struct CadetReliableMessage *tail_recv; + struct ChannelMessageIdentifier mid; /** - * Messages received. + * The envelope with the payload of the out-of-order message */ - unsigned int n_recv; + struct GNUNET_MQ_Envelope *env; - /** - * Next MID to use for outgoing traffic. - */ - uint32_t mid_send; +}; - /** - * Next MID expected for incoming traffic. - */ - uint32_t mid_recv; +/** + * Client endpoint of a `struct CadetChannel`. A channel may be a + * loopback channel, in which case it has two of these endpoints. + * Note that flow control also is required in both directions. + */ +struct CadetChannelClient +{ /** - * Handle for queued unique data CREATE, DATA_ACK. + * Client handle. Not by itself sufficient to designate + * the client endpoint, as the same client handle may + * be used for both the owner and the destination, and + * we thus also need the channel ID to identify the client. */ - struct CadetChannelQueue *uniq; + struct CadetClient *c; /** - * Can we send data to the client? + * Head of DLL of messages received out of order or while client was unready. */ - int client_ready; + struct CadetOutOfOrderMessage *head_recv; /** - * Can the client send data to us? + * Tail DLL of messages received out of order or while client was unready. */ - int client_allowed; + struct CadetOutOfOrderMessage *tail_recv; /** - * Task to resend/poll in case no ACK is received. + * Local tunnel number for this client. + * (if owner >= #GNUNET_CADET_LOCAL_CHANNEL_ID_CLI, + * otherwise < #GNUNET_CADET_LOCAL_CHANNEL_ID_CLI) */ - struct GNUNET_SCHEDULER_Task * retry_task; + struct GNUNET_CADET_ClientChannelNumber ccn; /** - * Counter for exponential backoff. + * Number of entries currently in @a head_recv DLL. */ - struct GNUNET_TIME_Relative retry_timer; + unsigned int num_recv; /** - * How long does it usually take to get an ACK. + * Can we send data to the client? */ - struct GNUNET_TIME_Relative expected_delay; + int client_ready; + }; @@ -209,41 +253,44 @@ struct CadetChannel struct CadetTunnel *t; /** - * Destination port of the channel. + * Client owner of the tunnel, if any. + * (Used if this channel represends the initiating end of the tunnel.) */ - struct GNUNET_HashCode port; + struct CadetChannelClient *owner; /** - * Global channel number ( < GNUNET_CADET_LOCAL_CHANNEL_ID_CLI) + * Client destination of the tunnel, if any. + * (Used if this channel represents the listening end of the tunnel.) */ - struct GNUNET_CADET_ChannelTunnelNumber gid; + struct CadetChannelClient *dest; /** - * Local tunnel number for root (owner) client. - * ( >= GNUNET_CADET_LOCAL_CHANNEL_ID_CLI or 0 ) + * Last entry in the tunnel's queue relating to control messages + * (#GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN or + * #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK). Used to cancel + * transmission in case we receive updated information. */ - struct GNUNET_CADET_ClientChannelNumber lid_root; + struct CadetTunnelQueueEntry *last_control_qe; /** - * Local tunnel number for local destination clients (incoming number) - * ( >= GNUNET_CADET_LOCAL_CHANNEL_ID_SERV or 0). + * Head of DLL of messages sent and not yet ACK'd. */ - struct GNUNET_CADET_ClientChannelNumber lid_dest; + struct CadetReliableMessage *head_sent; /** - * Channel state. + * Tail of DLL of messages sent and not yet ACK'd. */ - enum CadetChannelState state; + struct CadetReliableMessage *tail_sent; /** - * Is the tunnel bufferless (minimum latency)? + * Task to resend/poll in case no ACK is received. */ - int nobuffer; + struct GNUNET_SCHEDULER_Task *retry_control_task; /** - * Is the tunnel reliable? + * Task to resend/poll in case no ACK is received. */ - int reliable; + struct GNUNET_SCHEDULER_Task *retry_data_task; /** * Last time the channel was used @@ -251,21 +298,29 @@ struct CadetChannel struct GNUNET_TIME_Absolute timestamp; /** - * Client owner of the tunnel, if any + * Destination port of the channel. */ - struct CadetClient *root; + struct GNUNET_HashCode port; /** - * Client destination of the tunnel, if any. + * Counter for exponential backoff. */ - struct CadetClient *dest; + struct GNUNET_TIME_Relative retry_time; /** - * Flag to signal the destruction of the channel. - * If this is set to #GNUNET_YES the channel will be destroyed - * when the queue is empty. + * Bitfield of already-received messages past @e mid_recv. */ - int destroy; + uint64_t mid_futures; + + /** + * Next MID expected for incoming traffic. + */ + struct ChannelMessageIdentifier mid_recv; + + /** + * Next MID to use for outgoing traffic. + */ + struct ChannelMessageIdentifier mid_send; /** * Total (reliable) messages pending ACK for this channel. @@ -273,2290 +328,1710 @@ struct CadetChannel unsigned int pending_messages; /** - * Reliability data. - * Only present (non-NULL) at the owner of a tunnel. + * Maximum (reliable) messages pending ACK for this channel + * before we throttle the client. */ - struct CadetChannelReliability *root_rel; + unsigned int max_pending_messages; /** - * Reliability data. - * Only present (non-NULL) at the destination of a tunnel. + * Number identifying this channel in its tunnel. */ - struct CadetChannelReliability *dest_rel; + struct GNUNET_CADET_ChannelTunnelNumber ctn; -}; + /** + * Channel state. + */ + enum CadetChannelState state; + /** + * Count how many ACKs we skipped, used to prevent long + * sequences of ACK skipping. + */ + unsigned int skip_ack_series; -/******************************************************************************/ -/******************************* GLOBALS ***********************************/ -/******************************************************************************/ + /** + * Is the tunnel bufferless (minimum latency)? + */ + int nobuffer; -/** - * Global handle to the statistics service. - */ -extern struct GNUNET_STATISTICS_Handle *stats; + /** + * Is the tunnel reliable? + */ + int reliable; -/** - * Local peer own ID (memory efficient handle). - */ -extern GNUNET_PEER_Id myid; + /** + * Is the tunnel out-of-order? + */ + int out_of_order; + /** + * Is this channel a loopback channel, where the destination is us again? + */ + int is_loopback; -/******************************************************************************/ -/******************************** STATIC ***********************************/ -/******************************************************************************/ + /** + * Flag to signal the destruction of the channel. If this is set to + * #GNUNET_YES the channel will be destroyed once the queue is + * empty. + */ + int destroy; +}; -/** - * Destroy a reliable message after it has been acknowledged, either by - * direct mid ACK or bitfield. Updates the appropriate data structures and - * timers and frees all memory. - * - * @param copy Message that is no longer needed: remote peer got it. - * @param update_time Is the timing information relevant? - * If this message is ACK in a batch the timing information - * is skewed by the retransmission, count only for the - * retransmitted message. - * - * @return #GNUNET_YES if channel was destroyed as a result of the call, - * #GNUNET_NO otherwise. - */ -static int -rel_message_free (struct CadetReliableMessage *copy, int update_time); /** - * send a channel create message. + * Get the static string for identification of the channel. * - * @param ch Channel for which to send. - */ -static void -send_create (struct CadetChannel *ch); - -/** - * Confirm we got a channel create, FWD ack. + * @param ch Channel. * - * @param ch The channel to confirm. - * @param fwd Should we send a FWD ACK? (going dest->root) + * @return Static string with the channel IDs. */ -static void -send_ack (struct CadetChannel *ch, int fwd); - +const char * +GCCH_2s (const struct CadetChannel *ch) +{ + static char buf[128]; + + GNUNET_snprintf (buf, + sizeof (buf), + "Channel %s:%s ctn:%X(%X/%X)", + (GNUNET_YES == ch->is_loopback) + ? "loopback" + : GNUNET_i2s (GCP_get_id (GCT_get_destination (ch->t))), + GNUNET_h2s (&ch->port), + ch->ctn, + (NULL == ch->owner) ? 0 : ntohl (ch->owner->ccn.channel_of_client), + (NULL == ch->dest) ? 0 : ntohl (ch->dest->ccn.channel_of_client)); + return buf; +} /** - * Test if the channel is loopback: both root and dest are on the local peer. + * Get the channel's public ID. * - * @param ch Channel to test. + * @param ch Channel. * - * @return #GNUNET_YES if channel is loopback, #GNUNET_NO otherwise. + * @return ID used to identify the channel with the remote peer. */ -static int -is_loopback (const struct CadetChannel *ch) +struct GNUNET_CADET_ChannelTunnelNumber +GCCH_get_id (const struct CadetChannel *ch) { - if (NULL != ch->t) - return GCT_is_loopback (ch->t); - - return (NULL != ch->root && NULL != ch->dest); + return ch->ctn; } /** - * Save a copy of the data message for later retransmission. + * Release memory associated with @a ccc * - * @param msg Message to copy. - * @param mid Message ID. - * @param rel Reliability data for retransmission. + * @param ccc data structure to clean up */ -static struct CadetReliableMessage * -copy_message (const struct GNUNET_CADET_ChannelAppDataMessage *msg, uint32_t mid, - struct CadetChannelReliability *rel) +static void +free_channel_client (struct CadetChannelClient *ccc) { - struct CadetReliableMessage *copy; - uint16_t size; + struct CadetOutOfOrderMessage *com; - size = ntohs (msg->header.size); - copy = GNUNET_malloc (sizeof (*copy) + size); - copy->mid = mid; - copy->rel = rel; - copy->type = GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA; - GNUNET_memcpy (©[1], msg, size); - - return copy; + while (NULL != (com = ccc->head_recv)) + { + GNUNET_CONTAINER_DLL_remove (ccc->head_recv, + ccc->tail_recv, + com); + ccc->num_recv--; + GNUNET_MQ_discard (com->env); + GNUNET_free (com); + } + GNUNET_free (ccc); } + /** - * We have received a message out of order, or the client is not ready. - * Buffer it until we receive an ACK from the client or the missing - * message from the channel. + * Destroy the given channel. * - * @param msg Message to buffer (MUST be of type CADET_DATA). - * @param rel Reliability data to the corresponding direction. + * @param ch channel to destroy */ static void -add_buffered_data (const struct GNUNET_CADET_ChannelAppDataMessage *msg, - struct CadetChannelReliability *rel) +channel_destroy (struct CadetChannel *ch) { - struct CadetReliableMessage *copy; - struct CadetReliableMessage *prev; - uint32_t mid; - - mid = ntohl (msg->mid); + struct CadetReliableMessage *crm; - LOG (GNUNET_ERROR_TYPE_DEBUG, "add_buffered_data MID %u (%u)\n", - mid, rel->n_recv); - - rel->n_recv++; - - // FIXME do something better than O(n), although n < 64... - // FIXME start from the end (most messages are the latest ones) - for (prev = rel->head_recv; NULL != prev; prev = prev->next) + while (NULL != (crm = ch->head_sent)) { - LOG (GNUNET_ERROR_TYPE_DEBUG, " prev %u\n", prev->mid); - if (prev->mid == mid) + GNUNET_assert (ch == crm->ch); + if (NULL != crm->qe) { - LOG (GNUNET_ERROR_TYPE_DEBUG, " already there!\n"); - rel->n_recv--; - return; - } - else if (GC_is_pid_bigger (prev->mid, mid)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " bingo!\n"); - copy = copy_message (msg, mid, rel); - GNUNET_CONTAINER_DLL_insert_before (rel->head_recv, rel->tail_recv, - prev, copy); - return; + GCT_send_cancel (crm->qe); + crm->qe = NULL; } + GNUNET_CONTAINER_DLL_remove (ch->head_sent, + ch->tail_sent, + crm); + GNUNET_free (crm->data_message); + GNUNET_free (crm); + } + if (NULL != ch->owner) + { + free_channel_client (ch->owner); + ch->owner = NULL; } - copy = copy_message (msg, mid, rel); - LOG (GNUNET_ERROR_TYPE_DEBUG, " insert at tail! (now: %u)\n", rel->n_recv); - GNUNET_CONTAINER_DLL_insert_tail (rel->head_recv, rel->tail_recv, copy); - LOG (GNUNET_ERROR_TYPE_DEBUG, "add_buffered_data END\n"); + if (NULL != ch->dest) + { + free_channel_client (ch->dest); + ch->dest = NULL; + } + if (NULL != ch->last_control_qe) + { + GCT_send_cancel (ch->last_control_qe); + ch->last_control_qe = NULL; + } + if (NULL != ch->retry_data_task) + { + GNUNET_SCHEDULER_cancel (ch->retry_data_task); + ch->retry_data_task = NULL; + } + if (NULL != ch->retry_control_task) + { + GNUNET_SCHEDULER_cancel (ch->retry_control_task); + ch->retry_control_task = NULL; + } + if (GNUNET_NO == ch->is_loopback) + { + GCT_remove_channel (ch->t, + ch, + ch->ctn); + ch->t = NULL; + } + GNUNET_free (ch); } /** - * Add a destination client to a channel, initializing all data structures - * in the channel and the client. + * Send a channel create message. * - * @param ch Channel to which add the destination. - * @param c Client which to add to the channel. + * @param cls Channel for which to send. */ static void -add_destination (struct CadetChannel *ch, struct CadetClient *c) -{ - if (NULL != ch->dest) - { - GNUNET_break (0); - return; - } - - /* Assign local id as destination */ - ch->lid_dest = GML_get_next_ccn (c); - - /* Store in client's hashmap */ - GML_channel_add (c, ch->lid_dest, ch); - - GNUNET_break (NULL == ch->dest_rel); - ch->dest_rel = GNUNET_new (struct CadetChannelReliability); - ch->dest_rel->ch = ch; - ch->dest_rel->expected_delay.rel_value_us = 0; - ch->dest_rel->retry_timer = CADET_RETRANSMIT_TIME; - - ch->dest = c; -} +send_channel_open (void *cls); /** - * Set options in a channel, extracted from a bit flag field. + * Function called once the tunnel confirms that we sent the + * create message. Delays for a bit until we retry. * - * @param ch Channel to set options to. - * @param options Bit array in host byte order. + * @param cls our `struct CadetChannel`. + * @param cid identifier of the connection within the tunnel, NULL + * if transmission failed */ static void -channel_set_options (struct CadetChannel *ch, uint32_t options) +channel_open_sent_cb (void *cls, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) { - ch->nobuffer = (options & GNUNET_CADET_OPTION_NOBUFFER) != 0 ? - GNUNET_YES : GNUNET_NO; - ch->reliable = (options & GNUNET_CADET_OPTION_RELIABLE) != 0 ? - GNUNET_YES : GNUNET_NO; + struct CadetChannel *ch = cls; + + GNUNET_assert (NULL != ch->last_control_qe); + ch->last_control_qe = NULL; + ch->retry_time = GNUNET_TIME_STD_BACKOFF (ch->retry_time); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sent CADET_CHANNEL_OPEN on %s, retrying in %s\n", + GCCH_2s (ch), + GNUNET_STRINGS_relative_time_to_string (ch->retry_time, + GNUNET_YES)); + ch->retry_control_task + = GNUNET_SCHEDULER_add_delayed (ch->retry_time, + &send_channel_open, + ch); } /** - * Get a bit flag field with the options of a channel. + * Send a channel open message. * - * @param ch Channel to get options from. - * - * @return Bit array in host byte order. + * @param cls Channel for which to send. */ -static uint32_t -channel_get_options (struct CadetChannel *ch) +static void +send_channel_open (void *cls) { + struct CadetChannel *ch = cls; + struct GNUNET_CADET_ChannelOpenMessage msgcc; uint32_t options; + ch->retry_control_task = NULL; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending CHANNEL_OPEN message for %s\n", + GCCH_2s (ch)); options = 0; if (ch->nobuffer) options |= GNUNET_CADET_OPTION_NOBUFFER; if (ch->reliable) options |= GNUNET_CADET_OPTION_RELIABLE; - - return options; + if (ch->out_of_order) + options |= GNUNET_CADET_OPTION_OUT_OF_ORDER; + msgcc.header.size = htons (sizeof (msgcc)); + msgcc.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN); + msgcc.opt = htonl (options); + msgcc.port = ch->port; + msgcc.ctn = ch->ctn; + ch->state = CADET_CHANNEL_OPEN_SENT; + if (NULL != ch->last_control_qe) + GCT_send_cancel (ch->last_control_qe); + ch->last_control_qe = GCT_send (ch->t, + &msgcc.header, + &channel_open_sent_cb, + ch); + GNUNET_assert (NULL == ch->retry_control_task); } /** - * Notify a client that the channel is no longer valid. - * - * @param ch Channel that is destroyed. - * @param local_only Should we avoid sending it to other peers? + * Function called once and only once after a channel was bound + * to its tunnel via #GCT_add_channel() is ready for transmission. + * Note that this is only the case for channels that this peer + * initiates, as for incoming channels we assume that they are + * ready for transmission immediately upon receiving the open + * message. Used to bootstrap the #GCT_send() process. + * + * @param ch the channel for which the tunnel is now ready */ -static void -send_destroy (struct CadetChannel *ch, int local_only) +void +GCCH_tunnel_up (struct CadetChannel *ch) { - struct GNUNET_CADET_ChannelManageMessage msg; - - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY); - msg.header.size = htons (sizeof (msg)); - msg.ctn = ch->gid; - - /* If root is not NULL, notify. - * If it's NULL, check lid_root. When a local destroy comes in, root - * is set to NULL but lid_root is left untouched. In this case, do nothing, - * the client is the one who requested the channel to be destroyed. - */ - if (NULL != ch->root) - GML_send_channel_destroy (ch->root, ch->lid_root); - else if (0 == ch->lid_root.channel_of_client && GNUNET_NO == local_only) - GCCH_send_prebuilt_message (&msg.header, ch, GNUNET_NO, NULL); - - if (NULL != ch->dest) - GML_send_channel_destroy (ch->dest, ch->lid_dest); - else if (0 == ch->lid_dest.channel_of_client && GNUNET_NO == local_only) - GCCH_send_prebuilt_message (&msg.header, ch, GNUNET_YES, NULL); + GNUNET_assert (NULL == ch->retry_control_task); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Tunnel up, sending CHANNEL_OPEN on %s now\n", + GCCH_2s (ch)); + ch->retry_control_task + = GNUNET_SCHEDULER_add_now (&send_channel_open, + ch); } /** - * Notify the destination client that a new incoming channel was created. + * Create a new channel. * - * @param ch Channel that was created. + * @param owner local client owning the channel + * @param ccn local number of this channel at the @a owner + * @param destination peer to which we should build the channel + * @param port desired port at @a destination + * @param options options for the channel + * @return handle to the new channel */ -static void -send_client_create (struct CadetChannel *ch) +struct CadetChannel * +GCCH_channel_local_new (struct CadetClient *owner, + struct GNUNET_CADET_ClientChannelNumber ccn, + struct CadetPeer *destination, + const struct GNUNET_HashCode *port, + uint32_t options) { - uint32_t opt; - - if (NULL == ch->dest) - return; - - opt = 0; - opt |= GNUNET_YES == ch->reliable ? GNUNET_CADET_OPTION_RELIABLE : 0; - opt |= GNUNET_YES == ch->nobuffer ? GNUNET_CADET_OPTION_NOBUFFER : 0; - GML_send_channel_create (ch->dest, - ch->lid_dest, - &ch->port, - opt, - GCT_get_destination (ch->t)); - -} + struct CadetChannel *ch; + struct CadetChannelClient *ccco; + ccco = GNUNET_new (struct CadetChannelClient); + ccco->c = owner; + ccco->ccn = ccn; + ccco->client_ready = GNUNET_YES; -/** - * Send data to a client. - * - * If the client is ready, send directly, otherwise buffer while listening - * for a local ACK. - * - * @param ch Channel - * @param msg Message. - * @param fwd Is this a fwd (root->dest) message? - */ -static void -send_client_data (struct CadetChannel *ch, - const struct GNUNET_CADET_ChannelAppDataMessage *msg, - int fwd) -{ - if (fwd) - { - if (ch->dest_rel->client_ready) + ch = GNUNET_new (struct CadetChannel); + ch->mid_recv.mid = htonl (1); /* The OPEN_ACK counts as message 0! */ + ch->nobuffer = (0 != (options & GNUNET_CADET_OPTION_NOBUFFER)); + ch->reliable = (0 != (options & GNUNET_CADET_OPTION_RELIABLE)); + ch->out_of_order = (0 != (options & GNUNET_CADET_OPTION_OUT_OF_ORDER)); + ch->max_pending_messages = (ch->nobuffer) ? 1 : 4; /* FIXME: 4!? Do not hardcode! */ + ch->owner = ccco; + ch->port = *port; + if (0 == memcmp (&my_full_id, + GCP_get_id (destination), + sizeof (struct GNUNET_PeerIdentity))) + { + struct CadetClient *c; + + ch->is_loopback = GNUNET_YES; + c = GNUNET_CONTAINER_multihashmap_get (open_ports, + port); + if (NULL == c) { - GML_send_data (ch->dest, msg, ch->lid_dest); - ch->dest_rel->client_ready = GNUNET_NO; - ch->dest_rel->mid_recv++; + /* port closed, wait for it to possibly open */ + ch->state = CADET_CHANNEL_LOOSE; + (void) GNUNET_CONTAINER_multihashmap_put (loose_channels, + port, + ch, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Created loose incoming loopback channel to port %s\n", + GNUNET_h2s (&ch->port)); } else - add_buffered_data (msg, ch->dest_rel); + { + GCCH_bind (ch, + c); + } } else { - if (ch->root_rel->client_ready) - { - GML_send_data (ch->root, msg, ch->lid_root); - ch->root_rel->client_ready = GNUNET_NO; - ch->root_rel->mid_recv++; - } - else - add_buffered_data (msg, ch->root_rel); + ch->t = GCP_get_tunnel (destination, + GNUNET_YES); + ch->retry_time = CADET_INITIAL_RETRANSMIT_TIME; + ch->ctn = GCT_add_channel (ch->t, + ch); } + GNUNET_STATISTICS_update (stats, + "# channels", + 1, + GNUNET_NO); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Created channel to port %s at peer %s for %s using %s\n", + GNUNET_h2s (port), + GCP_2s (destination), + GSC_2s (owner), + (GNUNET_YES == ch->is_loopback) ? "loopback" : GCT_2s (ch->t)); + return ch; } /** - * Send a buffered message to the client, for in order delivery or - * as result of client ACK. + * We had an incoming channel to a port that is closed. + * It has not been opened for a while, drop it. * - * @param ch Channel on which to empty the message buffer. - * @param c Client to send to. - * @param fwd Is this to send FWD data?. + * @param cls the channel to drop */ static void -send_client_buffered_data (struct CadetChannel *ch, - struct CadetClient *c, - int fwd) +timeout_closed_cb (void *cls) { - struct CadetReliableMessage *copy; - struct CadetChannelReliability *rel; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "send_buffered_data\n"); - rel = fwd ? ch->dest_rel : ch->root_rel; - if (GNUNET_NO == rel->client_ready) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "client not ready\n"); - return; - } + struct CadetChannel *ch = cls; - copy = rel->head_recv; - /* We never buffer channel management messages */ - if (NULL != copy) - { - if (copy->mid == rel->mid_recv || GNUNET_NO == ch->reliable) - { - struct GNUNET_CADET_ChannelAppDataMessage *msg = (struct GNUNET_CADET_ChannelAppDataMessage *) ©[1]; - - LOG (GNUNET_ERROR_TYPE_DEBUG, " have %u! now expecting %u\n", - copy->mid, rel->mid_recv + 1); - send_client_data (ch, msg, fwd); - rel->n_recv--; - GNUNET_CONTAINER_DLL_remove (rel->head_recv, rel->tail_recv, copy); - LOG (GNUNET_ERROR_TYPE_DEBUG, " free copy recv MID %u (%p), %u left\n", - copy->mid, copy, rel->n_recv); - GNUNET_free (copy); - GCCH_send_data_ack (ch, fwd); - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " reliable && don't have %u, next is %u\n", - rel->mid_recv, copy->mid); - if (GNUNET_YES == ch->destroy) - { - /* We don't have the next data piece and the remote peer has closed the - * channel. We won't receive it anymore, so just destroy the channel. - * FIXME: wait some time to allow other connections to - * deliver missing messages - */ - send_destroy (ch, GNUNET_YES); - GCCH_destroy (ch); - } - } - } - LOG (GNUNET_ERROR_TYPE_DEBUG, "send_buffered_data END\n"); + ch->retry_control_task = NULL; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Closing incoming channel to port %s from peer %s due to timeout\n", + GNUNET_h2s (&ch->port), + GCP_2s (GCT_get_destination (ch->t))); + channel_destroy (ch); } /** - * Allow a client to send more data. - * - * In case the client was already allowed to send data, do nothing. + * Create a new channel based on a request coming in over the network. * - * @param ch Channel. - * @param fwd Is this a FWD ACK? (FWD ACKs are sent to root) + * @param t tunnel to the remote peer + * @param ctn identifier of this channel in the tunnel + * @param port desired local port + * @param options options for the channel + * @return handle to the new channel */ -static void -send_client_ack (struct CadetChannel *ch, int fwd) +struct CadetChannel * +GCCH_channel_incoming_new (struct CadetTunnel *t, + struct GNUNET_CADET_ChannelTunnelNumber ctn, + const struct GNUNET_HashCode *port, + uint32_t options) { - struct CadetChannelReliability *rel = fwd ? ch->root_rel : ch->dest_rel; - struct CadetClient *c = fwd ? ch->root : ch->dest; + struct CadetChannel *ch; + struct CadetClient *c; + + ch = GNUNET_new (struct CadetChannel); + ch->port = *port; + ch->t = t; + ch->ctn = ctn; + ch->retry_time = CADET_INITIAL_RETRANSMIT_TIME; + ch->nobuffer = (0 != (options & GNUNET_CADET_OPTION_NOBUFFER)); + ch->reliable = (0 != (options & GNUNET_CADET_OPTION_RELIABLE)); + ch->out_of_order = (0 != (options & GNUNET_CADET_OPTION_OUT_OF_ORDER)); + ch->max_pending_messages = (ch->nobuffer) ? 1 : 4; /* FIXME: 4!? Do not hardcode! */ + GNUNET_STATISTICS_update (stats, + "# channels", + 1, + GNUNET_NO); + c = GNUNET_CONTAINER_multihashmap_get (open_ports, + port); if (NULL == c) { - GNUNET_break (GNUNET_NO != ch->destroy); - return; + /* port closed, wait for it to possibly open */ + ch->state = CADET_CHANNEL_LOOSE; + (void) GNUNET_CONTAINER_multihashmap_put (loose_channels, + port, + ch, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + GNUNET_assert (NULL == ch->retry_control_task); + ch->retry_control_task + = GNUNET_SCHEDULER_add_delayed (TIMEOUT_CLOSED_PORT, + &timeout_closed_cb, + ch); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Created loose incoming channel to port %s from peer %s\n", + GNUNET_h2s (&ch->port), + GCP_2s (GCT_get_destination (ch->t))); } - LOG (GNUNET_ERROR_TYPE_DEBUG, - " sending %s ack to client on channel %s\n", - GC_f2s (fwd), GCCH_2s (ch)); - - if (NULL == rel) + else { - GNUNET_break (0); - return; + GCCH_bind (ch, + c); } - - if (GNUNET_YES == rel->client_allowed) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " already allowed\n"); - return; - } - rel->client_allowed = GNUNET_YES; - - GML_send_ack (c, fwd ? ch->lid_root : ch->lid_dest); + GNUNET_STATISTICS_update (stats, + "# channels", + 1, + GNUNET_NO); + return ch; } /** - * Notify the root that the destination rejected the channel. + * Function called once the tunnel confirms that we sent the + * ACK message. Just remembers it was sent, we do not expect + * ACKs for ACKs ;-). * - * @param ch Rejected channel. + * @param cls our `struct CadetChannel`. + * @param cid identifier of the connection within the tunnel, NULL + * if transmission failed */ static void -send_client_nack (struct CadetChannel *ch) +send_ack_cb (void *cls, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) { - if (NULL == ch->root) - { - GNUNET_break (0); - return; - } - GML_send_channel_nack (ch->root, ch->lid_root); + struct CadetChannel *ch = cls; + + GNUNET_assert (NULL != ch->last_control_qe); + ch->last_control_qe = NULL; } /** - * We haven't received an ACK after a certain time: restransmit the message. + * Compute and send the current #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK to the other peer. * - * @param cls Closure (CadetChannelReliability with the message to restransmit) + * @param ch channel to send the #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK for */ static void -channel_retransmit_message (void *cls) +send_channel_data_ack (struct CadetChannel *ch) { - struct CadetChannelReliability *rel = cls; - struct CadetReliableMessage *copy; - struct CadetChannel *ch; - struct GNUNET_CADET_ChannelAppDataMessage *payload; - int fwd; - - rel->retry_task = NULL; - ch = rel->ch; - copy = rel->head_sent; - if (NULL == copy) - { - GNUNET_break (0); // FIXME tripped in rps testcase - return; - } - - payload = (struct GNUNET_CADET_ChannelAppDataMessage *) ©[1]; - fwd = (rel == ch->root_rel); - - /* Message not found in the queue that we are going to use. */ - LOG (GNUNET_ERROR_TYPE_DEBUG, "RETRANSMIT MID %u\n", copy->mid); + struct GNUNET_CADET_ChannelDataAckMessage msg; - GCCH_send_prebuilt_message (&payload->header, ch, fwd, copy); - GNUNET_STATISTICS_update (stats, "# data retransmitted", 1, GNUNET_NO); + if (GNUNET_NO == ch->reliable) + return; /* no ACKs */ + msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK); + msg.header.size = htons (sizeof (msg)); + msg.ctn = ch->ctn; + msg.mid.mid = htonl (ntohl (ch->mid_recv.mid)); + msg.futures = GNUNET_htonll (ch->mid_futures); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending DATA_ACK %u:%llX via %s\n", + (unsigned int) ntohl (msg.mid.mid), + (unsigned long long) ch->mid_futures, + GCCH_2s (ch)); + if (NULL != ch->last_control_qe) + GCT_send_cancel (ch->last_control_qe); + ch->last_control_qe = GCT_send (ch->t, + &msg.header, + &send_ack_cb, + ch); } /** - * We haven't received an Channel ACK after a certain time: resend the CREATE. + * Send our initial #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK to the client confirming that the + * connection is up. * - * @param cls Closure (CadetChannelReliability of the channel to recreate) + * @param cls the `struct CadetChannel` */ static void -channel_recreate (void *cls) +send_open_ack (void *cls) { - struct CadetChannelReliability *rel = cls; - - rel->retry_task = NULL; - LOG (GNUNET_ERROR_TYPE_DEBUG, "RE-CREATE\n"); - GNUNET_STATISTICS_update (stats, - "# data retransmitted", 1, GNUNET_NO); + struct CadetChannel *ch = cls; + struct GNUNET_CADET_ChannelManageMessage msg; - if (rel == rel->ch->root_rel) - { - send_create (rel->ch); - } - else if (rel == rel->ch->dest_rel) - { - send_ack (rel->ch, GNUNET_YES); - } - else - { - GNUNET_break (0); - } + ch->retry_control_task = NULL; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending CHANNEL_OPEN_ACK on %s\n", + GCCH_2s (ch)); + msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK); + msg.header.size = htons (sizeof (msg)); + msg.reserved = htonl (0); + msg.ctn = ch->ctn; + if (NULL != ch->last_control_qe) + GCT_send_cancel (ch->last_control_qe); + ch->last_control_qe = GCT_send (ch->t, + &msg.header, + &send_ack_cb, + ch); } /** - * Message has been sent: start retransmission timer. + * We got a #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN message again for + * this channel. If the binding was successful, (re)transmit the + * #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK. * - * @param cls Closure (queue structure). - * @param t Tunnel. - * @param q Queue handler (no longer valid). - * @param type Type of message. - * @param size Size of the message. + * @param ch channel that got the duplicate open + * @param cti identifier of the connection that delivered the message */ -static void -ch_message_sent (void *cls, - struct CadetTunnel *t, - struct CadetTunnelQueue *q, - uint16_t type, size_t size) +void +GCCH_handle_duplicate_open (struct CadetChannel *ch, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti) { - struct CadetChannelQueue *chq = cls; - struct CadetReliableMessage *copy = chq->copy; - struct CadetChannelReliability *rel; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "channel_message_sent callback %s\n", - GC_m2s (chq->type)); - - switch (chq->type) + if (NULL == ch->dest) { - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA: - LOG (GNUNET_ERROR_TYPE_DEBUG, "data MID %u sent\n", copy->mid); - GNUNET_assert (chq == copy->chq); - copy->timestamp = GNUNET_TIME_absolute_get (); - rel = copy->rel; - if (NULL == rel->retry_task) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " scheduling retry in %d * %s\n", - CADET_RETRANSMIT_MARGIN, - GNUNET_STRINGS_relative_time_to_string (rel->expected_delay, - GNUNET_YES)); - if (0 != rel->expected_delay.rel_value_us) - { - rel->retry_timer = - GNUNET_TIME_relative_saturating_multiply (rel->expected_delay, - CADET_RETRANSMIT_MARGIN); - } - else - { - rel->retry_timer = CADET_RETRANSMIT_TIME; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " using delay %s\n", - GNUNET_STRINGS_relative_time_to_string (rel->retry_timer, - GNUNET_NO)); - rel->retry_task = - GNUNET_SCHEDULER_add_delayed (rel->retry_timer, - &channel_retransmit_message, rel); - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "retry running %p\n", rel->retry_task); - } - copy->chq = NULL; - break; - - - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK: - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN: - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK: - LOG (GNUNET_ERROR_TYPE_DEBUG, "sent %s\n", GC_m2s (chq->type)); - rel = chq->rel; - GNUNET_assert (rel->uniq == chq); - rel->uniq = NULL; - - if (CADET_CHANNEL_READY != rel->ch->state - && GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK != type - && GNUNET_NO == rel->ch->destroy) - { - GNUNET_assert (NULL == rel->retry_task); - LOG (GNUNET_ERROR_TYPE_DEBUG, "STD BACKOFF %s\n", - GNUNET_STRINGS_relative_time_to_string (rel->retry_timer, - GNUNET_NO)); - rel->retry_timer = GNUNET_TIME_STD_BACKOFF (rel->retry_timer); - rel->retry_task = GNUNET_SCHEDULER_add_delayed (rel->retry_timer, - &channel_recreate, rel); - } - break; - - default: - GNUNET_break (0); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Ignoring duplicate CHANNEL_OPEN on %s: port is closed\n", + GCCH_2s (ch)); + return; } - - GNUNET_free (chq); + if (NULL != ch->retry_control_task) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Ignoring duplicate CHANNEL_OPEN on %s: control message is pending\n", + GCCH_2s (ch)); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Retransmitting CHANNEL_OPEN_ACK on %s\n", + GCCH_2s (ch)); + ch->retry_control_task + = GNUNET_SCHEDULER_add_now (&send_open_ack, + ch); } /** - * send a channel create message. + * Send a #GNUNET_MESSAGE_TYPE_CADET_LOCAL_ACK to the client to solicit more messages. * - * @param ch Channel for which to send. + * @param ch channel the ack is for + * @param to_owner #GNUNET_YES to send to owner, + * #GNUNET_NO to send to dest */ static void -send_create (struct CadetChannel *ch) +send_ack_to_client (struct CadetChannel *ch, + int to_owner) { - struct GNUNET_CADET_ChannelOpenMessage msgcc; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_CADET_LocalAck *ack; + struct CadetChannelClient *ccc; - msgcc.header.size = htons (sizeof (msgcc)); - msgcc.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN); - msgcc.ctn = ch->gid; - msgcc.port = ch->port; - msgcc.opt = htonl (channel_get_options (ch)); - - GCCH_send_prebuilt_message (&msgcc.header, ch, GNUNET_YES, NULL); + ccc = (GNUNET_YES == to_owner) ? ch->owner : ch->dest; + if (NULL == ccc) + { + /* This can happen if we are just getting ACKs after + our local client already disconnected. */ + GNUNET_assert (GNUNET_YES == ch->destroy); + return; + } + env = GNUNET_MQ_msg (ack, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_ACK); + ack->ccn = ccc->ccn; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending CADET_LOCAL_ACK to %s (%s) at ccn %X (%u/%u pending)\n", + GSC_2s (ccc->c), + (GNUNET_YES == to_owner) ? "owner" : "dest", + ntohl (ack->ccn.channel_of_client), + ch->pending_messages, + ch->max_pending_messages); + GSC_send_to_client (ccc->c, + env); } /** - * Confirm we got a channel create or FWD ack. + * A client is bound to the port that we have a channel + * open to. Send the acknowledgement for the connection + * request and establish the link with the client. * - * @param ch The channel to confirm. - * @param fwd Should we send a FWD ACK? (going dest->root) + * @param ch open incoming channel + * @param c client listening on the respective port */ -static void -send_ack (struct CadetChannel *ch, int fwd) +void +GCCH_bind (struct CadetChannel *ch, + struct CadetClient *c) { - struct GNUNET_CADET_ChannelManageMessage msg; + uint32_t options; + struct CadetChannelClient *cccd; - msg.header.size = htons (sizeof (msg)); - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK); LOG (GNUNET_ERROR_TYPE_DEBUG, - " sending channel %s ack for channel %s\n", - GC_f2s (fwd), GCCH_2s (ch)); - - msg.ctn =ch->gid; - GCCH_send_prebuilt_message (&msg.header, ch, !fwd, NULL); + "Binding %s from %s to port %s of %s\n", + GCCH_2s (ch), + GCT_2s (ch->t), + GNUNET_h2s (&ch->port), + GSC_2s (c)); + if (NULL != ch->retry_control_task) + { + /* there might be a timeout task here */ + GNUNET_SCHEDULER_cancel (ch->retry_control_task); + ch->retry_control_task = NULL; + } + options = 0; + if (ch->nobuffer) + options |= GNUNET_CADET_OPTION_NOBUFFER; + if (ch->reliable) + options |= GNUNET_CADET_OPTION_RELIABLE; + if (ch->out_of_order) + options |= GNUNET_CADET_OPTION_OUT_OF_ORDER; + cccd = GNUNET_new (struct CadetChannelClient); + GNUNET_assert (NULL == ch->dest); + ch->dest = cccd; + cccd->c = c; + cccd->client_ready = GNUNET_YES; + cccd->ccn = GSC_bind (c, + ch, + (GNUNET_YES == ch->is_loopback) + ? GCP_get (&my_full_id, + GNUNET_YES) + : GCT_get_destination (ch->t), + &ch->port, + options); + GNUNET_assert (ntohl (cccd->ccn.channel_of_client) < + GNUNET_CADET_LOCAL_CHANNEL_ID_CLI); + ch->mid_recv.mid = htonl (1); /* The OPEN counts as message 0! */ + if (GNUNET_YES == ch->is_loopback) + { + ch->state = CADET_CHANNEL_OPEN_SENT; + GCCH_handle_channel_open_ack (ch, + NULL); + } + else + { + /* notify other peer that we accepted the connection */ + ch->state = CADET_CHANNEL_READY; + ch->retry_control_task + = GNUNET_SCHEDULER_add_now (&send_open_ack, + ch); + } + /* give client it's initial supply of ACKs */ + GNUNET_assert (ntohl (cccd->ccn.channel_of_client) < + GNUNET_CADET_LOCAL_CHANNEL_ID_CLI); + for (unsigned int i=0;i<ch->max_pending_messages;i++) + send_ack_to_client (ch, + GNUNET_NO); } /** - * Send a message and don't keep any info about it: we won't need to cancel it - * or resend it. + * One of our clients has disconnected, tell the other one that we + * are finished. Done asynchronously to avoid concurrent modification + * issues if this is the same client. * - * @param msg Header of the message to fire away. - * @param ch Channel on which the message should go. - * @param force Is this a forced (undroppable) message? + * @param cls the `struct CadetChannel` where one of the ends is now dead */ static void -fire_and_forget (const struct GNUNET_MessageHeader *msg, - struct CadetChannel *ch, - int force) +signal_remote_destroy_cb (void *cls) { - GNUNET_break (NULL == - GCT_send_prebuilt_message (msg, ch->t, NULL, - force, NULL, NULL)); + struct CadetChannel *ch = cls; + struct CadetChannelClient *ccc; + + /* Find which end is left... */ + ch->retry_control_task = NULL; + ccc = (NULL != ch->owner) ? ch->owner : ch->dest; + GSC_handle_remote_channel_destroy (ccc->c, + ccc->ccn, + ch); + channel_destroy (ch); } /** - * Notify that a channel create didn't succeed. + * Destroy locally created channel. Called by the local client, so no + * need to tell the client. * - * @param ch The channel to reject. + * @param ch channel to destroy + * @param c client that caused the destruction + * @param ccn client number of the client @a c */ -static void -send_nack (struct CadetChannel *ch) +void +GCCH_channel_local_destroy (struct CadetChannel *ch, + struct CadetClient *c, + struct GNUNET_CADET_ClientChannelNumber ccn) { - struct GNUNET_CADET_ChannelManageMessage msg; - - msg.header.size = htons (sizeof (msg)); - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_NACK_DEPRECATED); LOG (GNUNET_ERROR_TYPE_DEBUG, - " sending channel NACK for channel %s\n", + "%s asks for destruction of %s\n", + GSC_2s (c), GCCH_2s (ch)); + GNUNET_assert (NULL != c); + if ( (NULL != ch->owner) && + (c == ch->owner->c) && + (ccn.channel_of_client == ch->owner->ccn.channel_of_client) ) + { + free_channel_client (ch->owner); + ch->owner = NULL; + } + else if ( (NULL != ch->dest) && + (c == ch->dest->c) && + (ccn.channel_of_client == ch->dest->ccn.channel_of_client) ) + { + free_channel_client (ch->dest); + ch->dest = NULL; + } + else + { + GNUNET_assert (0); + } - msg.ctn = ch->gid; - GCCH_send_prebuilt_message (&msg.header, ch, GNUNET_NO, NULL); -} - - -/** - * Destroy all reliable messages queued for a channel, - * during a channel destruction. - * Frees the reliability structure itself. - * - * @param rel Reliability data for a channel. - */ -static void -channel_rel_free_all (struct CadetChannelReliability *rel) -{ - struct CadetReliableMessage *copy; - struct CadetReliableMessage *next; - - if (NULL == rel) - return; - - for (copy = rel->head_recv; NULL != copy; copy = next) + if (GNUNET_YES == ch->destroy) { - next = copy->next; - GNUNET_CONTAINER_DLL_remove (rel->head_recv, rel->tail_recv, copy); - LOG (GNUNET_ERROR_TYPE_DEBUG, " COPYFREE ALL RECV %p\n", copy); - GNUNET_break (NULL == copy->chq); - GNUNET_free (copy); + /* other end already destroyed, with the local client gone, no need + to finish transmissions, just destroy immediately. */ + channel_destroy (ch); + return; } - for (copy = rel->head_sent; NULL != copy; copy = next) + if ( (NULL != ch->head_sent) && + ( (NULL != ch->owner) || + (NULL != ch->dest) ) ) { - next = copy->next; - GNUNET_CONTAINER_DLL_remove (rel->head_sent, rel->tail_sent, copy); - LOG (GNUNET_ERROR_TYPE_DEBUG, " COPYFREE ALL SEND %p\n", copy); - if (NULL != copy->chq) - { - if (NULL != copy->chq->tq) - { - GCT_cancel (copy->chq->tq); - /* ch_message_sent will free copy->q */ - } - else - { - GNUNET_free (copy->chq); - GNUNET_break (0); - } - } - GNUNET_free (copy); + /* Wait for other end to destroy us as well, + and otherwise allow send queue to be transmitted first */ + ch->destroy = GNUNET_YES; + return; } - if (NULL != rel->uniq && NULL != rel->uniq->tq) + if ( (GNUNET_YES == ch->is_loopback) && + ( (NULL != ch->owner) || + (NULL != ch->dest) ) ) { - GCT_cancel (rel->uniq->tq); - /* ch_message_sent is called freeing uniq */ + if (NULL != ch->retry_control_task) + GNUNET_SCHEDULER_cancel (ch->retry_control_task); + ch->retry_control_task + = GNUNET_SCHEDULER_add_now (&signal_remote_destroy_cb, + ch); + return; } - if (NULL != rel->retry_task) + if (GNUNET_NO == ch->is_loopback) { - GNUNET_SCHEDULER_cancel (rel->retry_task); - rel->retry_task = NULL; + /* If the we ever sent the CHANNEL_CREATE, we need to send a destroy message. */ + switch (ch->state) + { + case CADET_CHANNEL_NEW: + /* We gave up on a channel that we created as a client to a remote + target, but that never went anywhere. Nothing to do here. */ + break; + case CADET_CHANNEL_LOOSE: + GSC_drop_loose_channel (&ch->port, + ch); + break; + default: + GCT_send_channel_destroy (ch->t, + ch->ctn); + } } - GNUNET_free (rel); + /* Nothing left to do, just finish destruction */ + channel_destroy (ch); } /** - * Mark future messages as ACK'd. - * - * @param rel Reliability data. - * @param msg DataACK message with a bitfield of future ACK'd messages. + * We got an acknowledgement for the creation of the channel + * (the port is open on the other side). Begin transmissions. * - * @return How many messages have been freed. + * @param ch channel to destroy + * @param cti identifier of the connection that delivered the message */ -static unsigned int -channel_rel_free_sent (struct CadetChannelReliability *rel, - const struct GNUNET_CADET_ChannelDataAckMessage *msg) +void +GCCH_handle_channel_open_ack (struct CadetChannel *ch, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti) { - struct CadetReliableMessage *copy; - struct CadetReliableMessage *next; - uint64_t bitfield; - uint64_t mask; - uint32_t mid; - uint32_t target; - unsigned int i; - unsigned int r; - - bitfield = msg->futures; - mid = ntohl (msg->mid); - LOG (GNUNET_ERROR_TYPE_DEBUG, "free_sent_reliable %u %lX\n", mid, bitfield); - LOG (GNUNET_ERROR_TYPE_DEBUG, " rel %p, head %p\n", rel, rel->head_sent); - for (i = 0, r = 0, copy = rel->head_sent; - i < 64 && NULL != copy && 0 != bitfield; - i++) + switch (ch->state) { - LOG (GNUNET_ERROR_TYPE_DEBUG, " trying bit %u (mid %u)\n", i, mid + i + 1); - mask = 0x1LL << i; - if (0 == (bitfield & mask)) - continue; - - LOG (GNUNET_ERROR_TYPE_DEBUG, " set!\n"); - /* Bit was set, clear the bit from the bitfield */ - bitfield &= ~mask; - - /* The i-th bit was set. Do we have that copy? */ - /* Skip copies with mid < target */ - target = mid + i + 1; - LOG (GNUNET_ERROR_TYPE_DEBUG, " target %u\n", target); - while (NULL != copy && GC_is_pid_bigger (target, copy->mid)) - copy = copy->next; - - /* Did we run out of copies? (previously freed, it's ok) */ - if (NULL == copy) + case CADET_CHANNEL_NEW: + /* this should be impossible */ + GNUNET_break (0); + break; + case CADET_CHANNEL_LOOSE: + /* This makes no sense. */ + GNUNET_break_op (0); + break; + case CADET_CHANNEL_OPEN_SENT: + if (NULL == ch->owner) { - LOG (GNUNET_ERROR_TYPE_DEBUG, "run out of copies...\n"); - return r; + /* We're not the owner, wrong direction! */ + GNUNET_break_op (0); + return; } - - /* Did we overshoot the target? (previously freed, it's ok) */ - if (GC_is_pid_bigger (copy->mid, target)) + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received CHANNEL_OPEN_ACK for waiting %s, entering READY state\n", + GCCH_2s (ch)); + if (NULL != ch->retry_control_task) /* can be NULL if ch->is_loopback */ { - LOG (GNUNET_ERROR_TYPE_DEBUG, " next copy %u\n", copy->mid); - i += copy->mid - target - 1; /* MID: 90, t = 85, i += 4 (i++ later) */ - mask = (0x1LL << (i + 1)) - 1; /* Mask = i-th bit and all before */ - bitfield &= ~mask; /* Clear all bits up to MID - 1 */ - continue; + GNUNET_SCHEDULER_cancel (ch->retry_control_task); + ch->retry_control_task = NULL; } - - /* Now copy->mid == target, free it */ - next = copy->next; - GNUNET_break (GNUNET_YES != rel_message_free (copy, GNUNET_YES)); - r++; - copy = next; + ch->state = CADET_CHANNEL_READY; + /* On first connect, send client as many ACKs as we allow messages + to be buffered! */ + for (unsigned int i=0;i<ch->max_pending_messages;i++) + send_ack_to_client (ch, + GNUNET_YES); + break; + case CADET_CHANNEL_READY: + /* duplicate ACK, maybe we retried the CREATE. Ignore. */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received duplicate channel OPEN_ACK for %s\n", + GCCH_2s (ch)); + GNUNET_STATISTICS_update (stats, + "# duplicate CREATE_ACKs", + 1, + GNUNET_NO); + break; } - LOG (GNUNET_ERROR_TYPE_DEBUG, "free_sent_reliable END\n"); - return r; } /** - * Destroy a reliable message after it has been acknowledged, either by - * direct mid ACK or bitfield. Updates the appropriate data structures and - * timers and frees all memory. + * Test if element @a e1 comes before element @a e2. * - * @param copy Message that is no longer needed: remote peer got it. - * @param update_time Is the timing information relevant? - * If this message is ACK in a batch the timing information - * is skewed by the retransmission, count only for the - * retransmitted message. - * - * @return #GNUNET_YES if channel was destroyed as a result of the call, - * #GNUNET_NO otherwise. + * @param cls closure, to a flag where we indicate duplicate packets + * @param m1 a message of to sort + * @param m2 another message to sort + * @return #GNUNET_YES if @e1 < @e2, otherwise #GNUNET_NO */ static int -rel_message_free (struct CadetReliableMessage *copy, int update_time) +is_before (void *cls, + struct CadetOutOfOrderMessage *m1, + struct CadetOutOfOrderMessage *m2) { - struct CadetChannelReliability *rel; - struct GNUNET_TIME_Relative time; + int *duplicate = cls; + uint32_t v1 = ntohl (m1->mid.mid); + uint32_t v2 = ntohl (m2->mid.mid); + uint32_t delta; - rel = copy->rel; - LOG (GNUNET_ERROR_TYPE_DEBUG, "Freeing %u\n", copy->mid); - if (GNUNET_YES == update_time) + delta = v2 - v1; + if (0 == delta) + *duplicate = GNUNET_YES; + if (delta > (uint32_t) INT_MAX) { - time = GNUNET_TIME_absolute_get_duration (copy->timestamp); - if (0 == rel->expected_delay.rel_value_us) - rel->expected_delay = time; - else - { - rel->expected_delay.rel_value_us *= 7; - rel->expected_delay.rel_value_us += time.rel_value_us; - rel->expected_delay.rel_value_us /= 8; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " message time %12s\n", - GNUNET_STRINGS_relative_time_to_string (time, GNUNET_NO)); - LOG (GNUNET_ERROR_TYPE_DEBUG, " new delay %12s\n", - GNUNET_STRINGS_relative_time_to_string (rel->expected_delay, - GNUNET_NO)); - rel->retry_timer = rel->expected_delay; + /* in overflow range, we can safely assume we wrapped around */ + return GNUNET_NO; } else { - LOG (GNUNET_ERROR_TYPE_DEBUG, "batch free, ignoring timing\n"); - } - rel->ch->pending_messages--; - if (NULL != copy->chq) - { - GCT_cancel (copy->chq->tq); - /* copy->q is set to NULL by ch_message_sent */ - } - GNUNET_CONTAINER_DLL_remove (rel->head_sent, rel->tail_sent, copy); - LOG (GNUNET_ERROR_TYPE_DEBUG, " free send copy MID %u at %p\n", - copy->mid, copy); - GNUNET_free (copy); - - if (GNUNET_NO != rel->ch->destroy && 0 == rel->ch->pending_messages) - { - GCCH_destroy (rel->ch); + /* result is small, thus v2 > v1, thus m1 < m2 */ return GNUNET_YES; } - return GNUNET_NO; } /** - * Channel was ACK'd by remote peer, mark as ready and cancel retransmission. + * We got payload data for a channel. Pass it on to the client + * and send an ACK to the other end (once flow control allows it!) * - * @param ch Channel to mark as ready. - * @param fwd Was the ACK message a FWD ACK? (dest->root, SYNACK) + * @param ch channel that got data + * @param cti identifier of the connection that delivered the message + * @param msg message that was received */ -static void -channel_confirm (struct CadetChannel *ch, int fwd) +void +GCCH_handle_channel_plaintext_data (struct CadetChannel *ch, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti, + const struct GNUNET_CADET_ChannelAppDataMessage *msg) { - struct CadetChannelReliability *rel; - enum CadetChannelState oldstate; - - rel = fwd ? ch->root_rel : ch->dest_rel; - if (NULL == rel) + struct GNUNET_MQ_Envelope *env; + struct GNUNET_CADET_LocalData *ld; + struct CadetChannelClient *ccc; + size_t payload_size; + struct CadetOutOfOrderMessage *com; + int duplicate; + uint32_t mid_min; + uint32_t mid_max; + uint32_t mid_msg; + uint32_t delta; + + GNUNET_assert (GNUNET_NO == ch->is_loopback); + if ( (GNUNET_YES == ch->destroy) && + (NULL == ch->owner) && + (NULL == ch->dest) ) + { + /* This client is gone, but we still have messages to send to + the other end (which is why @a ch is not yet dead). However, + we cannot pass messages to our client anymore. */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Dropping incoming payload on %s as this end is already closed\n", + GCCH_2s (ch)); + /* send back DESTROY notification to stop further retransmissions! */ + GCT_send_channel_destroy (ch->t, + ch->ctn); + return; + } + payload_size = ntohs (msg->header.size) - sizeof (*msg); + env = GNUNET_MQ_msg_extra (ld, + payload_size, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA); + ld->ccn = (NULL == ch->dest) ? ch->owner->ccn : ch->dest->ccn; + GNUNET_memcpy (&ld[1], + &msg[1], + payload_size); + ccc = (NULL != ch->owner) ? ch->owner : ch->dest; + if ( (GNUNET_YES == ccc->client_ready) && + ( (GNUNET_YES == ch->out_of_order) || + (msg->mid.mid == ch->mid_recv.mid) ) ) { - GNUNET_break (GNUNET_NO != ch->destroy); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Giving %u bytes of payload with MID %u from %s to client %s\n", + (unsigned int) payload_size, + ntohl (msg->mid.mid), + GCCH_2s (ch), + GSC_2s (ccc->c)); + ccc->client_ready = GNUNET_NO; + GSC_send_to_client (ccc->c, + env); + ch->mid_recv.mid = htonl (1 + ntohl (ch->mid_recv.mid)); + ch->mid_futures >>= 1; + send_channel_data_ack (ch); return; } - LOG (GNUNET_ERROR_TYPE_DEBUG, " channel confirm %s %s\n", - GC_f2s (fwd), GCCH_2s (ch)); - oldstate = ch->state; - ch->state = CADET_CHANNEL_READY; - if (CADET_CHANNEL_READY != oldstate || GNUNET_YES == is_loopback (ch)) + if (GNUNET_YES == ch->reliable) { - rel->client_ready = GNUNET_YES; - rel->expected_delay = rel->retry_timer; - LOG (GNUNET_ERROR_TYPE_DEBUG, " confirm retry timer %s\n", - GNUNET_STRINGS_relative_time_to_string (rel->retry_timer, GNUNET_NO)); - if (GCT_get_connections_buffer (ch->t) > 0 || GCT_is_loopback (ch->t)) - send_client_ack (ch, fwd); - - if (NULL != rel->retry_task) + /* check if message ought to be dropped because it is ancient/too distant/duplicate */ + mid_min = ntohl (ch->mid_recv.mid); + mid_max = mid_min + ch->max_pending_messages; + mid_msg = ntohl (msg->mid.mid); + if ( ( (uint32_t) (mid_msg - mid_min) > ch->max_pending_messages) || + ( (uint32_t) (mid_max - mid_msg) > ch->max_pending_messages) ) { - GNUNET_SCHEDULER_cancel (rel->retry_task); - rel->retry_task = NULL; - } - else if (NULL != rel->uniq) - { - GCT_cancel (rel->uniq->tq); - /* ch_message_sent will free and NULL uniq */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "%s at %u drops ancient or far-future message %u\n", + GCCH_2s (ch), + (unsigned int) mid_min, + ntohl (msg->mid.mid)); + + GNUNET_STATISTICS_update (stats, + "# duplicate DATA (ancient or future)", + 1, + GNUNET_NO); + GNUNET_MQ_discard (env); + send_channel_data_ack (ch); + return; } - else if (GNUNET_NO == is_loopback (ch)) + /* mark bit for future ACKs */ + delta = mid_msg - mid_min - 1; /* overflow/underflow are OK here */ + if (delta < 64) { - /* We SHOULD have been trying to retransmit this! */ - GNUNET_break (0); + if (0 != (ch->mid_futures & (1LLU << delta))) + { + /* Duplicate within the queue, drop also */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Duplicate payload of %u bytes on %s (mid %u) dropped\n", + (unsigned int) payload_size, + GCCH_2s (ch), + ntohl (msg->mid.mid)); + GNUNET_STATISTICS_update (stats, + "# duplicate DATA", + 1, + GNUNET_NO); + GNUNET_MQ_discard (env); + send_channel_data_ack (ch); + return; + } + ch->mid_futures |= (1LLU << delta); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Marked bit %llX for mid %u (base: %u); now: %llX\n", + (1LLU << delta), + mid_msg, + mid_min, + ch->mid_futures); } } - - /* In case of a FWD ACK (SYNACK) send a BCK ACK (ACK). */ - if (GNUNET_YES == fwd) - send_ack (ch, GNUNET_NO); -} - - -/** - * Save a copy to retransmit in case it gets lost. - * - * Initializes all needed callbacks and timers. - * - * @param ch Channel this message goes on. - * @param msg Message to copy. - * @param fwd Is this fwd traffic? - */ -static struct CadetReliableMessage * -channel_save_copy (struct CadetChannel *ch, - const struct GNUNET_MessageHeader *msg, - int fwd) -{ - struct CadetChannelReliability *rel; - struct CadetReliableMessage *copy; - uint32_t mid; - uint16_t type; - uint16_t size; - - rel = fwd ? ch->root_rel : ch->dest_rel; - mid = rel->mid_send - 1; - type = ntohs (msg->type); - size = ntohs (msg->size); - - LOG (GNUNET_ERROR_TYPE_DEBUG, "save MID %u %s\n", mid, GC_m2s (type)); - copy = GNUNET_malloc (sizeof (struct CadetReliableMessage) + size); - LOG (GNUNET_ERROR_TYPE_DEBUG, " at %p\n", copy); - copy->mid = mid; - copy->rel = rel; - copy->type = type; - GNUNET_memcpy (©[1], msg, size); - GNUNET_CONTAINER_DLL_insert_tail (rel->head_sent, rel->tail_sent, copy); - ch->pending_messages++; - - return copy; -} - - -/** - * Create a new channel. - * - * @param t Tunnel this channel is in. - * @param owner Client that owns the channel, NULL for foreign channels. - * @param lid_root Local ID for root client. - * - * @return A new initialized channel. NULL on error. - */ -static struct CadetChannel * -channel_new (struct CadetTunnel *t, - struct CadetClient *owner, - struct GNUNET_CADET_ClientChannelNumber lid_root) -{ - struct CadetChannel *ch; - - ch = GNUNET_new (struct CadetChannel); - ch->root = owner; - ch->lid_root = lid_root; - ch->t = t; - - GNUNET_STATISTICS_update (stats, "# channels", 1, GNUNET_NO); - - if (NULL != owner) - { - ch->gid = GCT_get_next_ctn (t); - GML_channel_add (owner, lid_root, ch); - } - GCT_add_channel (t, ch); - - return ch; -} - - -/** - * Handle a loopback message: call the appropriate handler for the message type. - * - * @param ch Channel this message is on. - * @param msgh Message header. - * @param fwd Is this FWD traffic? - */ -void -handle_loopback (struct CadetChannel *ch, - const struct GNUNET_MessageHeader *msgh, - int fwd) -{ - uint16_t type; - - type = ntohs (msgh->type); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Loopback %s %s message!\n", - GC_f2s (fwd), GC_m2s (type)); - - switch (type) + else /* ! ch->reliable */ { - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA: - /* Don't send hop ACK, wait for client to ACK */ - LOG (GNUNET_ERROR_TYPE_DEBUG, "SEND loopback %u (%u)\n", - ntohl (((struct GNUNET_CADET_ChannelAppDataMessage *) msgh)->mid), ntohs (msgh->size)); - GCCH_handle_data (ch, (struct GNUNET_CADET_ChannelAppDataMessage *) msgh, fwd); - break; - - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK: - GCCH_handle_data_ack (ch, - (const struct GNUNET_CADET_ChannelDataAckMessage *) msgh, fwd); - break; - - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN: - GCCH_handle_create (ch->t, - (const struct GNUNET_CADET_ChannelOpenMessage *) msgh); - break; - - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK: - GCCH_handle_ack (ch, - (const struct GNUNET_CADET_ChannelManageMessage *) msgh, - fwd); - break; - - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_NACK_DEPRECATED: - GCCH_handle_nack (ch); - break; - - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY: - GCCH_handle_destroy (ch, - (const struct GNUNET_CADET_ChannelManageMessage *) msgh, - fwd); - break; + /* Channel is unreliable, so we do not ACK. But we also cannot + allow buffering everything, so check if we have space... */ + if (ccc->num_recv >= ch->max_pending_messages) + { + struct CadetOutOfOrderMessage *drop; - default: - GNUNET_break_op (0); + /* Yep, need to drop. Drop the oldest message in + the buffer. */ LOG (GNUNET_ERROR_TYPE_DEBUG, - "end-to-end message not known (%u)\n", - ntohs (msgh->type)); + "Queue full due slow client on %s, dropping oldest message\n", + GCCH_2s (ch)); + GNUNET_STATISTICS_update (stats, + "# messages dropped due to slow client", + 1, + GNUNET_NO); + drop = ccc->head_recv; + GNUNET_CONTAINER_DLL_remove (ccc->head_recv, + ccc->tail_recv, + drop); + ccc->num_recv--; + GNUNET_MQ_discard (drop->env); + GNUNET_free (drop); + } } -} - - - -/******************************************************************************/ -/******************************** API ***********************************/ -/******************************************************************************/ - -/** - * Destroy a channel and free all resources. - * - * @param ch Channel to destroy. - */ -void -GCCH_destroy (struct CadetChannel *ch) -{ - struct CadetClient *c; - struct CadetTunnel *t; - if (NULL == ch) + /* Insert message into sorted out-of-order queue */ + com = GNUNET_new (struct CadetOutOfOrderMessage); + com->mid = msg->mid; + com->env = env; + duplicate = GNUNET_NO; + GNUNET_CONTAINER_DLL_insert_sorted (struct CadetOutOfOrderMessage, + is_before, + &duplicate, + ccc->head_recv, + ccc->tail_recv, + com); + ccc->num_recv++; + if (GNUNET_YES == duplicate) + { + /* Duplicate within the queue, drop also (this is not covered by + the case above if "delta" >= 64, which could be the case if + max_pending_messages is also >= 64 or if our client is unready + and we are seeing retransmissions of the message our client is + blocked on. */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Duplicate payload of %u bytes on %s (mid %u) dropped\n", + (unsigned int) payload_size, + GCCH_2s (ch), + ntohl (msg->mid.mid)); + GNUNET_STATISTICS_update (stats, + "# duplicate DATA", + 1, + GNUNET_NO); + GNUNET_CONTAINER_DLL_remove (ccc->head_recv, + ccc->tail_recv, + com); + ccc->num_recv--; + GNUNET_MQ_discard (com->env); + GNUNET_free (com); + send_channel_data_ack (ch); return; - if (2 == ch->destroy) - return; /* recursive call */ - ch->destroy = 2; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "destroying channel %s:%u\n", - GCT_2s (ch->t), ch->gid); - GCCH_debug (ch, GNUNET_ERROR_TYPE_DEBUG); - - c = ch->root; - if (NULL != c) - { - GML_channel_remove (c, ch->lid_root, ch); - } - - c = ch->dest; - if (NULL != c) - { - GML_channel_remove (c, ch->lid_dest, ch); } - - channel_rel_free_all (ch->root_rel); - channel_rel_free_all (ch->dest_rel); - - t = ch->t; - GCT_remove_channel (t, ch); - GNUNET_STATISTICS_update (stats, "# channels", -1, GNUNET_NO); - - GNUNET_free (ch); - GCT_destroy_if_empty (t); -} - - -/** - * Get the channel's public ID. - * - * @param ch Channel. - * - * @return ID used to identify the channel with the remote peer. - */ -struct GNUNET_CADET_ChannelTunnelNumber -GCCH_get_id (const struct CadetChannel *ch) -{ - return ch->gid; -} - - -/** - * Get the channel tunnel. - * - * @param ch Channel to get the tunnel from. - * - * @return tunnel of the channel. - */ -struct CadetTunnel * -GCCH_get_tunnel (const struct CadetChannel *ch) -{ - return ch->t; -} - - -/** - * Get free buffer space towards the client on a specific channel. - * - * @param ch Channel. - * @param fwd Is query about FWD traffic? - * - * @return Free buffer space [0 - 64] - */ -unsigned int -GCCH_get_buffer (struct CadetChannel *ch, int fwd) -{ - struct CadetChannelReliability *rel; - - rel = fwd ? ch->dest_rel : ch->root_rel; - LOG (GNUNET_ERROR_TYPE_DEBUG, " get buffer, channel %s\n", GCCH_2s (ch)); - GCCH_debug (ch, GNUNET_ERROR_TYPE_DEBUG); - /* If rel is NULL it means that the end is not yet created, - * most probably is a loopback channel at the point of sending - * the ChannelCreate to itself. - */ - if (NULL == rel) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " rel is NULL: max\n"); - return 64; - } - - LOG (GNUNET_ERROR_TYPE_DEBUG, " n_recv %d\n", rel->n_recv); - return (64 - rel->n_recv); -} - - -/** - * Get flow control status of end point: is client allow to send? - * - * @param ch Channel. - * @param fwd Is query about FWD traffic? (Request root status). - * - * @return #GNUNET_YES if client is allowed to send us data. - */ -int -GCCH_get_allowed (struct CadetChannel *ch, int fwd) -{ - struct CadetChannelReliability *rel; - - rel = fwd ? ch->root_rel : ch->dest_rel; - - if (NULL == rel) - { - /* Probably shutting down: root/dest NULL'ed to mark disconnection */ - GNUNET_break (GNUNET_NO != ch->destroy); - return 0; - } - - return rel->client_allowed; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Queued %s payload of %u bytes on %s-%X(%p) (mid %u, need %u first)\n", + (GNUNET_YES == ccc->client_ready) + ? "out-of-order" + : "client-not-ready", + (unsigned int) payload_size, + GCCH_2s (ch), + ntohl (ccc->ccn.channel_of_client), + ccc, + ntohl (msg->mid.mid), + ntohl (ch->mid_recv.mid)); + /* NOTE: this ACK we _could_ skip, as the packet is out-of-order and + the sender may already be transmitting the previous one. Needs + experimental evaluation to see if/when this ACK helps or + hurts. (We might even want another option.) */ + send_channel_data_ack (ch); } /** - * Is the root client for this channel on this peer? - * - * @param ch Channel. - * @param fwd Is this for fwd traffic? - * - * @return #GNUNET_YES in case it is. + * Function called once the tunnel has sent one of our messages. + * If the message is unreliable, simply frees the `crm`. If the + * message was reliable, calculate retransmission time and + * wait for ACK (or retransmit). + * + * @param cls the `struct CadetReliableMessage` that was sent + * @param cid identifier of the connection within the tunnel, NULL + * if transmission failed */ -int -GCCH_is_origin (struct CadetChannel *ch, int fwd) -{ - struct CadetClient *c; - - c = fwd ? ch->root : ch->dest; - return NULL != c; -} +static void +data_sent_cb (void *cls, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid); /** - * Is the destination client for this channel on this peer? - * - * @param ch Channel. - * @param fwd Is this for fwd traffic? + * We need to retry a transmission, the last one took too long to + * be acknowledged. * - * @return #GNUNET_YES in case it is. + * @param cls the `struct CadetChannel` where we need to retransmit */ -int -GCCH_is_terminal (struct CadetChannel *ch, int fwd) +static void +retry_transmission (void *cls) { - struct CadetClient *c; + struct CadetChannel *ch = cls; + struct CadetReliableMessage *crm = ch->head_sent; - c = fwd ? ch->dest : ch->root; - return NULL != c; + ch->retry_data_task = NULL; + GNUNET_assert (NULL == crm->qe); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Retrying transmission on %s of message %u\n", + GCCH_2s (ch), + (unsigned int) ntohl (crm->data_message->mid.mid)); + crm->qe = GCT_send (ch->t, + &crm->data_message->header, + &data_sent_cb, + crm); + GNUNET_assert (NULL == ch->retry_data_task); } /** - * Send an end-to-end ACK message for the most recent in-sequence payload. + * We got an PLAINTEXT_DATA_ACK for a message in our queue, remove it from + * the queue and tell our client that it can send more. * - * If channel is not reliable, do nothing. - * - * @param ch Channel this is about. - * @param fwd Is for FWD traffic? (ACK dest->owner) + * @param ch the channel that got the PLAINTEXT_DATA_ACK + * @param cti identifier of the connection that delivered the message + * @param crm the message that got acknowledged */ -void -GCCH_send_data_ack (struct CadetChannel *ch, int fwd) +static void +handle_matching_ack (struct CadetChannel *ch, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti, + struct CadetReliableMessage *crm) { - struct GNUNET_CADET_ChannelDataAckMessage msg; - struct CadetChannelReliability *rel; - struct CadetReliableMessage *copy; - unsigned int delta; - uint64_t mask; - uint32_t ack; - - if (GNUNET_NO == ch->reliable) - return; - - rel = fwd ? ch->dest_rel : ch->root_rel; - ack = rel->mid_recv - 1; - - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK); - msg.header.size = htons (sizeof (msg)); - msg.ctn = ch->gid; - msg.mid = htonl (ack); - - msg.futures = 0LL; - for (copy = rel->head_recv; NULL != copy; copy = copy->next) - { - if (copy->type != GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA) + GNUNET_CONTAINER_DLL_remove (ch->head_sent, + ch->tail_sent, + crm); + ch->pending_messages--; + GNUNET_assert (ch->pending_messages < ch->max_pending_messages); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received DATA_ACK on %s for message %u (%u ACKs pending)\n", + GCCH_2s (ch), + (unsigned int) ntohl (crm->data_message->mid.mid), + ch->pending_messages); + if (NULL != crm->qe) + { + GCT_send_cancel (crm->qe); + crm->qe = NULL; + } + if ( (1 == crm->num_transmissions) && + (NULL != cti) ) + { + GCC_ack_observed (cti); + if (0 == memcmp (cti, + &crm->connection_taken, + sizeof (struct GNUNET_CADET_ConnectionTunnelIdentifier))) { - LOG (GNUNET_ERROR_TYPE_DEBUG, " Type %s, expected DATA\n", - GC_m2s (copy->type)); - continue; + GCC_latency_observed (cti, + GNUNET_TIME_absolute_get_duration (crm->first_transmission_time)); } - GNUNET_assert (GC_is_pid_bigger(copy->mid, ack)); - delta = copy->mid - (ack + 1); - if (63 < delta) - break; - mask = 0x1LL << delta; - msg.futures |= mask; - LOG (GNUNET_ERROR_TYPE_DEBUG, - " setting bit for %u (delta %u) (%lX) -> %lX\n", - copy->mid, delta, mask, msg.futures); } - - GCCH_send_prebuilt_message (&msg.header, ch, !fwd, NULL); - LOG (GNUNET_ERROR_TYPE_DEBUG, "send_data_ack END\n"); + GNUNET_free (crm->data_message); + GNUNET_free (crm); + send_ack_to_client (ch, + (NULL == ch->owner) + ? GNUNET_NO + : GNUNET_YES); } /** - * Allow a client to send us more data, in case it was choked. + * We got an acknowledgement for payload data for a channel. + * Possibly resume transmissions. * - * @param ch Channel. - * @param fwd Is this about FWD traffic? (Root client). + * @param ch channel that got the ack + * @param cti identifier of the connection that delivered the message + * @param ack details about what was received */ void -GCCH_allow_client (struct CadetChannel *ch, int fwd) +GCCH_handle_channel_plaintext_data_ack (struct CadetChannel *ch, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti, + const struct GNUNET_CADET_ChannelDataAckMessage *ack) { - struct CadetChannelReliability *rel; - unsigned int buffer; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "GMCH allow\n"); + struct CadetReliableMessage *crm; + struct CadetReliableMessage *crmn; + int found; + uint32_t mid_base; + uint64_t mid_mask; + unsigned int delta; - if (CADET_CHANNEL_READY != ch->state) + GNUNET_break (GNUNET_NO == ch->is_loopback); + if (GNUNET_NO == ch->reliable) { - LOG (GNUNET_ERROR_TYPE_DEBUG, " channel not ready yet!\n"); + /* not expecting ACKs on unreliable channel, odd */ + GNUNET_break_op (0); return; } - - if (GNUNET_YES == ch->reliable) - { - rel = fwd ? ch->root_rel : ch->dest_rel; - if (NULL == rel) - { - GNUNET_break (GNUNET_NO != ch->destroy); - return; - } - if (NULL != rel->head_sent) + /* mid_base is the MID of the next message that the + other peer expects (i.e. that is missing!), everything + LOWER (but excluding mid_base itself) was received. */ + mid_base = ntohl (ack->mid.mid); + mid_mask = GNUNET_htonll (ack->futures); + found = GNUNET_NO; + for (crm = ch->head_sent; + NULL != crm; + crm = crmn) + { + crmn = crm->next; + delta = (unsigned int) (ntohl (crm->data_message->mid.mid) - mid_base); + if (delta >= UINT_MAX - ch->max_pending_messages) { - if (64 <= rel->mid_send - rel->head_sent->mid) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " too big MID gap! Wait for ACK.\n"); - return; - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " gap ok: %u - %u\n", - rel->head_sent->mid, rel->mid_send); - struct CadetReliableMessage *aux; - for (aux = rel->head_sent; NULL != aux; aux = aux->next) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " - sent mid %u\n", aux->mid); - } - } + /* overflow, means crm was a bit in the past, so this ACK counts for it. */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got DATA_ACK with base %u satisfying past message %u on %s\n", + (unsigned int) mid_base, + ntohl (crm->data_message->mid.mid), + GCCH_2s (ch)); + handle_matching_ack (ch, + cti, + crm); + found = GNUNET_YES; + continue; } - else + delta--; + if (delta >= 64) + continue; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Testing bit %llX for mid %u (base: %u)\n", + (1LLU << delta), + ntohl (crm->data_message->mid.mid), + mid_base); + if (0 != (mid_mask & (1LLU << delta))) { - LOG (GNUNET_ERROR_TYPE_DEBUG, " head sent is NULL\n"); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got DATA_ACK with mask for %u on %s\n", + ntohl (crm->data_message->mid.mid), + GCCH_2s (ch)); + handle_matching_ack (ch, + cti, + crm); + found = GNUNET_YES; } } - - if (is_loopback (ch)) - buffer = GCCH_get_buffer (ch, fwd); - else - buffer = GCT_get_connections_buffer (ch->t); - - if (0 == buffer) + if (GNUNET_NO == found) { - LOG (GNUNET_ERROR_TYPE_DEBUG, " no buffer space.\n"); + /* ACK for message we already dropped, might have been a + duplicate ACK? Ignore. */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Duplicate DATA_ACK on %s, ignoring\n", + GCCH_2s (ch)); + GNUNET_STATISTICS_update (stats, + "# duplicate DATA_ACKs", + 1, + GNUNET_NO); return; } - - LOG (GNUNET_ERROR_TYPE_DEBUG, " buffer space %u, allowing\n", buffer); - send_client_ack (ch, fwd); + if (NULL != ch->retry_data_task) + { + GNUNET_SCHEDULER_cancel (ch->retry_data_task); + ch->retry_data_task = NULL; + } + if ( (NULL != ch->head_sent) && + (NULL == ch->head_sent->qe) ) + ch->retry_data_task + = GNUNET_SCHEDULER_add_at (ch->head_sent->next_retry, + &retry_transmission, + ch); } /** - * Log channel info. + * Destroy channel, based on the other peer closing the + * connection. Also needs to remove this channel from + * the tunnel. * - * @param ch Channel. - * @param level Debug level to use. + * @param ch channel to destroy + * @param cti identifier of the connection that delivered the message, + * NULL if we are simulating receiving a destroy due to shutdown */ void -GCCH_debug (struct CadetChannel *ch, enum GNUNET_ErrorType level) +GCCH_handle_remote_destroy (struct CadetChannel *ch, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti) { - int do_log; + struct CadetChannelClient *ccc; - do_log = GNUNET_get_log_call_status (level & (~GNUNET_ERROR_TYPE_BULK), - "cadet-chn", - __FILE__, __FUNCTION__, __LINE__); - if (0 == do_log) - return; - - if (NULL == ch) + GNUNET_assert (GNUNET_NO == ch->is_loopback); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received remote channel DESTROY for %s\n", + GCCH_2s (ch)); + if (GNUNET_YES == ch->destroy) { - LOG2 (level, "CHN *** DEBUG NULL CHANNEL ***\n"); + /* Local client already gone, this is instant-death. */ + channel_destroy (ch); return; } - LOG2 (level, "CHN Channel %s:%X (%p)\n", GCT_2s (ch->t), ch->gid, ch); - LOG2 (level, "CHN root %p/%p\n", ch->root, ch->root_rel); - if (NULL != ch->root) - { - LOG2 (level, "CHN cli %s\n", GML_2s (ch->root)); - LOG2 (level, "CHN ready %s\n", ch->root_rel->client_ready ? "YES" : "NO"); - LOG2 (level, "CHN id %X\n", ch->lid_root.channel_of_client); - LOG2 (level, "CHN recv %d\n", ch->root_rel->n_recv); - LOG2 (level, "CHN MID r: %d, s: %d\n", - ch->root_rel->mid_recv, ch->root_rel->mid_send); - } - LOG2 (level, "CHN dest %p/%p\n", - ch->dest, ch->dest_rel); - if (NULL != ch->dest) + ccc = (NULL != ch->owner) ? ch->owner : ch->dest; + if ( (NULL != ccc) && + (NULL != ccc->head_recv) ) { - LOG2 (level, "CHN cli %s\n", GML_2s (ch->dest)); - LOG2 (level, "CHN ready %s\n", ch->dest_rel->client_ready ? "YES" : "NO"); - LOG2 (level, "CHN id %X\n", ch->lid_dest); - LOG2 (level, "CHN recv %d\n", ch->dest_rel->n_recv); - LOG2 (level, "CHN MID r: %d, s: %d\n", - ch->dest_rel->mid_recv, ch->dest_rel->mid_send); - + LOG (GNUNET_ERROR_TYPE_WARNING, + "Lost end of transmission due to remote shutdown on %s\n", + GCCH_2s (ch)); + /* FIXME: change API to notify client about truncated transmission! */ } + ch->destroy = GNUNET_YES; + if (NULL != ccc) + GSC_handle_remote_channel_destroy (ccc->c, + ccc->ccn, + ch); + channel_destroy (ch); } /** - * Handle an ACK given by a client. - * - * Mark client as ready and send him any buffered data we could have for him. + * Test if element @a e1 comes before element @a e2. * - * @param ch Channel. - * @param fwd Is this a "FWD ACK"? (FWD ACKs are sent by dest and go BCK) + * @param cls closure, to a flag where we indicate duplicate packets + * @param crm1 an element of to sort + * @param crm2 another element to sort + * @return #GNUNET_YES if @e1 < @e2, otherwise #GNUNET_NO */ -void -GCCH_handle_local_ack (struct CadetChannel *ch, int fwd) +static int +cmp_crm_by_next_retry (void *cls, + struct CadetReliableMessage *crm1, + struct CadetReliableMessage *crm2) { - struct CadetChannelReliability *rel; - struct CadetClient *c; - - rel = fwd ? ch->dest_rel : ch->root_rel; - c = fwd ? ch->dest : ch->root; - - rel->client_ready = GNUNET_YES; - send_client_buffered_data (ch, c, fwd); - - if (GNUNET_YES == ch->destroy && 0 == rel->n_recv) - { - send_destroy (ch, GNUNET_YES); - GCCH_destroy (ch); - return; - } - /* if loopback is marked for destruction, no need to ACK to the other peer, - * it requested the destruction and is already gone, therefore, else if. - */ - else if (is_loopback (ch)) - { - unsigned int buffer; - - buffer = GCCH_get_buffer (ch, fwd); - if (0 < buffer) - GCCH_allow_client (ch, fwd); - - return; - } - GCT_send_connection_acks (ch->t); + if (crm1->next_retry.abs_value_us < + crm2->next_retry.abs_value_us) + return GNUNET_YES; + return GNUNET_NO; } /** - * Handle data given by a client. - * - * Check whether the client is allowed to send in this tunnel, save if channel - * is reliable and send an ACK to the client if there is still buffer space - * in the tunnel. - * - * @param ch Channel. - * @param c Client which sent the data. - * @param fwd Is this a FWD data? - * @param message Data message. - * @param size Size of data. - * - * @return #GNUNET_OK if everything goes well, #GNUNET_SYSERR in case of en error. + * Function called once the tunnel has sent one of our messages. + * If the message is unreliable, simply frees the `crm`. If the + * message was reliable, calculate retransmission time and + * wait for ACK (or retransmit). + * + * @param cls the `struct CadetReliableMessage` that was sent + * @param cid identifier of the connection within the tunnel, NULL + * if transmission failed */ -int -GCCH_handle_local_data (struct CadetChannel *ch, - struct CadetClient *c, - int fwd, - const struct GNUNET_MessageHeader *message, - size_t size) +static void +data_sent_cb (void *cls, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) { - struct CadetChannelReliability *rel; - struct GNUNET_CADET_ChannelAppDataMessage *payload; - uint16_t p2p_size = sizeof(struct GNUNET_CADET_ChannelAppDataMessage) + size; - unsigned char cbuf[p2p_size]; - unsigned char buffer; - - /* Is the client in the channel? */ - if ( !( (fwd && - ch->root == c) - || - (!fwd && - ch->dest == c) ) ) + struct CadetReliableMessage *crm = cls; + struct CadetChannel *ch = crm->ch; + + GNUNET_assert (GNUNET_NO == ch->is_loopback); + GNUNET_assert (NULL != crm->qe); + crm->qe = NULL; + GNUNET_CONTAINER_DLL_remove (ch->head_sent, + ch->tail_sent, + crm); + if (GNUNET_NO == ch->reliable) { - GNUNET_break_op (0); - return GNUNET_SYSERR; + GNUNET_free (crm->data_message); + GNUNET_free (crm); + ch->pending_messages--; + send_ack_to_client (ch, + (NULL == ch->owner) + ? GNUNET_NO + : GNUNET_YES); + return; } - - rel = fwd ? ch->root_rel : ch->dest_rel; - - if (GNUNET_NO == rel->client_allowed) + if (NULL == cid) { - GNUNET_break_op (0); - return GNUNET_SYSERR; + /* There was an error sending. */ + crm->num_transmissions = GNUNET_SYSERR; } - - rel->client_allowed = GNUNET_NO; - - /* Ok, everything is correct, send the message. */ - payload = (struct GNUNET_CADET_ChannelAppDataMessage *) cbuf; - payload->mid = htonl (rel->mid_send); - rel->mid_send++; - GNUNET_memcpy (&payload[1], message, size); - payload->header.size = htons (p2p_size); - payload->header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA); - payload->ctn = ch->gid; - LOG (GNUNET_ERROR_TYPE_DEBUG, " sending on channel...\n"); - GCCH_send_prebuilt_message (&payload->header, ch, fwd, NULL); - - if (is_loopback (ch)) - buffer = GCCH_get_buffer (ch, fwd); - else - buffer = GCT_get_connections_buffer (ch->t); - - if (0 < buffer) - GCCH_allow_client (ch, fwd); - - return GNUNET_OK; -} - - -/** - * Handle a channel destroy requested by a client. - * - * TODO: add "reason" field - * - * Destroy the channel and the tunnel in case this was the last channel. - * - * @param ch Channel. - * @param c Client that requested the destruction (to avoid notifying him). - * @param is_root Is the request coming from root? - */ -void -GCCH_handle_local_destroy (struct CadetChannel *ch, - struct CadetClient *c, - int is_root) -{ - ch->destroy = GNUNET_YES; - /* Cleanup after the tunnel */ - if (GNUNET_NO == is_root && c == ch->dest) + else if (GNUNET_SYSERR != crm->num_transmissions) { - LOG (GNUNET_ERROR_TYPE_DEBUG, " Client %s is destination.\n", GML_2s (c)); - GML_client_delete_channel (c, ch, ch->lid_dest); - ch->dest = NULL; + /* Increment transmission counter, and possibly store @a cid + if this was the first transmission. */ + crm->num_transmissions++; + if (1 == crm->num_transmissions) + { + crm->first_transmission_time = GNUNET_TIME_absolute_get (); + crm->connection_taken = *cid; + GCC_ack_expected (cid); + } } - if (GNUNET_YES == is_root && c == ch->root) + if ( (0 == crm->retry_delay.rel_value_us) && + (NULL != cid) ) { - LOG (GNUNET_ERROR_TYPE_DEBUG, " Client %s is owner.\n", GML_2s (c)); - GML_client_delete_channel (c, ch, ch->lid_root); - ch->root = NULL; - } + struct CadetConnection *cc = GCC_lookup (cid); - send_destroy (ch, GNUNET_NO); - if (0 == ch->pending_messages) - GCCH_destroy (ch); + if (NULL != cc) + crm->retry_delay = GCC_get_metrics (cc)->aged_latency; + else + crm->retry_delay = ch->retry_time; + } + crm->retry_delay = GNUNET_TIME_STD_BACKOFF (crm->retry_delay); + crm->retry_delay = GNUNET_TIME_relative_max (crm->retry_delay, + MIN_RTT_DELAY); + crm->next_retry = GNUNET_TIME_relative_to_absolute (crm->retry_delay); + + GNUNET_CONTAINER_DLL_insert_sorted (struct CadetReliableMessage, + cmp_crm_by_next_retry, + NULL, + ch->head_sent, + ch->tail_sent, + crm); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Message %u sent, next transmission on %s in %s\n", + (unsigned int) ntohl (crm->data_message->mid.mid), + GCCH_2s (ch), + GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (ch->head_sent->next_retry), + GNUNET_YES)); + if (NULL == ch->head_sent->qe) + { + if (NULL != ch->retry_data_task) + GNUNET_SCHEDULER_cancel (ch->retry_data_task); + ch->retry_data_task + = GNUNET_SCHEDULER_add_at (ch->head_sent->next_retry, + &retry_transmission, + ch); + } } /** - * Handle a channel create requested by a client. - * - * Create the channel and the tunnel in case this was the first0 channel. + * Handle data given by a client. * - * @param c Client that requested the creation (will be the root). - * @param msg Create Channel message. + * Check whether the client is allowed to send in this tunnel, save if + * channel is reliable and send an ACK to the client if there is still + * buffer space in the tunnel. * - * @return #GNUNET_OK if everything went fine, #GNUNET_SYSERR otherwise. + * @param ch Channel. + * @param sender_ccn ccn of the sender + * @param buf payload to transmit. + * @param buf_len number of bytes in @a buf + * @return #GNUNET_OK if everything goes well, + * #GNUNET_SYSERR in case of an error. */ int -GCCH_handle_local_create (struct CadetClient *c, - struct GNUNET_CADET_LocalChannelCreateMessage *msg) +GCCH_handle_local_data (struct CadetChannel *ch, + struct GNUNET_CADET_ClientChannelNumber sender_ccn, + const char *buf, + size_t buf_len) { - struct CadetChannel *ch; - struct CadetTunnel *t; - struct CadetPeer *peer; - struct GNUNET_CADET_ClientChannelNumber ccn; - - LOG (GNUNET_ERROR_TYPE_DEBUG, " towards %s:%u\n", - GNUNET_i2s (&msg->peer), GNUNET_h2s (&msg->port)); - ccn = msg->ccn; - - /* Sanity check for duplicate channel IDs */ - if (NULL != GML_channel_get (c, ccn)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } + struct CadetReliableMessage *crm; - peer = GCP_get (&msg->peer, GNUNET_YES); - GCP_add_tunnel (peer); - t = GCP_get_tunnel (peer); - - if (GCP_get_short_id (peer) == myid) - { - GCT_change_cstate (t, CADET_TUNNEL_READY); - } - else - { - /* FIXME change to a tunnel API, eliminate ch <-> peer connection */ - GCP_connect (peer); - } - - /* Create channel */ - ch = channel_new (t, c, ccn); - if (NULL == ch) + if (ch->pending_messages > ch->max_pending_messages) { GNUNET_break (0); return GNUNET_SYSERR; } - ch->port = msg->port; - channel_set_options (ch, ntohl (msg->opt)); - - /* In unreliable channels, we'll use the DLL to buffer BCK data */ - ch->root_rel = GNUNET_new (struct CadetChannelReliability); - ch->root_rel->ch = ch; - ch->root_rel->retry_timer = CADET_RETRANSMIT_TIME; - ch->root_rel->expected_delay.rel_value_us = 0; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "CREATED CHANNEL %s\n", GCCH_2s (ch)); - - send_create (ch); - - return GNUNET_OK; -} - - -/** - * Handler for cadet network payload traffic. - * - * @param ch Channel for the message. - * @param msg Unencryted data message. - * @param fwd Is this message fwd? This only is meaningful in loopback channels. - * #GNUNET_YES if message is FWD on the respective channel (loopback) - * #GNUNET_NO if message is BCK on the respective channel (loopback) - * #GNUNET_SYSERR if message on a one-ended channel (remote) - */ -void -GCCH_handle_data (struct CadetChannel *ch, - const struct GNUNET_CADET_ChannelAppDataMessage *msg, - int fwd) -{ - struct CadetChannelReliability *rel; - struct CadetClient *c; - struct GNUNET_MessageHeader *payload_msg; - uint32_t mid; - uint16_t payload_type; - uint16_t payload_size; - - /* If this is a remote (non-loopback) channel, find 'fwd'. */ - if (GNUNET_SYSERR == fwd) + if (GNUNET_YES == ch->destroy) { - if (is_loopback (ch)) - { - /* It is a loopback channel after all... */ - GNUNET_break (0); - return; - } - fwd = (NULL != ch->dest) ? GNUNET_YES : GNUNET_NO; + /* we are going down, drop messages */ + return GNUNET_OK; } + ch->pending_messages++; - /* Initialize FWD/BCK data */ - c = fwd ? ch->dest : ch->root; - rel = fwd ? ch->dest_rel : ch->root_rel; - - if (NULL == c) + if (GNUNET_YES == ch->is_loopback) { - GNUNET_break (GNUNET_NO != ch->destroy); - return; - } + struct CadetChannelClient *receiver; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_CADET_LocalData *ld; + int ack_to_owner; - if (CADET_CHANNEL_READY != ch->state) - { - if (GNUNET_NO == fwd) + env = GNUNET_MQ_msg_extra (ld, + buf_len, + GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA); + if ( (NULL != ch->owner) && + (sender_ccn.channel_of_client == + ch->owner->ccn.channel_of_client) ) { - /* If we are the root, this means the other peer has sent traffic before - * receiving our ACK. Even if the SYNACK goes missing, no traffic should - * be sent before the ACK. - */ - GNUNET_break_op (0); - return; + receiver = ch->dest; + ack_to_owner = GNUNET_YES; } - /* If we are the dest, this means that the SYNACK got to the root but - * the ACK went missing. Treat this as an ACK. - */ - channel_confirm (ch, GNUNET_NO); - } - - payload_msg = (struct GNUNET_MessageHeader *) &msg[1]; - payload_type = ntohs (payload_msg->type); - payload_size = ntohs (payload_msg->size); - - GNUNET_STATISTICS_update (stats, "# messages received", 1, GNUNET_NO); - GNUNET_STATISTICS_update (stats, "# bytes received", payload_size, GNUNET_NO); - - mid = ntohl (msg->mid); - LOG (GNUNET_ERROR_TYPE_INFO, "<== %s (%s %4u) on chan %s (%p) %s [%5u]\n", - GC_m2s (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA), GC_m2s (payload_type), mid, - GCCH_2s (ch), ch, GC_f2s (fwd), ntohs (msg->header.size)); - - if ( (GNUNET_NO == ch->reliable) || - ( (! GC_is_pid_bigger (rel->mid_recv, mid)) && - GC_is_pid_bigger (rel->mid_recv + 64, mid) ) ) - { - if (GNUNET_YES == ch->reliable) + else if ( (NULL != ch->dest) && + (sender_ccn.channel_of_client == + ch->dest->ccn.channel_of_client) ) { - /* Is this the exact next expected messasge? */ - if (mid == rel->mid_recv) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "as expected, sending to client\n"); - send_client_data (ch, msg, fwd); - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "save for later\n"); - add_buffered_data (msg, rel); - } + receiver = ch->owner; + ack_to_owner = GNUNET_NO; } else { - /* Tunnel is unreliable: send to clients directly */ - /* FIXME: accept Out Of Order traffic */ - rel->mid_recv = mid + 1; - send_client_data (ch, msg, fwd); + GNUNET_break (0); + return GNUNET_SYSERR; } - } - else - { - GNUNET_STATISTICS_update (stats, "# duplicate MID", 1, GNUNET_NO); - if (GC_is_pid_bigger (rel->mid_recv, mid)) + GNUNET_assert (NULL != receiver); + ld->ccn = receiver->ccn; + GNUNET_memcpy (&ld[1], + buf, + buf_len); + if (GNUNET_YES == receiver->client_ready) { - GNUNET_break_op (0); - LOG (GNUNET_ERROR_TYPE_WARNING, - "MID %u on channel %s not expected (window: %u - %u). Dropping!\n", - mid, GCCH_2s (ch), rel->mid_recv, rel->mid_recv + 63); + ch->pending_messages--; + GSC_send_to_client (receiver->c, + env); + send_ack_to_client (ch, + ack_to_owner); } else { - LOG (GNUNET_ERROR_TYPE_INFO, - "Duplicate MID %u, channel %s (expecting MID %u). Re-sending ACK!\n", - mid, GCCH_2s (ch), rel->mid_recv); - if (NULL != rel->uniq) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - "We are trying to send an ACK, but don't seem have the " - "bandwidth. Have you set enough [ats] QUOTA in your config?\n"); - } - + struct CadetOutOfOrderMessage *oom; + + oom = GNUNET_new (struct CadetOutOfOrderMessage); + oom->env = env; + GNUNET_CONTAINER_DLL_insert_tail (receiver->head_recv, + receiver->tail_recv, + oom); + receiver->num_recv++; } - } - - GCCH_send_data_ack (ch, fwd); + return GNUNET_OK; + } + + /* Everything is correct, send the message. */ + crm = GNUNET_malloc (sizeof (*crm)); + crm->ch = ch; + crm->data_message = GNUNET_malloc (sizeof (struct GNUNET_CADET_ChannelAppDataMessage) + + buf_len); + crm->data_message->header.size = htons (sizeof (struct GNUNET_CADET_ChannelAppDataMessage) + buf_len); + crm->data_message->header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA); + ch->mid_send.mid = htonl (ntohl (ch->mid_send.mid) + 1); + crm->data_message->mid = ch->mid_send; + crm->data_message->ctn = ch->ctn; + GNUNET_memcpy (&crm->data_message[1], + buf, + buf_len); + GNUNET_CONTAINER_DLL_insert_tail (ch->head_sent, + ch->tail_sent, + crm); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending message %u from local client to %s with %u bytes\n", + ntohl (crm->data_message->mid.mid), + GCCH_2s (ch), + buf_len); + if (NULL != ch->retry_data_task) + { + GNUNET_SCHEDULER_cancel (ch->retry_data_task); + ch->retry_data_task = NULL; + } + crm->qe = GCT_send (ch->t, + &crm->data_message->header, + &data_sent_cb, + crm); + GNUNET_assert (NULL == ch->retry_data_task); + return GNUNET_OK; } /** - * Handler for cadet network traffic end-to-end ACKs. + * Handle ACK from client on local channel. Means the client is ready + * for more data, see if we have any for it. * - * @param ch Channel on which we got this message. - * @param msg Data message. - * @param fwd Is this message fwd? This only is meaningful in loopback channels. - * #GNUNET_YES if message is FWD on the respective channel (loopback) - * #GNUNET_NO if message is BCK on the respective channel (loopback) - * #GNUNET_SYSERR if message on a one-ended channel (remote) + * @param ch channel to destroy + * @param client_ccn ccn of the client sending the ack */ void -GCCH_handle_data_ack (struct CadetChannel *ch, - const struct GNUNET_CADET_ChannelDataAckMessage *msg, - int fwd) +GCCH_handle_local_ack (struct CadetChannel *ch, + struct GNUNET_CADET_ClientChannelNumber client_ccn) { - struct CadetChannelReliability *rel; - struct CadetReliableMessage *copy; - struct CadetReliableMessage *next; - uint32_t ack; - int work; - - /* If this is a remote (non-loopback) channel, find 'fwd'. */ - if (GNUNET_SYSERR == fwd) - { - if (is_loopback (ch)) - { - /* It is a loopback channel after all... */ - GNUNET_break (0); - return; - } - /* Inverted: if message came 'FWD' is a 'BCK ACK'. */ - fwd = (NULL != ch->dest) ? GNUNET_NO : GNUNET_YES; - } - - ack = ntohl (msg->mid); - LOG (GNUNET_ERROR_TYPE_INFO, - "<== %s (0x%010lX %4u) on chan %s (%p) %s [%5u]\n", - GC_m2s (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK), msg->futures, ack, - GCCH_2s (ch), ch, GC_f2s (fwd), ntohs (msg->header.size)); - - if (GNUNET_YES == fwd) - rel = ch->root_rel; + struct CadetChannelClient *ccc; + struct CadetOutOfOrderMessage *com; + + if ( (NULL != ch->owner) && + (ch->owner->ccn.channel_of_client == client_ccn.channel_of_client) ) + ccc = ch->owner; + else if ( (NULL != ch->dest) && + (ch->dest->ccn.channel_of_client == client_ccn.channel_of_client) ) + ccc = ch->dest; else - rel = ch->dest_rel; - - if (NULL == rel) + GNUNET_assert (0); + ccc->client_ready = GNUNET_YES; + com = ccc->head_recv; + if (NULL == com) { - GNUNET_break (GNUNET_NO != ch->destroy); - return; - } - - /* Free ACK'd copies: no need to retransmit those anymore FIXME refactor */ - for (work = GNUNET_NO, copy = rel->head_sent; copy != NULL; copy = next) - { - if (GC_is_pid_bigger (copy->mid, ack)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " head %u, out!\n", copy->mid); - if (0 < channel_rel_free_sent (rel, msg)) - work = GNUNET_YES; - break; - } - work = GNUNET_YES; - LOG (GNUNET_ERROR_TYPE_DEBUG, " id %u\n", copy->mid); - next = copy->next; - if (GNUNET_YES == rel_message_free (copy, GNUNET_YES)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " channel destoyed\n"); - return; - } - } - - /* ACK client if needed and possible */ - GCCH_allow_client (ch, fwd); - - /* If some message was free'd, update the retransmission delay */ - if (GNUNET_YES == work) - { - if (NULL != rel->retry_task) + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got LOCAL_ACK, %s-%X ready to receive more data, but none pending on %s-%X(%p)!\n", + GSC_2s (ccc->c), + ntohl (client_ccn.channel_of_client), + GCCH_2s (ch), + ntohl (ccc->ccn.channel_of_client), + ccc); + return; /* none pending */ + } + if (GNUNET_YES == ch->is_loopback) + { + int to_owner; + + /* Messages are always in-order, just send */ + GNUNET_CONTAINER_DLL_remove (ccc->head_recv, + ccc->tail_recv, + com); + ccc->num_recv--; + GSC_send_to_client (ccc->c, + com->env); + /* Notify sender that we can receive more */ + if ( (NULL != ch->owner) && + (ccc->ccn.channel_of_client == + ch->owner->ccn.channel_of_client) ) { - GNUNET_SCHEDULER_cancel (rel->retry_task); - rel->retry_task = NULL; - if (NULL != rel->head_sent && NULL == rel->head_sent->chq) - { - struct GNUNET_TIME_Absolute new_target; - struct GNUNET_TIME_Relative delay; - - delay = GNUNET_TIME_relative_saturating_multiply (rel->retry_timer, - CADET_RETRANSMIT_MARGIN); - new_target = GNUNET_TIME_absolute_add (rel->head_sent->timestamp, - delay); - delay = GNUNET_TIME_absolute_get_remaining (new_target); - rel->retry_task = - GNUNET_SCHEDULER_add_delayed (delay, - &channel_retransmit_message, - rel); - } + to_owner = GNUNET_NO; } else { - /* Work was done but no task was pending. - * Task was cancelled by a retransmission that is sitting in the queue. - */ - // FIXME add test to make sure this is the case, probably add return - // value to GCCH_send_prebuilt_message + GNUNET_assert ( (NULL != ch->dest) && + (ccc->ccn.channel_of_client == + ch->dest->ccn.channel_of_client) ); + to_owner = GNUNET_YES; } + send_ack_to_client (ch, + to_owner); + GNUNET_free (com); + return; } -} - -/** - * Handler for channel create messages. - * - * Does not have fwd parameter because it's always 'FWD': channel is incoming. - * - * @param t Tunnel this channel will be in. - * @param msg Channel crate message. - */ -struct CadetChannel * -GCCH_handle_create (struct CadetTunnel *t, - const struct GNUNET_CADET_ChannelOpenMessage *msg) -{ - struct GNUNET_CADET_ClientChannelNumber ccn; - struct GNUNET_CADET_ChannelTunnelNumber gid; - struct CadetChannel *ch; - struct CadetClient *c; - int new_channel; - const struct GNUNET_HashCode *port; - - gid = msg->ctn; - ch = GCT_get_channel (t, gid); - if (NULL == ch) - { - /* Create channel */ - ccn.channel_of_client = htonl (0); - ch = channel_new (t, NULL, ccn); - ch->gid = gid; - channel_set_options (ch, ntohl (msg->opt)); - new_channel = GNUNET_YES; - } - else + if ( (com->mid.mid != ch->mid_recv.mid) && + (GNUNET_NO == ch->out_of_order) && + (GNUNET_YES == ch->reliable) ) { - new_channel = GNUNET_NO; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got LOCAL_ACK, %s-%X ready to receive more data (but next one is out-of-order %u vs. %u)!\n", + GSC_2s (ccc->c), + ntohl (ccc->ccn.channel_of_client), + ntohl (com->mid.mid), + ntohl (ch->mid_recv.mid)); + return; /* missing next one in-order */ } - port = &msg->port; - - LOG (GNUNET_ERROR_TYPE_INFO, - "<== %s ( 0x%08X %4u) on chan %s (%p) %s [%5u]\n", - GC_m2s (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN), ccn, port, - GCCH_2s (ch), ch, GC_f2s (GNUNET_YES), ntohs (msg->header.size)); - - if (GNUNET_YES == new_channel || GCT_is_loopback (t)) - { - /* Find a destination client */ - ch->port = *port; - LOG (GNUNET_ERROR_TYPE_DEBUG, " port %s\n", GNUNET_h2s (port)); - c = GML_client_get_by_port (port); - if (NULL == c) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " no client has port registered\n"); - if (is_loopback (ch)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " loopback: destroy on handler\n"); - send_nack (ch); - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " not loopback: destroy now\n"); - send_nack (ch); - GCCH_destroy (ch); - } - return NULL; - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " client %p has port registered\n", c); - } - - add_destination (ch, c); - if (GNUNET_YES == ch->reliable) - LOG (GNUNET_ERROR_TYPE_DEBUG, "Reliable\n"); - else - LOG (GNUNET_ERROR_TYPE_DEBUG, "Not Reliable\n"); - send_client_create (ch); - ch->state = CADET_CHANNEL_SENT; - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " duplicate create channel\n"); - if (NULL != ch->dest_rel->retry_task) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " clearing retry task\n"); - /* we were waiting to re-send our 'SYNACK', wait no more! */ - GNUNET_SCHEDULER_cancel (ch->dest_rel->retry_task); - ch->dest_rel->retry_task = NULL; - } - else if (NULL != ch->dest_rel->uniq) - { - /* we are waiting to for our 'SYNACK' to leave the queue, all done! */ - return ch; - } - } - send_ack (ch, GNUNET_YES); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got LOCAL_ACK, giving payload message %u to %s-%X on %s\n", + ntohl (com->mid.mid), + GSC_2s (ccc->c), + ntohl (ccc->ccn.channel_of_client), + GCCH_2s (ch)); - return ch; + /* all good, pass next message to client */ + GNUNET_CONTAINER_DLL_remove (ccc->head_recv, + ccc->tail_recv, + com); + ccc->num_recv--; + /* FIXME: if unreliable, this is not aggressive + enough, as it would be OK to have lost some! */ + + ch->mid_recv.mid = htonl (1 + ntohl (com->mid.mid)); + ch->mid_futures >>= 1; /* equivalent to division by 2 */ + ccc->client_ready = GNUNET_NO; + GSC_send_to_client (ccc->c, + com->env); + GNUNET_free (com); + send_channel_data_ack (ch); + if (NULL != ccc->head_recv) + return; + if (GNUNET_NO == ch->destroy) + return; + GCT_send_channel_destroy (ch->t, + ch->ctn); + channel_destroy (ch); } -/** - * Handler for channel NACK messages. - * - * NACK messages always go dest -> root, no need for 'fwd' or 'msg' parameter. - * - * @param ch Channel. - */ -void -GCCH_handle_nack (struct CadetChannel *ch) -{ - LOG (GNUNET_ERROR_TYPE_INFO, - "<== %s ( 0x%08X %4u) on chan %s (%p) %s [%5u]\n", - GC_m2s (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_NACK_DEPRECATED), ch->gid, 0, - GCCH_2s (ch), ch, "---", 0); - - send_client_nack (ch); - GCCH_destroy (ch); -} +#define LOG2(level, ...) GNUNET_log_from_nocheck(level,"cadet-chn",__VA_ARGS__) /** - * Handler for channel ack messages. + * Log channel info. * * @param ch Channel. - * @param msg Message. - * @param fwd Is this message fwd? This only is meaningful in loopback channels. - * #GNUNET_YES if message is FWD on the respective channel (loopback) - * #GNUNET_NO if message is BCK on the respective channel (loopback) - * #GNUNET_SYSERR if message on a one-ended channel (remote) - */ -void -GCCH_handle_ack (struct CadetChannel *ch, - const struct GNUNET_CADET_ChannelManageMessage *msg, - int fwd) -{ - LOG (GNUNET_ERROR_TYPE_INFO, - "<== %s ( 0x%08X %4u) on chan %s (%p) %s [%5u]\n", - GC_m2s (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK), ch->gid, 0, - GCCH_2s (ch), ch, GC_f2s (fwd), ntohs (msg->header.size)); - - /* If this is a remote (non-loopback) channel, find 'fwd'. */ - if (GNUNET_SYSERR == fwd) - { - if (is_loopback (ch)) - { - /* It is a loopback channel after all... */ - GNUNET_break (0); - return; - } - fwd = (NULL != ch->dest) ? GNUNET_YES : GNUNET_NO; - } - - channel_confirm (ch, !fwd); -} - - -/** - * Handler for channel destroy messages. - * - * @param ch Channel to be destroyed of. - * @param msg Message. - * @param fwd Is this message fwd? This only is meaningful in loopback channels. - * #GNUNET_YES if message is FWD on the respective channel (loopback) - * #GNUNET_NO if message is BCK on the respective channel (loopback) - * #GNUNET_SYSERR if message on a one-ended channel (remote) + * @param level Debug level to use. */ void -GCCH_handle_destroy (struct CadetChannel *ch, - const struct GNUNET_CADET_ChannelManageMessage *msg, - int fwd) +GCCH_debug (struct CadetChannel *ch, + enum GNUNET_ErrorType level) { - struct CadetChannelReliability *rel; - - LOG (GNUNET_ERROR_TYPE_INFO, - "<== %s ( 0x%08X %4u) on chan %s (%p) %s [%5u]\n", - GC_m2s (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY), ch->gid, 0, - GCCH_2s (ch), ch, GC_f2s (fwd), ntohs (msg->header.size)); - - /* If this is a remote (non-loopback) channel, find 'fwd'. */ - if (GNUNET_SYSERR == fwd) - { - if (is_loopback (ch)) - { - /* It is a loopback channel after all... */ - GNUNET_break (0); - return; - } - fwd = (NULL != ch->dest) ? GNUNET_YES : GNUNET_NO; - } + int do_log; - GCCH_debug (ch, GNUNET_ERROR_TYPE_DEBUG); - if ( (fwd && NULL == ch->dest) || (!fwd && NULL == ch->root) ) - { - /* Not for us (don't destroy twice a half-open loopback channel) */ + do_log = GNUNET_get_log_call_status (level & (~GNUNET_ERROR_TYPE_BULK), + "cadet-chn", + __FILE__, __FUNCTION__, __LINE__); + if (0 == do_log) return; - } - - rel = fwd ? ch->dest_rel : ch->root_rel; - if (0 == rel->n_recv) - { - send_destroy (ch, GNUNET_YES); - GCCH_destroy (ch); - } - else - { - ch->destroy = GNUNET_YES; - } -} - - -/** - * Sends an already built message on a channel. - * - * If the channel is on a loopback tunnel, notifies the appropriate destination - * client locally. - * - * On a normal channel passes the message to the tunnel for encryption and - * sending on a connection. - * - * This function DOES NOT save the message for retransmission. - * - * @param message Message to send. Function makes a copy of it. - * @param ch Channel on which this message is transmitted. - * @param fwd Is this a fwd message? - * @param existing_copy This is a retransmission, don't save a copy. - */ -void -GCCH_send_prebuilt_message (const struct GNUNET_MessageHeader *message, - struct CadetChannel *ch, int fwd, - void *existing_copy) -{ - struct CadetChannelQueue *chq; - uint32_t data_id; - uint16_t type; - uint16_t size; - char info[32]; - - type = ntohs (message->type); - size = ntohs (message->size); - - data_id = 0; - switch (type) - { - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA: - { - struct GNUNET_CADET_ChannelAppDataMessage *data_msg; - struct GNUNET_MessageHeader *payload_msg; - uint16_t payload_type; - - data_msg = (struct GNUNET_CADET_ChannelAppDataMessage *) message; - data_id = ntohl (data_msg->mid); - payload_msg = (struct GNUNET_MessageHeader *) &data_msg[1]; - payload_type = ntohs (payload_msg->type); - strncpy (info, GC_m2s (payload_type), 31); - info[31] = '\0'; - break; - } - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK: - { - struct GNUNET_CADET_ChannelDataAckMessage *ack_msg; - ack_msg = (struct GNUNET_CADET_ChannelDataAckMessage *) message; - data_id = ntohl (ack_msg->mid); - SPRINTF (info, "0x%010lX", - (unsigned long int) ack_msg->futures); - break; - } - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN: - { - struct GNUNET_CADET_ChannelOpenMessage *cc_msg; - cc_msg = (struct GNUNET_CADET_ChannelOpenMessage *) message; - SPRINTF (info, " 0x%08X", ntohl (cc_msg->ctn.cn)); - break; - } - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK: - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_NACK_DEPRECATED: - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY: - { - struct GNUNET_CADET_ChannelManageMessage *m_msg; - m_msg = (struct GNUNET_CADET_ChannelManageMessage *) message; - SPRINTF (info, " 0x%08X", ntohl (m_msg->ctn.cn)); - break; - } - default: - info[0] = '\0'; - } - LOG (GNUNET_ERROR_TYPE_INFO, - "==> %s (%12s %4u) on chan %s (%p) %s [%5u]\n", - GC_m2s (type), info, data_id, - GCCH_2s (ch), ch, GC_f2s (fwd), size); - if (GCT_is_loopback (ch->t)) + if (NULL == ch) { - handle_loopback (ch, message, fwd); + LOG2 (level, "CHN *** DEBUG NULL CHANNEL ***\n"); return; } - - switch (type) + LOG2 (level, + "CHN %s:%X (%p)\n", + GCT_2s (ch->t), + ch->ctn, + ch); + if (NULL != ch->owner) { - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA: - if (GNUNET_YES == ch->reliable) - { - chq = GNUNET_new (struct CadetChannelQueue); - chq->type = type; - if (NULL == existing_copy) - chq->copy = channel_save_copy (ch, message, fwd); - else - { - chq->copy = (struct CadetReliableMessage *) existing_copy; - if (NULL != chq->copy->chq) - { - /* Last retransmission was queued but not yet sent! - * This retransmission was scheduled by a ch_message_sent which - * followed a very fast RTT, so the tiny delay made the - * retransmission function to execute before the previous - * retransmitted message even had a chance to leave the peer. - * Cancel this message and wait until the pending - * retransmission leaves the peer and ch_message_sent starts - * the timer for the next one. - */ - GNUNET_free (chq); - LOG (GNUNET_ERROR_TYPE_DEBUG, - " exisitng copy not yet transmitted!\n"); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - " using existing copy: %p {r:%p q:%p t:%u}\n", - existing_copy, - chq->copy->rel, chq->copy->chq, chq->copy->type); - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " new chq: %p\n", chq); - chq->copy->chq = chq; - chq->tq = GCT_send_prebuilt_message (message, ch->t, NULL, - GNUNET_YES, - &ch_message_sent, chq); - /* q itself is stored in copy */ - GNUNET_assert (NULL != chq->tq || GNUNET_NO != ch->destroy); - } - else - { - fire_and_forget (message, ch, GNUNET_NO); - } - break; - - - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK: - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN: - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK: - chq = GNUNET_new (struct CadetChannelQueue); - chq->type = type; - chq->rel = fwd ? ch->root_rel : ch->dest_rel; - if (NULL != chq->rel->uniq) - { - if (NULL != chq->rel->uniq->tq) - { - GCT_cancel (chq->rel->uniq->tq); - /* ch_message_sent is called, freeing and NULLing uniq */ - GNUNET_break (NULL == chq->rel->uniq); - } - else - { - GNUNET_break (0); - GNUNET_free (chq->rel->uniq); - } - } - - chq->rel->uniq = chq; - chq->tq = GCT_send_prebuilt_message (message, ch->t, NULL, GNUNET_YES, - &ch_message_sent, chq); - if (NULL == chq->tq) - { - GNUNET_break (0); - chq->rel->uniq = NULL; - GCT_debug (ch->t, GNUNET_ERROR_TYPE_ERROR); - GNUNET_free (chq); - chq = NULL; - return; - } - break; - - - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY: - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_NACK_DEPRECATED: - fire_and_forget (message, ch, GNUNET_YES); - break; - - - default: - GNUNET_break (0); - LOG (GNUNET_ERROR_TYPE_WARNING, "type %s unknown!\n", GC_m2s (type)); - fire_and_forget (message, ch, GNUNET_YES); + LOG2 (level, + "CHN origin %s ready %s local-id: %u\n", + GSC_2s (ch->owner->c), + ch->owner->client_ready ? "YES" : "NO", + ntohl (ch->owner->ccn.channel_of_client)); } + if (NULL != ch->dest) + { + LOG2 (level, + "CHN destination %s ready %s local-id: %u\n", + GSC_2s (ch->dest->c), + ch->dest->client_ready ? "YES" : "NO", + ntohl (ch->dest->ccn.channel_of_client)); + } + LOG2 (level, + "CHN Message IDs recv: %d (%LLX), send: %d\n", + ntohl (ch->mid_recv.mid), + (unsigned long long) ch->mid_futures, + ntohl (ch->mid_send.mid)); } -/** - * Get the static string for identification of the channel. - * - * @param ch Channel. - * - * @return Static string with the channel IDs. - */ -const char * -GCCH_2s (const struct CadetChannel *ch) -{ - static char buf[64]; - - if (NULL == ch) - return "(NULL Channel)"; - SPRINTF (buf, - "%s:%s gid:%X (%X / %X)", - GCT_2s (ch->t), - GNUNET_h2s (&ch->port), - ntohl (ch->gid.cn), - ntohl (ch->lid_root.channel_of_client), - ntohl (ch->lid_dest.channel_of_client)); - - return buf; -} +/* end of gnunet-service-cadet-new_channel.c */ diff --git a/src/cadet/gnunet-service-cadet_channel.h b/src/cadet/gnunet-service-cadet_channel.h index 9d48932697..a3ef9a06d7 100644 --- a/src/cadet/gnunet-service-cadet_channel.h +++ b/src/cadet/gnunet-service-cadet_channel.h @@ -1,6 +1,7 @@ + /* This file is part of GNUnet. - Copyright (C) 2013 GNUnet e.V. + Copyright (C) 2001-2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -20,344 +21,242 @@ /** * @file cadet/gnunet-service-cadet_channel.h - * @brief cadet service; dealing with end-to-end channels + * @brief GNUnet CADET service with encryption * @author Bartlomiej Polot - * - * All functions in this file should use the prefix GMCH (Gnunet Cadet CHannel) + * @author Christian Grothoff */ - #ifndef GNUNET_SERVICE_CADET_CHANNEL_H #define GNUNET_SERVICE_CADET_CHANNEL_H -#ifdef __cplusplus -extern "C" -{ -#if 0 /* keep Emacsens' auto-indent happy */ -} -#endif -#endif - -#include "platform.h" -#include "gnunet_util_lib.h" - +#include "gnunet-service-cadet.h" +#include "gnunet-service-cadet_peer.h" #include "cadet_protocol.h" -#include "cadet.h" - -/** - * Struct containing all information regarding a channel to a remote client. - */ -struct CadetChannel; - - -#include "gnunet-service-cadet_tunnel.h" -#include "gnunet-service-cadet_local.h" - - -/** - * Destroy a channel and free all resources. - * - * @param ch Channel to destroy. - */ -void -GCCH_destroy (struct CadetChannel *ch); /** - * Get the channel's public ID. - * - * @param ch Channel. - * - * @return ID used to identify the channel with the remote peer. + * A channel is a bidirectional connection between two CADET + * clients. Communiation can be reliable, unreliable, in-order + * or out-of-order. One client is the "local" client, this + * one initiated the connection. The other client is the + * "incoming" client, this one listened on a port to accept + * the connection from the "local" client. */ -struct GNUNET_CADET_ChannelTunnelNumber -GCCH_get_id (const struct CadetChannel *ch); - - -/** - * Get the channel tunnel. - * - * @param ch Channel to get the tunnel from. - * - * @return tunnel of the channel. - */ -struct CadetTunnel * -GCCH_get_tunnel (const struct CadetChannel *ch); +struct CadetChannel; /** - * Get free buffer space towards the client on a specific channel. + * Get the static string for identification of the channel. * * @param ch Channel. - * @param fwd Is query about FWD traffic? * - * @return Free buffer space [0 - 64] + * @return Static string with the channel IDs. */ -unsigned int -GCCH_get_buffer (struct CadetChannel *ch, int fwd); +const char * +GCCH_2s (const struct CadetChannel *ch); /** - * Get flow control status of end point: is client allow to send? + * Log channel info. * * @param ch Channel. - * @param fwd Is query about FWD traffic? (Request root status). - * - * @return #GNUNET_YES if client is allowed to send us data. + * @param level Debug level to use. */ -int -GCCH_get_allowed (struct CadetChannel *ch, int fwd); +void +GCCH_debug (struct CadetChannel *ch, + enum GNUNET_ErrorType level); /** - * Is the root client for this channel on this peer? + * Get the channel's public ID. * * @param ch Channel. - * @param fwd Is this for fwd traffic? * - * @return #GNUNET_YES in case it is. + * @return ID used to identify the channel with the remote peer. */ -int -GCCH_is_origin (struct CadetChannel *ch, int fwd); +struct GNUNET_CADET_ChannelTunnelNumber +GCCH_get_id (const struct CadetChannel *ch); -/** - * Is the destination client for this channel on this peer? - * - * @param ch Channel. - * @param fwd Is this for fwd traffic? - * - * @return #GNUNET_YES in case it is. - */ -int -GCCH_is_terminal (struct CadetChannel *ch, int fwd); /** - * Send an end-to-end ACK message for the most recent in-sequence payload. - * - * If channel is not reliable, do nothing. - * - * @param ch Channel this is about. - * @param fwd Is for FWD traffic? (ACK dest->owner) + * Create a new channel. + * + * @param owner local client owning the channel + * @param owner_id local chid of this channel at the @a owner + * @param destination peer to which we should build the channel + * @param port desired port at @a destination + * @param options options for the channel + * @return handle to the new channel */ -void -GCCH_send_data_ack (struct CadetChannel *ch, int fwd); +struct CadetChannel * +GCCH_channel_local_new (struct CadetClient *owner, + struct GNUNET_CADET_ClientChannelNumber owner_id, + struct CadetPeer *destination, + const struct GNUNET_HashCode *port, + uint32_t options); -/** - * Notify the destination client that a new incoming channel was created. - * - * @param ch Channel that was created. - */ -void -GCCH_send_create (struct CadetChannel *ch); /** - * Allow a client to send us more data, in case it was choked. + * A client is bound to the port that we have a channel + * open to. Send the acknowledgement for the connection + * request and establish the link with the client. * - * @param ch Channel. - * @param fwd Is this about FWD traffic? (Root client). + * @param ch open incoming channel + * @param c client listening on the respective port */ void -GCCH_allow_client (struct CadetChannel *ch, int fwd); +GCCH_bind (struct CadetChannel *ch, + struct CadetClient *c); -/** - * Log channel info. - * - * @param ch Channel. - * @param level Debug level to use. - */ -void -GCCH_debug (struct CadetChannel *ch, enum GNUNET_ErrorType level); /** - * Handle an ACK given by a client. + * Destroy locally created channel. Called by the + * local client, so no need to tell the client. * - * Mark client as ready and send him any buffered data we could have for him. - * - * @param ch Channel. - * @param fwd Is this a "FWD ACK"? (FWD ACKs are sent by root and go BCK) + * @param ch channel to destroy + * @param c client that caused the destruction + * @param ccn client number of the client @a c */ void -GCCH_handle_local_ack (struct CadetChannel *ch, int fwd); +GCCH_channel_local_destroy (struct CadetChannel *ch, + struct CadetClient *c, + struct GNUNET_CADET_ClientChannelNumber ccn); -/** - * Handle data given by a client. - * - * Check whether the client is allowed to send in this tunnel, save if channel - * is reliable and send an ACK to the client if there is still buffer space - * in the tunnel. - * - * @param ch Channel. - * @param c Client which sent the data. - * @param fwd Is this a FWD data? - * @param message Data message. - * @param size Size of data. - * - * @return GNUNET_OK if everything goes well, GNUNET_SYSERR in case of en error. - */ -int -GCCH_handle_local_data (struct CadetChannel *ch, - struct CadetClient *c, int fwd, - const struct GNUNET_MessageHeader *message, - size_t size); /** - * Handle a channel destroy requested by a client. - * - * Destroy the channel and the tunnel in case this was the last channel. - * - * @param ch Channel. - * @param c Client that requested the destruction (to avoid notifying him). - * @param is_root Is the request coming from root? + * Function called once and only once after a channel was bound + * to its tunnel via #GCT_add_channel() is ready for transmission. + * Note that this is only the case for channels that this peer + * initiates, as for incoming channels we assume that they are + * ready for transmission immediately upon receiving the open + * message. Used to bootstrap the #GCT_send() process. + * + * @param ch the channel for which the tunnel is now ready */ void -GCCH_handle_local_destroy (struct CadetChannel *ch, - struct CadetClient *c, - int is_root); +GCCH_tunnel_up (struct CadetChannel *ch); /** - * Handle a channel create requested by a client. - * - * Create the channel and the tunnel in case this was the first0 channel. - * - * @param c Client that requested the creation (will be the root). - * @param msg Create Channel message. - * - * @return #GNUNET_OK if everything went fine, #GNUNET_SYSERR otherwise. + * Create a new channel based on a request coming in over the network. + * + * @param t tunnel to the remote peer + * @param chid identifier of this channel in the tunnel + * @param origin peer to who initiated the channel + * @param port desired local port + * @param options options for the channel + * @return handle to the new channel */ -int -GCCH_handle_local_create (struct CadetClient *c, - struct GNUNET_CADET_LocalChannelCreateMessage *msg); - -/** - * Handler for cadet network payload traffic. - * - * @param ch Channel for the message. - * @param msg Unencryted data message. - * @param fwd Is this message fwd? This only is meaningful in loopback channels. - * #GNUNET_YES if message is FWD on the respective channel (loopback) - * #GNUNET_NO if message is BCK on the respective channel (loopback) - * #GNUNET_SYSERR if message on a one-ended channel (remote) - */ -void -GCCH_handle_data (struct CadetChannel *ch, - const struct GNUNET_CADET_ChannelAppDataMessage *msg, - int fwd); +struct CadetChannel * +GCCH_channel_incoming_new (struct CadetTunnel *t, + struct GNUNET_CADET_ChannelTunnelNumber chid, + const struct GNUNET_HashCode *port, + uint32_t options); /** - * Handler for cadet network traffic end-to-end ACKs. + * We got a #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN message again for + * this channel. If the binding was successful, (re)transmit the + * #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK. * - * @param ch Channel on which we got this message. - * @param msg Data message. - * @param fwd Is this message fwd? This only is meaningful in loopback channels. - * #GNUNET_YES if message is FWD on the respective channel (loopback) - * #GNUNET_NO if message is BCK on the respective channel (loopback) - * #GNUNET_SYSERR if message on a one-ended channel (remote) + * @param ch channel that got the duplicate open + * @param cti identifier of the connection that delivered the message */ void -GCCH_handle_data_ack (struct CadetChannel *ch, - const struct GNUNET_CADET_ChannelDataAckMessage *msg, - int fwd); +GCCH_handle_duplicate_open (struct CadetChannel *ch, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti); -/** - * Handler for channel create messages. - * - * Does not have fwd parameter because it's always 'FWD': channel is incoming. - * - * @param t Tunnel this channel will be in. - * @param msg Channel crate message. - */ -struct CadetChannel * -GCCH_handle_create (struct CadetTunnel *t, - const struct GNUNET_CADET_ChannelOpenMessage *msg); - /** - * Handler for channel NACK messages. + * We got payload data for a channel. Pass it on to the client. * - * NACK messages always go dest -> root, no need for 'fwd' or 'msg' parameter. - * - * @param ch Channel. + * @param ch channel that got data + * @param cti identifier of the connection that delivered the message + * @param msg message that was received */ void -GCCH_handle_nack (struct CadetChannel *ch); +GCCH_handle_channel_plaintext_data (struct CadetChannel *ch, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti, + const struct GNUNET_CADET_ChannelAppDataMessage *msg); /** - * Handler for channel ack messages. + * We got an acknowledgement for payload data for a channel. + * Possibly resume transmissions. * - * @param ch Channel this channel is to be created in. - * @param msg Message. - * @param fwd Is this message fwd? This only is meaningful in loopback channels. - * #GNUNET_YES if message is FWD on the respective channel (loopback) - * #GNUNET_NO if message is BCK on the respective channel (loopback) - * #GNUNET_SYSERR if message on a one-ended channel (remote) + * @param ch channel that got the ack + * @param cti identifier of the connection that delivered the message + * @param ack details about what was received */ void -GCCH_handle_ack (struct CadetChannel *ch, - const struct GNUNET_CADET_ChannelManageMessage *msg, - int fwd); +GCCH_handle_channel_plaintext_data_ack (struct CadetChannel *ch, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti, + const struct GNUNET_CADET_ChannelDataAckMessage *ack); /** - * Handler for channel destroy messages. + * We got an acknowledgement for the creation of the channel + * (the port is open on the other side). Begin transmissions. * - * @param ch Channel this channel is to be destroyed of. - * @param msg Message. - * @param fwd Is this message fwd? This only is meaningful in loopback channels. - * #GNUNET_YES if message is FWD on the respective channel (loopback) - * #GNUNET_NO if message is BCK on the respective channel (loopback) - * #GNUNET_SYSERR if message on a one-ended channel (remote) + * @param ch channel to destroy + * @param cti identifier of the connection that delivered the message, + * NULL if the ACK was inferred because we got payload or are on loopback */ void -GCCH_handle_destroy (struct CadetChannel *ch, - const struct GNUNET_CADET_ChannelManageMessage *msg, - int fwd); +GCCH_handle_channel_open_ack (struct CadetChannel *ch, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti); /** - * Sends an already built message on a channel. + * Destroy channel, based on the other peer closing the + * connection. Also needs to remove this channel from + * the tunnel. * - * If the channel is on a loopback tunnel, notifies the appropriate destination - * client locally. + * FIXME: need to make it possible to defer destruction until we have + * received all messages up to the destroy, and right now the destroy + * message (and this API) fails to give is the information we need! * - * On a normal channel passes the message to the tunnel for encryption and - * sending on a connection. + * FIXME: also need to know if the other peer got a destroy from + * us before! * - * This function DOES NOT save the message for retransmission. - * - * @param message Message to send. Function makes a copy of it. - * @param ch Channel on which this message is transmitted. - * @param fwd Is this a fwd message? - * @param existing_copy This is a retransmission, don't save a copy. + * @param ch channel to destroy + * @param cti identifier of the connection that delivered the message, + * NULL during shutdown */ void -GCCH_send_prebuilt_message (const struct GNUNET_MessageHeader *message, - struct CadetChannel *ch, int fwd, - void *existing_copy); +GCCH_handle_remote_destroy (struct CadetChannel *ch, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti); /** - * Get the static string for identification of the channel. + * Handle data given by a client. * - * @param ch Channel.i + * Check whether the client is allowed to send in this tunnel, save if + * channel is reliable and send an ACK to the client if there is still + * buffer space in the tunnel. * - * @return Static string with the channel IDs. + * @param ch Channel. + * @param sender_ccn ccn of the sender + * @param buf payload to transmit. + * @param buf_len number of bytes in @a buf + * @return #GNUNET_OK if everything goes well, + * #GNUNET_SYSERR in case of an error. */ -const char * -GCCH_2s (const struct CadetChannel *ch); - - +int +GCCH_handle_local_data (struct CadetChannel *ch, + struct GNUNET_CADET_ClientChannelNumber sender_ccn, + const char *buf, + size_t buf_len); -#if 0 /* keep Emacsens' auto-indent happy */ -{ -#endif -#ifdef __cplusplus -} -#endif +/** + * Handle ACK from client on local channel. + * + * @param ch channel to destroy + * @param client_ccn ccn of the client sending the ack + */ +void +GCCH_handle_local_ack (struct CadetChannel *ch, + struct GNUNET_CADET_ClientChannelNumber client_ccn); -/* ifndef GNUNET_SERVICE_CADET_CHANNEL_H */ #endif -/* end of gnunet-service-cadet_channel.h */ diff --git a/src/cadet/gnunet-service-cadet_connection.c b/src/cadet/gnunet-service-cadet_connection.c index 2d5087f817..7b66f61a26 100644 --- a/src/cadet/gnunet-service-cadet_connection.c +++ b/src/cadet/gnunet-service-cadet_connection.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2001-2015 GNUnet e.V. + Copyright (C) 2001-2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -17,242 +17,126 @@ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + /** * @file cadet/gnunet-service-cadet_connection.c - * @brief GNUnet CADET service connection handling + * @brief management of CORE-level end-to-end connections; establishes + * end-to-end routes and transmits messages along the route * @author Bartlomiej Polot + * @author Christian Grothoff */ #include "platform.h" -#include "gnunet_util_lib.h" +#include "gnunet-service-cadet_connection.h" +#include "gnunet-service-cadet_channel.h" +#include "gnunet-service-cadet_paths.h" +#include "gnunet-service-cadet_tunnels.h" +#include "gnunet_cadet_service.h" #include "gnunet_statistics_service.h" -#include "cadet_path.h" #include "cadet_protocol.h" -#include "cadet.h" -#include "gnunet-service-cadet_connection.h" -#include "gnunet-service-cadet_peer.h" -#include "gnunet-service-cadet_tunnel.h" - - -/** - * Should we run somewhat expensive checks on our invariants? - */ -#define CHECK_INVARIANTS 0 - - -#define LOG(level, ...) GNUNET_log_from (level,"cadet-con",__VA_ARGS__) -#define LOG2(level, ...) GNUNET_log_from_nocheck(level,"cadet-con",__VA_ARGS__) - - -#define CADET_MAX_POLL_TIME GNUNET_TIME_relative_multiply (\ - GNUNET_TIME_UNIT_MINUTES,\ - 10) -#define AVG_MSGS 32 - - -/******************************************************************************/ -/******************************** STRUCTS **********************************/ -/******************************************************************************/ - -/** - * Handle for messages queued but not yet sent. - */ -struct CadetConnectionQueue -{ - struct CadetConnectionQueue *next; - struct CadetConnectionQueue *prev; - /** - * Peer queue handle, to cancel if necessary. - */ - struct CadetPeerQueue *peer_q; - - /** - * Continuation to call once sent. - */ - GCC_sent cont; - - /** - * Closure for @e cont. - */ - void *cont_cls; - - /** - * Was this a forced message? (Do not account for it) - */ - int forced; -}; +#define LOG(level, ...) GNUNET_log_from(level,"cadet-con",__VA_ARGS__) /** - * Struct to encapsulate all the Flow Control information to a peer to which - * we are directly connected (on a core level). + * All the states a connection can be in. */ -struct CadetFlowControl +enum CadetConnectionState { /** - * Connection this controls. - */ - struct CadetConnection *c; - - struct CadetConnectionQueue *q_head; - struct CadetConnectionQueue *q_tail; - - /** - * How many messages are in the queue on this connection. - */ - unsigned int queue_n; - - /** - * How many messages do we accept in the queue. - * If 0, the connection is broken in this direction (next hop disconnected). - */ - unsigned int queue_max; - - /** - * ID of the next packet to send. - */ - struct CadetEncryptedMessageIdentifier next_pid; - - /** - * ID of the last packet sent towards the peer. - */ - struct CadetEncryptedMessageIdentifier last_pid_sent; - - /** - * ID of the last packet received from the peer. - */ - struct CadetEncryptedMessageIdentifier last_pid_recv; - - /** - * Bitmap of past 32 messages received: - * - LSB being @c last_pid_recv. - * - MSB being @c last_pid_recv - 31 (mod UINTMAX). - */ - uint32_t recv_bitmap; - - /** - * Last ACK sent to the peer (peer is not allowed to send - * messages with PIDs higher than this value). + * Uninitialized status, we have not yet even gotten the message queue. */ - struct CadetEncryptedMessageIdentifier last_ack_sent; + CADET_CONNECTION_NEW, /** - * Last ACK sent towards the origin (for traffic towards leaf node). + * Connection create message in queue, awaiting transmission by CORE. */ - struct CadetEncryptedMessageIdentifier last_ack_recv; + CADET_CONNECTION_SENDING_CREATE, /** - * Task to poll the peer in case of a lost ACK causes stall. + * Connection create message sent, waiting for ACK. */ - struct GNUNET_SCHEDULER_Task *poll_task; + CADET_CONNECTION_SENT, /** - * How frequently to poll for ACKs. + * We are an inbound connection, and received a CREATE. Need to + * send an CREATE_ACK back. */ - struct GNUNET_TIME_Relative poll_time; + CADET_CONNECTION_CREATE_RECEIVED, /** - * Queued poll message, to cancel if not necessary anymore (got ACK). + * Connection confirmed, ready to carry traffic. */ - struct CadetConnectionQueue *poll_msg; + CADET_CONNECTION_READY - /** - * Queued poll message, to cancel if not necessary anymore (got ACK). - */ - struct CadetConnectionQueue *ack_msg; }; + /** - * Keep a record of the last messages sent on this connection. + * Low-level connection to a destination. */ -struct CadetConnectionPerformance +struct CadetConnection { - /** - * Circular buffer for storing measurements. - */ - double usecsperbyte[AVG_MSGS]; /** - * Running average of @c usecsperbyte. - */ - double avg; - - /** - * How many values of @c usecsperbyte are valid. - */ - uint16_t size; - - /** - * Index of the next "free" position in @c usecsperbyte. + * ID of the connection. */ - uint16_t idx; -}; - + struct GNUNET_CADET_ConnectionTunnelIdentifier cid; -/** - * Struct containing all information regarding a connection to a peer. - */ -struct CadetConnection -{ /** - * Tunnel this connection is part of. + * To which peer does this connection go? */ - struct CadetTunnel *t; + struct CadetPeer *destination; /** - * Flow control information for traffic fwd. + * Which tunnel is using this connection? */ - struct CadetFlowControl fwd_fc; + struct CadetTConnection *ct; /** - * Flow control information for traffic bck. + * Path we are using to our destination. */ - struct CadetFlowControl bck_fc; + struct CadetPeerPath *path; /** - * Measure connection performance on the endpoint. + * Pending message, NULL if we are ready to transmit. */ - struct CadetConnectionPerformance *perf; + struct GNUNET_MQ_Envelope *env; /** - * ID of the connection. + * Handle for calling #GCP_request_mq_cancel() once we are finished. */ - struct GNUNET_CADET_ConnectionTunnelIdentifier id; + struct GCP_MessageQueueManager *mq_man; /** - * Path being used for the tunnel. At the origin of the connection - * it's a pointer to the destination's path pool, otherwise just a copy. + * Task for connection maintenance. */ - struct CadetPeerPath *path; + struct GNUNET_SCHEDULER_Task *task; /** - * Task to keep the used paths alive at the owner, - * time tunnel out on all the other peers. + * Queue entry for keepalive messages. */ - struct GNUNET_SCHEDULER_Task *fwd_maintenance_task; + struct CadetTunnelQueueEntry *keepalive_qe; /** - * Task to keep the used paths alive at the destination, - * time tunnel out on all the other peers. + * Function to call once we are ready to transmit. */ - struct GNUNET_SCHEDULER_Task *bck_maintenance_task; + GCC_ReadyCallback ready_cb; /** - * Queue handle for maintainance traffic. One handle for FWD and BCK since - * one peer never needs to maintain both directions (no loopback connections). + * Closure for @e ready_cb. */ - struct CadetPeerQueue *maintenance_q; + void *ready_cb_cls; /** - * Should equal #get_next_hop(), or NULL if that peer disconnected. + * How long do we wait before we try again with a CREATE message? */ - struct CadetPeer *next_peer; + struct GNUNET_TIME_Relative retry_delay; /** - * Should equal #get_prev_hop(), or NULL if that peer disconnected. + * Performance metrics for this connection. */ - struct CadetPeer *prev_peer; + struct CadetConnectionMetrics metrics; /** * State of the connection. @@ -260,221 +144,36 @@ struct CadetConnection enum CadetConnectionState state; /** - * Position of the local peer in the path. + * Options for the route, control buffering. */ - unsigned int own_pos; + enum GNUNET_CADET_ChannelOption options; /** - * Pending message count. + * How many latency observations did we make for this connection? */ - unsigned int pending_messages; + unsigned int latency_datapoints; /** - * Destroy flag: - * - if 0, connection in use. - * - if 1, destroy on last message. - * - if 2, connection is being destroyed don't re-enter. + * Offset of our @e destination in @e path. */ - int destroy; + unsigned int off; /** - * In-connection-map flag. Sometimes, when @e destroy is set but - * actual destruction is delayed to enable us to finish processing - * queues (i.e. in the direction that is still working), we remove - * the connection from the map to prevent it from still being - * found (and used) by accident. This flag is set to #GNUNET_YES - * for a connection that is not in the #connections map. Should - * only be #GNUNET_YES if #destroy is also non-zero. + * Are we ready to transmit via @e mq_man right now? */ - int was_removed; + int mqm_ready; - /** - * Counter to do exponential backoff when creating a connection (max 64). - */ - unsigned short create_retry; - - /** - * Task to check if connection has duplicates. - */ - struct GNUNET_SCHEDULER_Task *check_duplicates_task; }; -/******************************************************************************/ -/******************************* GLOBALS ***********************************/ -/******************************************************************************/ - -/** - * Global handle to the statistics service. - */ -extern struct GNUNET_STATISTICS_Handle *stats; - -/** - * Local peer own ID (memory efficient handle). - */ -extern GNUNET_PEER_Id myid; - -/** - * Local peer own ID (full value). - */ -extern struct GNUNET_PeerIdentity my_full_id; - -/** - * Connections known, indexed by cid (CadetConnection). - */ -static struct GNUNET_CONTAINER_MultiShortmap *connections; - -/** - * How many connections are we willing to maintain. - * Local connections are always allowed, - * even if there are more connections than max. - */ -static unsigned long long max_connections; - -/** - * How many messages *in total* are we willing to queue, divide by number of - * connections to get connection queue size. - */ -static unsigned long long max_msgs_queue; - -/** - * How often to send path keepalives. Paths timeout after 4 missed. - */ -static struct GNUNET_TIME_Relative refresh_connection_time; - -/** - * How often to send path create / ACKs. - */ -static struct GNUNET_TIME_Relative create_connection_time; - - -/******************************************************************************/ -/******************************** STATIC ***********************************/ -/******************************************************************************/ - - - -#if 0 // avoid compiler warning for unused static function -static void -fc_debug (struct CadetFlowControl *fc) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, " IN: %u/%u\n", - ntohl (fc->last_pid_recv.pid), - ntohl (fc->last_ack_sent.pid)); - LOG (GNUNET_ERROR_TYPE_DEBUG, " OUT: %u/%u\n", - fc->last_pid_sent, fc->last_ack_recv); - LOG (GNUNET_ERROR_TYPE_DEBUG, " QUEUE: %u/%u\n", - fc->queue_n, fc->queue_max); -} - -static void -connection_debug (struct CadetConnection *c) -{ - if (NULL == c) - { - LOG (GNUNET_ERROR_TYPE_INFO, "DEBUG NULL CONNECTION\n"); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, "Connection %s:%X\n", - peer2s (c->t->peer), GCC_2s (c)); - LOG (GNUNET_ERROR_TYPE_DEBUG, " state: %u, pending msgs: %u\n", - c->state, c->pending_messages); - LOG (GNUNET_ERROR_TYPE_DEBUG, " FWD FC\n"); - fc_debug (&c->fwd_fc); - LOG (GNUNET_ERROR_TYPE_DEBUG, " BCK FC\n"); - fc_debug (&c->bck_fc); -} -#endif - - -/** - * Schedule next keepalive task, taking in consideration - * the connection state and number of retries. - * - * @param c Connection for which to schedule the next keepalive. - * @param fwd Direction for the next keepalive. - */ -static void -schedule_next_keepalive (struct CadetConnection *c, int fwd); - - -/** - * Resets the connection timeout task, some other message has done the - * task's job. - * - For the first peer on the direction this means to send - * a keepalive or a path confirmation message (either create or ACK). - * - For all other peers, this means to destroy the connection, - * due to lack of activity. - * Starts the timeout if no timeout was running (connection just created). - * - * @param c Connection whose timeout to reset. - * @param fwd Is this forward? - */ -static void -connection_reset_timeout (struct CadetConnection *c, int fwd); - - -/** - * Get string description for tunnel state. Reentrant. - * - * @param s Tunnel state. - * - * @return String representation. - */ -static const char * -GCC_state2s (enum CadetConnectionState s) -{ - switch (s) - { - case CADET_CONNECTION_NEW: - return "CADET_CONNECTION_NEW"; - case CADET_CONNECTION_SENT: - return "CADET_CONNECTION_SENT"; - case CADET_CONNECTION_ACK: - return "CADET_CONNECTION_ACK"; - case CADET_CONNECTION_READY: - return "CADET_CONNECTION_READY"; - case CADET_CONNECTION_DESTROYED: - return "CADET_CONNECTION_DESTROYED"; - case CADET_CONNECTION_BROKEN: - return "CADET_CONNECTION_BROKEN"; - default: - GNUNET_break (0); - LOG (GNUNET_ERROR_TYPE_ERROR, " conn state %u unknown!\n", s); - return "CADET_CONNECTION_STATE_ERROR"; - } -} - - /** - * Initialize a Flow Control structure to the initial state. + * Lookup a connection by its identifier. * - * @param fc Flow Control structure to initialize. + * @param cid identifier to resolve + * @return NULL if connection was not found */ -static void -fc_init (struct CadetFlowControl *fc) -{ - fc->next_pid.pid = 0; - fc->last_pid_sent.pid = htonl (UINT32_MAX); - fc->last_pid_recv.pid = htonl (UINT32_MAX); - fc->last_ack_sent.pid = (uint32_t) 0; - fc->last_ack_recv.pid = (uint32_t) 0; - fc->poll_task = NULL; - fc->poll_time = GNUNET_TIME_UNIT_SECONDS; - fc->queue_n = 0; - fc->queue_max = (max_msgs_queue / max_connections) + 1; -} - - -/** - * Find a connection. - * - * @param cid Connection ID. - * - * @return conntection with the given ID @cid or NULL if not found. - */ -static struct CadetConnection * -connection_get (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) +struct CadetConnection * +GCC_lookup (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) { return GNUNET_CONTAINER_multishortmap_get (connections, &cid->connection_of_tunnel); @@ -482,3232 +181,911 @@ connection_get (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) /** - * Change the connection state. Cannot change a connection marked as destroyed. + * Update the connection state. Also triggers the necessary + * MQM notifications. * - * @param c Connection to change. - * @param state New state to set. + * @param cc connection to update the state for + * @param new_state new state for @a cc + * @param new_mqm_ready new `mqm_ready` state for @a cc */ static void -connection_change_state (struct CadetConnection* c, - enum CadetConnectionState state) +update_state (struct CadetConnection *cc, + enum CadetConnectionState new_state, + int new_mqm_ready) { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Connection %s state %s -> %s\n", - GCC_2s (c), GCC_state2s (c->state), GCC_state2s (state)); - if (CADET_CONNECTION_DESTROYED <= c->state) /* Destroyed or broken. */ - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "state not changing anymore\n"); - return; - } - c->state = state; - if (CADET_CONNECTION_READY == state) - c->create_retry = 1; -} - + int old_ready; + int new_ready; -/** - * Mark a connection as "destroyed", to send all pending traffic and freeing - * all associated resources, without accepting new status changes on it. - * - * @param c Connection to mark as destroyed. - */ -static void -mark_destroyed (struct CadetConnection *c) -{ - c->destroy = GNUNET_YES; - connection_change_state (c, CADET_CONNECTION_DESTROYED); + if ( (new_state == cc->state) && + (new_mqm_ready == cc->mqm_ready) ) + return; /* no change, nothing to do */ + old_ready = ( (CADET_CONNECTION_READY == cc->state) && + (GNUNET_YES == cc->mqm_ready) ); + new_ready = ( (CADET_CONNECTION_READY == new_state) && + (GNUNET_YES == new_mqm_ready) ); + cc->state = new_state; + cc->mqm_ready = new_mqm_ready; + if (old_ready != new_ready) + cc->ready_cb (cc->ready_cb_cls, + new_ready); } /** - * Function called if a connection has been stalled for a while, - * possibly due to a missed ACK. Poll the neighbor about its ACK status. - * - * @param cls Closure (poll ctx). - */ -static void -send_poll (void *cls); - - -/** - * Send an ACK on the connection, informing the predecessor about - * the available buffer space. Should not be called in case the peer - * is origin (no predecessor) in the @c fwd direction. - * - * Note that for fwd ack, the FWD mean forward *traffic* (root->dest), - * the ACK itself goes "back" (dest->root). + * Destroy a connection, part of the internal implementation. Called + * only from #GCC_destroy_from_core() or #GCC_destroy_from_tunnel(). * - * @param c Connection on which to send the ACK. - * @param buffer How much space free to advertise? - * @param fwd Is this FWD ACK? (Going dest -> root) - * @param force Don't optimize out. + * @param cc connection to destroy */ static void -send_ack (struct CadetConnection *c, - unsigned int buffer, - int fwd, - int force) +GCC_destroy (struct CadetConnection *cc) { - static struct CadetEncryptedMessageIdentifier zero; - struct CadetFlowControl *next_fc; - struct CadetFlowControl *prev_fc; - struct GNUNET_CADET_ConnectionEncryptedAckMessage msg; - struct CadetEncryptedMessageIdentifier ack_cemi; - int delta; - - GCC_check_connections (); - GNUNET_assert (GNUNET_NO == GCC_is_origin (c, fwd)); - - next_fc = fwd ? &c->fwd_fc : &c->bck_fc; - prev_fc = fwd ? &c->bck_fc : &c->fwd_fc; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "send %s ack on %s\n", - GC_f2s (fwd), GCC_2s (c)); - - /* Check if we need to transmit the ACK. */ - delta = ntohl (prev_fc->last_ack_sent.pid) - ntohl (prev_fc->last_pid_recv.pid); - if (3 < delta && buffer < delta && GNUNET_NO == force) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "Not sending ACK, delta > 3\n"); - LOG (GNUNET_ERROR_TYPE_DEBUG, - " last pid recv: %u, last ack sent: %u\n", - ntohl (prev_fc->last_pid_recv.pid), - ntohl (prev_fc->last_ack_sent.pid)); - GCC_check_connections (); - return; - } - - /* Ok, ACK might be necessary, what PID to ACK? */ - ack_cemi.pid = htonl (ntohl (prev_fc->last_pid_recv.pid) + buffer); LOG (GNUNET_ERROR_TYPE_DEBUG, - " ACK %u, last PID %u, last ACK %u, qmax %u, q %u\n", - ntohl (ack_cemi.pid), - ntohl (prev_fc->last_pid_recv.pid), - ntohl (prev_fc->last_ack_sent.pid), - next_fc->queue_max, next_fc->queue_n); - if ( (ack_cemi.pid == prev_fc->last_ack_sent.pid) && - (GNUNET_NO == force) ) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "Not sending FWD ACK, not needed\n"); - GCC_check_connections (); - return; - } - - /* Check if message is already in queue */ - if (NULL != prev_fc->ack_msg) - { - if (GC_is_pid_bigger (ntohl (ack_cemi.pid), - ntohl (prev_fc->last_ack_sent.pid))) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " canceling old ACK\n"); - GCC_cancel (prev_fc->ack_msg); - /* GCC_cancel triggers ack_sent(), which clears fc->ack_msg */ - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " same ACK already in queue\n"); - GCC_check_connections (); - return; - } - } - GNUNET_break (GC_is_pid_bigger (ntohl (ack_cemi.pid), - ntohl (prev_fc->last_ack_sent.pid))); - prev_fc->last_ack_sent = ack_cemi; - - /* Build ACK message and send on conn */ - msg.header.size = htons (sizeof (msg)); - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CONNECTION_HOP_BY_HOP_ENCRYPTED_ACK); - msg.cemi_max = ack_cemi; - msg.cid = c->id; - - prev_fc->ack_msg = GCC_send_prebuilt_message (&msg.header, - UINT16_MAX, - zero, - c, - !fwd, - GNUNET_YES, - NULL, NULL); - GNUNET_assert (NULL != prev_fc->ack_msg); - GCC_check_connections (); -} - - -/** - * Update performance information if we are a connection's endpoint. - * - * @param c Connection to update. - * @param wait How much time did we wait to send the last message. - * @param size Size of the last message. - */ -static void -update_perf (struct CadetConnection *c, - struct GNUNET_TIME_Relative wait, - uint16_t size) -{ - struct CadetConnectionPerformance *p; - double usecsperbyte; - - if (NULL == c->perf) - return; /* Only endpoints are interested in timing. */ - - p = c->perf; - usecsperbyte = ((double) wait.rel_value_us) / size; - if (p->size == AVG_MSGS) - { - /* Array is full. Substract oldest value, add new one and store. */ - p->avg -= (p->usecsperbyte[p->idx] / AVG_MSGS); - p->usecsperbyte[p->idx] = usecsperbyte; - p->avg += (p->usecsperbyte[p->idx] / AVG_MSGS); - } - else - { - /* Array not yet full. Add current value to avg and store. */ - p->usecsperbyte[p->idx] = usecsperbyte; - p->avg *= p->size; - p->avg += p->usecsperbyte[p->idx]; - p->size++; - p->avg /= p->size; - } - p->idx = (p->idx + 1) % AVG_MSGS; -} - - -/** - * Callback called when a connection queued message is sent. - * - * Calculates the average time and connection packet tracking. - * - * @param cls Closure (ConnectionQueue Handle), can be NULL. - * @param c Connection this message was on. - * @param fwd Was this a FWD going message? - * @param sent Was it really sent? (Could have been canceled) - * @param type Type of message sent. - * @param payload_type Type of payload, if applicable. - * @param pid Message ID, or 0 if not applicable (create, destroy, etc). - * @param size Size of the message. - * @param wait Time spent waiting for core (only the time for THIS message) - */ -static void -conn_message_sent (void *cls, - struct CadetConnection *c, - int fwd, - int sent, - uint16_t type, - uint16_t payload_type, - struct CadetEncryptedMessageIdentifier pid, - size_t size, - struct GNUNET_TIME_Relative wait) -{ - struct CadetConnectionQueue *q = cls; - struct CadetFlowControl *fc; - int forced; - - GCC_check_connections (); - LOG (GNUNET_ERROR_TYPE_INFO, - ">>> %s (%s %4u) on conn %s (%p) %s [%5u] in queue %s\n", - GC_m2s (type), GC_m2s (payload_type), - ntohl (pid.pid), - GCC_2s (c), - c, - GC_f2s (fwd), size, - GNUNET_STRINGS_relative_time_to_string (wait, GNUNET_YES)); - - /* If c is NULL, nothing to update. */ - if (NULL == c) - { - if (type != GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN - && type != GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY) - { - LOG (GNUNET_ERROR_TYPE_ERROR, "Message %s sent on NULL connection!\n", - GC_m2s (type)); - } - GCC_check_connections (); - return; - } - - LOG (GNUNET_ERROR_TYPE_DEBUG, " %ssent %s %s pid %u\n", - sent ? "" : "not ", GC_f2s (fwd), - GC_m2s (type), GC_m2s (payload_type), - ntohl (pid.pid)); - GCC_debug (c, GNUNET_ERROR_TYPE_DEBUG); - - /* Update flow control info. */ - fc = fwd ? &c->fwd_fc : &c->bck_fc; - - if (NULL != q) + "Destroying %s\n", + GCC_2s (cc)); + if (NULL != cc->mq_man) { - GNUNET_CONTAINER_DLL_remove (fc->q_head, fc->q_tail, q); - forced = q->forced; - if (NULL != q->cont) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " calling cont\n"); - q->cont (q->cont_cls, c, q, type, fwd, size); - } - GNUNET_free (q); + GCP_request_mq_cancel (cc->mq_man, + NULL); + cc->mq_man = NULL; } - else /* CONN_CREATE or CONN_ACK */ + if (NULL != cc->task) { - GNUNET_assert (GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED != type); - forced = GNUNET_YES; + GNUNET_SCHEDULER_cancel (cc->task); + cc->task = NULL; } - - LOG (GNUNET_ERROR_TYPE_DEBUG, " C_P- %p %u\n", c, c->pending_messages); - c->pending_messages--; - if ( (GNUNET_YES == c->destroy) && - (0 == c->pending_messages) ) + if (NULL != cc->keepalive_qe) { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "! destroying connection!\n"); - GCC_destroy (c); - GCC_check_connections (); - return; + GCT_send_cancel (cc->keepalive_qe); + cc->keepalive_qe = NULL; } - - /* Send ACK if needed, after accounting for sent ID in fc->queue_n */ - switch (type) - { - case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE: - case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK: - c->maintenance_q = NULL; - /* Don't trigger a keepalive for sent ACKs, only SYN and SYNACKs */ - if (GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE == type || !fwd) - schedule_next_keepalive (c, fwd); - break; - - case GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED: - if (GNUNET_YES == sent) - { - fc->last_pid_sent = pid; - if (GC_is_pid_bigger (ntohl (fc->last_pid_sent.pid) + 1, - ntohl (fc->last_ack_recv.pid)) ) - GCC_start_poll (c, fwd); - GCC_send_ack (c, fwd, GNUNET_NO); - connection_reset_timeout (c, fwd); - } - - LOG (GNUNET_ERROR_TYPE_DEBUG, "! Q_N- %p %u\n", fc, fc->queue_n); - if (GNUNET_NO == forced) - { - fc->queue_n--; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "! accounting pid %u\n", - ntohl (fc->last_pid_sent.pid)); - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "! forced, Q_N not accounting pid %u\n", - ntohl (fc->last_pid_sent.pid)); - } - break; - - case GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX: - if (GNUNET_YES == sent) - connection_reset_timeout (c, fwd); - break; - - case GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED_POLL: - fc->poll_msg = NULL; - if (2 == c->destroy) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "POLL canceled on shutdown\n"); - return; - } - if (0 == fc->queue_max) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "POLL cancelled: neighbor disconnected\n"); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, "POLL sent for %s, scheduling new one!\n", - GCC_2s (c)); - GNUNET_assert (NULL == fc->poll_task); - fc->poll_time = GNUNET_TIME_STD_BACKOFF (fc->poll_time); - fc->poll_task = GNUNET_SCHEDULER_add_delayed (fc->poll_time, - &send_poll, fc); - LOG (GNUNET_ERROR_TYPE_DEBUG, " task %u\n", fc->poll_task); - break; - - case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_HOP_BY_HOP_ENCRYPTED_ACK: - fc->ack_msg = NULL; - break; - - case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN: - case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY: - break; - - default: - LOG (GNUNET_ERROR_TYPE_ERROR, "%s unknown\n", GC_m2s (type)); - GNUNET_break (0); - break; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, "! message sent!\n"); - - update_perf (c, wait, size); - GCC_check_connections (); -} - - -/** - * Get the previous hop in a connection - * - * @param c Connection. - * - * @return Previous peer in the connection. - */ -static struct CadetPeer * -get_prev_hop (const struct CadetConnection *c) -{ - GNUNET_PEER_Id id; - - if (NULL == c->path) - return NULL; - LOG (GNUNET_ERROR_TYPE_DEBUG, - " get prev hop %s [%u/%u]\n", - GCC_2s (c), c->own_pos, c->path->length); - if (0 == c->own_pos || c->path->length < 2) - id = c->path->peers[0]; - else - id = c->path->peers[c->own_pos - 1]; - - LOG (GNUNET_ERROR_TYPE_DEBUG, " ID: %s (%u)\n", - GNUNET_i2s (GNUNET_PEER_resolve2 (id)), id); - - return GCP_get_short (id, GNUNET_YES); -} - - -/** - * Get the next hop in a connection - * - * @param c Connection. - * - * @return Next peer in the connection. - */ -static struct CadetPeer * -get_next_hop (const struct CadetConnection *c) -{ - GNUNET_PEER_Id id; - - if (NULL == c->path) - return NULL; - - LOG (GNUNET_ERROR_TYPE_DEBUG, " get next hop %s [%u/%u]\n", - GCC_2s (c), c->own_pos, c->path->length); - if ((c->path->length - 1) == c->own_pos || c->path->length < 2) - id = c->path->peers[c->path->length - 1]; - else - id = c->path->peers[c->own_pos + 1]; - - LOG (GNUNET_ERROR_TYPE_DEBUG, " ID: %s (%u)\n", - GNUNET_i2s (GNUNET_PEER_resolve2 (id)), id); - - return GCP_get_short (id, GNUNET_YES); + GCPP_del_connection (cc->path, + cc->off, + cc); + for (unsigned int i=0;i<cc->off;i++) + GCP_remove_connection (GCPP_get_peer_at_offset (cc->path, + i), + cc); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multishortmap_remove (connections, + &GCC_get_id (cc)->connection_of_tunnel, + cc)); + GNUNET_free (cc); } -/** - * Check that the direct neighbours (previous and next hop) - * are properly associated with this connection. - * - * @param c connection to check - */ -static void -check_neighbours (const struct CadetConnection *c) -{ - if (NULL == c->path) - return; /* nothing to check */ - GCP_check_connection (get_next_hop (c), c); - GCP_check_connection (get_prev_hop (c), c); -} - /** - * Helper for #GCC_check_connections(). Calls #check_neighbours(). + * Destroy a connection, called when the CORE layer is already done + * (i.e. has received a BROKEN message), but if we still have to + * communicate the destruction of the connection to the tunnel (if one + * exists). * - * @param cls NULL - * @param key ignored - * @param value the `struct CadetConnection` to check - * @return #GNUNET_OK (continue to iterate) - */ -static int -check_connection (void *cls, - const struct GNUNET_ShortHashCode *key, - void *value) -{ - struct CadetConnection *c = value; - - check_neighbours (c); - return GNUNET_OK; -} - - -/** - * Check invariants for all connections using #check_neighbours(). + * @param cc connection to destroy */ void -GCC_check_connections () -{ - if (0 == CHECK_INVARIANTS) - return; - if (NULL == connections) - return; - GNUNET_CONTAINER_multishortmap_iterate (connections, - &check_connection, - NULL); -} - - -/** - * Get the hop in a connection. - * - * @param c Connection. - * @param fwd Next in the FWD direction? - * - * @return Next peer in the connection. - */ -static struct CadetPeer * -get_hop (struct CadetConnection *c, int fwd) -{ - return (fwd) ? get_next_hop (c) : get_prev_hop (c); -} - - -/** - * Get a bit mask for a message received out-of-order. - * - * @param last_pid_recv Last PID we received prior to the out-of-order. - * @param ooo_pid PID of the out-of-order message. - */ -static uint32_t -get_recv_bitmask (struct CadetEncryptedMessageIdentifier last_pid_recv, - struct CadetEncryptedMessageIdentifier ooo_pid) -{ - // FIXME: should assert that the delta is in range... - return 1 << (ntohl (last_pid_recv.pid) - ntohl (ooo_pid.pid)); -} - - -/** - * Check is an out-of-order message is ok: - * - at most 31 messages behind. - * - not duplicate. - * - * @param last_pid_recv Last in-order PID received. - */ -static int -is_ooo_ok (struct CadetEncryptedMessageIdentifier last_pid_recv, - struct CadetEncryptedMessageIdentifier ooo_pid, - uint32_t ooo_bitmap) -{ - uint32_t mask; - - if (GC_is_pid_bigger (ntohl (last_pid_recv.pid) - 31, - ntohl (ooo_pid.pid))) - return GNUNET_NO; - - mask = get_recv_bitmask (last_pid_recv, - ooo_pid); - if (0 != (ooo_bitmap & mask)) - return GNUNET_NO; - - return GNUNET_YES; -} - - -/** - * Is traffic coming from this sender 'FWD' traffic? - * - * @param c Connection to check. - * @param sender Short peer identity of neighbor. - * - * @return #GNUNET_YES in case the sender is the 'prev' hop and therefore - * the traffic is 'FWD'. - * #GNUNET_NO for BCK. - * #GNUNET_SYSERR for errors (sender isn't a hop in the connection). - */ -static int -is_fwd (const struct CadetConnection *c, - const struct CadetPeer *sender) -{ - GNUNET_PEER_Id id; - - id = GCP_get_short_id (sender); - if (GCP_get_short_id (get_prev_hop (c)) == id) - return GNUNET_YES; - - if (GCP_get_short_id (get_next_hop (c)) == id) - return GNUNET_NO; - - return GNUNET_SYSERR; -} - - -/** - * Sends a CONNECTION ACK message in reponse to a received CONNECTION_CREATE - * or a first CONNECTION_ACK directed to us. - * - * @param c Connection to confirm. - * @param fwd Should we send it FWD? (root->dest) - * (First (~SYNACK) goes BCK, second (~ACK) goes FWD) - */ -static void -send_connection_ack (struct CadetConnection *c, int fwd) -{ - static struct CadetEncryptedMessageIdentifier zero; - struct GNUNET_CADET_ConnectionCreateAckMessage msg; - struct CadetTunnel *t; - const uint16_t size = sizeof (struct GNUNET_CADET_ConnectionCreateAckMessage); - const uint16_t type = GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK; - - GCC_check_connections (); - t = c->t; - LOG (GNUNET_ERROR_TYPE_INFO, - "==> %s ({ C %s ACK} 0) on conn %s (%p) %s [%5u]\n", - GC_m2s (type), GC_f2s (!fwd), GCC_2s (c), c, GC_f2s (fwd), size); - - msg.header.size = htons (size); - msg.header.type = htons (type); - msg.reserved = htonl (0); - msg.cid = c->id; - - GNUNET_assert (NULL == c->maintenance_q); - c->maintenance_q = GCP_send (get_hop (c, fwd), - &msg.header, - GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK, - zero, - c, - fwd, - &conn_message_sent, NULL); - LOG (GNUNET_ERROR_TYPE_DEBUG, " C_P+ %p %u (conn`ACK)\n", - c, c->pending_messages); - c->pending_messages++; - - if (CADET_TUNNEL_NEW == GCT_get_cstate (t)) - GCT_change_cstate (t, CADET_TUNNEL_WAITING); - if (CADET_CONNECTION_READY != c->state) - connection_change_state (c, CADET_CONNECTION_SENT); - GCC_check_connections (); -} - - -/** - * Send a notification that a connection is broken. - * - * @param c Connection that is broken. - * @param id1 Peer that has disconnected. - * @param id2 Peer that has disconnected. - * @param fwd Direction towards which to send it. - */ -static void -send_broken (struct CadetConnection *c, - const struct GNUNET_PeerIdentity *id1, - const struct GNUNET_PeerIdentity *id2, - int fwd) -{ - static struct CadetEncryptedMessageIdentifier zero; - struct GNUNET_CADET_ConnectionBrokenMessage msg; - - GCC_check_connections (); - msg.header.size = htons (sizeof (struct GNUNET_CADET_ConnectionBrokenMessage)); - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN); - msg.cid = c->id; - msg.reserved = htonl (0); - msg.peer1 = *id1; - msg.peer2 = *id2; - (void) GCC_send_prebuilt_message (&msg.header, - UINT16_MAX, - zero, - c, - fwd, - GNUNET_YES, - NULL, NULL); - GCC_check_connections (); -} - - -/** - * Send a notification that a connection is broken, when a connection - * isn't even known to the local peer or soon to be destroyed. - * - * @param connection_id Connection ID. - * @param id1 Peer that has disconnected, probably local peer. - * @param id2 Peer that has disconnected can be NULL if unknown. - * @param neighbor Peer to notify (neighbor who sent the connection). - */ -static void -send_broken_unknown (const struct GNUNET_CADET_ConnectionTunnelIdentifier *connection_id, - const struct GNUNET_PeerIdentity *id1, - const struct GNUNET_PeerIdentity *id2, - struct CadetPeer *neighbor) -{ - static struct CadetEncryptedMessageIdentifier zero; - struct GNUNET_CADET_ConnectionBrokenMessage msg; - - GCC_check_connections (); - LOG (GNUNET_ERROR_TYPE_INFO, "--> BROKEN on unknown connection %s\n", - GNUNET_sh2s (&connection_id->connection_of_tunnel)); - - msg.header.size = htons (sizeof (struct GNUNET_CADET_ConnectionBrokenMessage)); - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN); - msg.cid = *connection_id; - msg.reserved = htonl (0); - msg.peer1 = *id1; - if (NULL != id2) - msg.peer2 = *id2; - else - memset (&msg.peer2, 0, sizeof (msg.peer2)); - GNUNET_assert (NULL != GCP_send (neighbor, - &msg.header, - UINT16_MAX, - zero, - NULL, - GNUNET_SYSERR, /* connection, fwd */ - NULL, NULL)); /* continuation */ - GCC_check_connections (); -} - - -/** - * Send keepalive packets for a connection. - * - * @param c Connection to keep alive.. - * @param fwd Is this a FWD keepalive? (owner -> dest). - */ -static void -send_connection_keepalive (struct CadetConnection *c, int fwd) +GCC_destroy_without_core (struct CadetConnection *cc) { - struct GNUNET_MessageHeader msg; - struct CadetFlowControl *fc; - int tunnel_ready; - - GCC_check_connections (); - LOG (GNUNET_ERROR_TYPE_INFO, - "keepalive %s for connection %s\n", - GC_f2s (fwd), GCC_2s (c)); - - GNUNET_assert (NULL != c->t); - fc = fwd ? &c->fwd_fc : &c->bck_fc; - tunnel_ready = GNUNET_YES == GCT_has_queued_traffic (c->t) - && CADET_TUNNEL_KEY_OK <= GCT_get_estate (c->t); - if (0 < fc->queue_n || tunnel_ready) + if (NULL != cc->ct) { - LOG (GNUNET_ERROR_TYPE_INFO, "not sending keepalive, traffic in queue\n"); - return; + GCT_connection_lost (cc->ct); + cc->ct = NULL; } - - GNUNET_STATISTICS_update (stats, "# keepalives sent", 1, GNUNET_NO); - - GNUNET_assert (NULL != c->t); - msg.size = htons (sizeof (msg)); - msg.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE); - - GNUNET_assert (NULL == - GCT_send_prebuilt_message (&msg, c->t, c, - GNUNET_NO, NULL, NULL)); - GCC_check_connections (); -} - - -/** - * Send CONNECTION_{CREATE/ACK} packets for a connection. - * - * @param c Connection for which to send the message. - * @param fwd If #GNUNET_YES, send CREATE, otherwise send ACK. - */ -static void -connection_recreate (struct CadetConnection *c, int fwd) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "sending connection recreate\n"); - if (fwd) - GCC_send_create (c); - else - send_connection_ack (c, GNUNET_NO); + GCC_destroy (cc); } /** - * Generic connection timer management. - * Depending on the role of the peer in the connection will send the - * appropriate message (build or keepalive) + * Destroy a connection, called if the tunnel association with the + * connection was already broken, but we still need to notify the CORE + * layer about the breakage. * - * @param c Conncetion to maintain. - * @param fwd Is FWD? + * @param cc connection to destroy */ -static void -connection_maintain (struct CadetConnection *c, int fwd) +void +GCC_destroy_without_tunnel (struct CadetConnection *cc) { - if (GNUNET_NO != c->destroy) + cc->ct = NULL; + if ( (CADET_CONNECTION_SENDING_CREATE != cc->state) && + (NULL != cc->mq_man) ) { - LOG (GNUNET_ERROR_TYPE_INFO, "not sending keepalive, being destroyed\n"); - return; - } - - if (NULL == c->t) - { - GNUNET_break (0); - GCC_debug (c, GNUNET_ERROR_TYPE_ERROR); - return; - } + struct GNUNET_MQ_Envelope *env; + struct GNUNET_CADET_ConnectionDestroyMessage *destroy_msg; - if (CADET_TUNNEL_SEARCHING == GCT_get_cstate (c->t)) - { - /* If status is SEARCHING, why is there a connection? Should be WAITING */ - GNUNET_break (0); - GCT_debug (c->t, GNUNET_ERROR_TYPE_ERROR); - LOG (GNUNET_ERROR_TYPE_INFO, "not sending keepalive, tunnel SEARCHING\n"); - schedule_next_keepalive (c, fwd); - return; - } - switch (c->state) - { - case CADET_CONNECTION_NEW: - GNUNET_break (0); - /* fall-through */ - case CADET_CONNECTION_SENT: - connection_recreate (c, fwd); - break; - case CADET_CONNECTION_READY: - send_connection_keepalive (c, fwd); - break; - default: - break; + /* Need to notify next hop that we are down. */ + env = GNUNET_MQ_msg (destroy_msg, + GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY); + destroy_msg->cid = cc->cid; + GCP_request_mq_cancel (cc->mq_man, + env); + cc->mq_man = NULL; } + GCC_destroy (cc); } /** - * Keep the connection alive. + * Return the tunnel associated with this connection. * - * @param c Connection to keep alive. - * @param fwd Direction. + * @param cc connection to query + * @return corresponding entry in the tunnel's connection list */ -static void -connection_keepalive (struct CadetConnection *c, - int fwd) +struct CadetTConnection * +GCC_get_ct (struct CadetConnection *cc) { - GCC_check_connections (); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "%s keepalive for %s\n", - GC_f2s (fwd), GCC_2s (c)); - - if (fwd) - c->fwd_maintenance_task = NULL; - else - c->bck_maintenance_task = NULL; - connection_maintain (c, fwd); - GCC_check_connections (); - /* Next execution will be scheduled by message_sent or _maintain*/ + return cc->ct; } /** - * Keep the connection alive in the FWD direction. + * Obtain performance @a metrics from @a cc. * - * @param cls Closure (connection to keepalive). + * @param cc connection to query + * @return the metrics */ -static void -connection_fwd_keepalive (void *cls) +const struct CadetConnectionMetrics * +GCC_get_metrics (struct CadetConnection *cc) { - struct CadetConnection *c = cls; - - GCC_check_connections (); - connection_keepalive (c, - GNUNET_YES); - GCC_check_connections (); + return &cc->metrics; } /** - * Keep the connection alive in the BCK direction. + * Send a #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE through the + * tunnel to prevent it from timing out. * - * @param cls Closure (connection to keepalive). + * @param cls the `struct CadetConnection` to keep alive. */ static void -connection_bck_keepalive (void *cls) -{ - struct CadetConnection *c = cls; - - GCC_check_connections (); - connection_keepalive (c, - GNUNET_NO); - GCC_check_connections (); -} +send_keepalive (void *cls); /** - * Schedule next keepalive task, taking in consideration - * the connection state and number of retries. + * Keepalive was transmitted. Remember this, and possibly + * schedule the next one. * - * If the peer is not the origin, do nothing. - * - * @param c Connection for which to schedule the next keepalive. - * @param fwd Direction for the next keepalive. + * @param cls the `struct CadetConnection` to keep alive. + * @param cid identifier of the connection within the tunnel, NULL + * if transmission failed */ static void -schedule_next_keepalive (struct CadetConnection *c, int fwd) +keepalive_done (void *cls, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) { - struct GNUNET_TIME_Relative delay; - struct GNUNET_SCHEDULER_Task * *task_id; - GNUNET_SCHEDULER_TaskCallback keepalive_task; - - GCC_check_connections (); - if (GNUNET_NO == GCC_is_origin (c, fwd)) - return; - - /* Calculate delay to use, depending on the state of the connection */ - if (CADET_CONNECTION_READY == c->state) - { - delay = refresh_connection_time; - } - else - { - if (1 > c->create_retry) - c->create_retry = 1; - delay = GNUNET_TIME_relative_saturating_multiply (create_connection_time, - c->create_retry); - if (c->create_retry < 64) // TODO make configurable - c->create_retry *= 2; - } - - /* Select direction-dependent parameters */ - if (GNUNET_YES == fwd) - { - task_id = &c->fwd_maintenance_task; - keepalive_task = &connection_fwd_keepalive; - } - else - { - task_id = &c->bck_maintenance_task; - keepalive_task = &connection_bck_keepalive; - } + struct CadetConnection *cc = cls; - /* Check that no one scheduled it before us */ - if (NULL != *task_id) - { - /* No need for a _break. It can happen for instance when sending a SYNACK - * for a duplicate SYN: the first SYNACK scheduled the task. */ - GNUNET_SCHEDULER_cancel (*task_id); - } - - /* Schedule the task */ - *task_id = GNUNET_SCHEDULER_add_delayed (delay, - keepalive_task, - c); - LOG (GNUNET_ERROR_TYPE_INFO, - "next keepalive for %s in in %s\n", - GCC_2s (c), GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES)); - GCC_check_connections (); + cc->keepalive_qe = NULL; + if ( (GNUNET_YES == cc->mqm_ready) && + (NULL == cc->task) ) + cc->task = GNUNET_SCHEDULER_add_delayed (keepalive_period, + &send_keepalive, + cc); } /** - * Cancel all transmissions that belong to a certain connection. + * Send a #GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE through the + * tunnel to prevent it from timing out. * - * If the connection is scheduled for destruction and no more messages are left, - * the connection will be destroyed by the continuation call. - * - * @param c Connection which to cancel. Might be destroyed during this call. - * @param fwd Cancel fwd traffic? + * @param cls the `struct CadetConnection` to keep alive. */ static void -connection_cancel_queues (struct CadetConnection *c, - int fwd) +send_keepalive (void *cls) { - struct CadetFlowControl *fc; + struct CadetConnection *cc = cls; + struct GNUNET_MessageHeader msg; - GCC_check_connections (); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Cancel %s queues for connection %s\n", - GC_f2s (fwd), GCC_2s (c)); - if (NULL == c) + cc->task = NULL; + if (CADET_TUNNEL_KEY_OK != GCT_get_estate (cc->ct->t)) { - GNUNET_break (0); + /* Tunnel not yet ready, wait with keepalives... */ + cc->task = GNUNET_SCHEDULER_add_delayed (keepalive_period, + &send_keepalive, + cc); return; } - - fc = fwd ? &c->fwd_fc : &c->bck_fc; - if (NULL != fc->poll_task) - { - GNUNET_SCHEDULER_cancel (fc->poll_task); - fc->poll_task = NULL; - LOG (GNUNET_ERROR_TYPE_DEBUG, " cancelled POLL task for fc %p\n", fc); - } - if (NULL != fc->poll_msg) - { - GCC_cancel (fc->poll_msg); - LOG (GNUNET_ERROR_TYPE_DEBUG, " cancelled POLL msg for fc %p\n", fc); - } - - while (NULL != fc->q_head) - { - GCC_cancel (fc->q_head); - } - GCC_check_connections (); -} - - -/** - * Function called if a connection has been stalled for a while, - * possibly due to a missed ACK. Poll the neighbor about its ACK status. - * - * @param cls Closure (poll ctx). - */ -static void -send_poll (void *cls) -{ - static struct CadetEncryptedMessageIdentifier zero; - struct CadetFlowControl *fc = cls; - struct GNUNET_CADET_ConnectionHopByHopPollMessage msg; - struct CadetConnection *c; - int fwd; - - fc->poll_task = NULL; - GCC_check_connections (); - c = fc->c; - fwd = fc == &c->fwd_fc; - LOG (GNUNET_ERROR_TYPE_DEBUG, "Polling connection %s %s\n", - GCC_2s (c), GC_f2s (fwd)); - - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED_POLL); - msg.header.size = htons (sizeof (msg)); - msg.cid = c->id; - msg.cemi = fc->last_pid_sent; - LOG (GNUNET_ERROR_TYPE_DEBUG, " last pid sent: %u\n", ntohl (fc->last_pid_sent.pid)); - fc->poll_msg - = GCC_send_prebuilt_message (&msg.header, - UINT16_MAX, - zero, - c, - fc == &c->fwd_fc, - GNUNET_YES, - NULL, - NULL); - GNUNET_assert (NULL != fc->poll_msg); - GCC_check_connections (); -} - - -/** - * Generic connection timeout implementation. - * - * Timeout function due to lack of keepalive/traffic from an endpoint. - * Destroys connection if called. - * - * @param c Connection to destroy. - * @param fwd Was the timeout from the origin? (FWD timeout) - */ -static void -connection_timeout (struct CadetConnection *c, int fwd) -{ - GCC_check_connections (); - + GNUNET_assert (NULL != cc->ct); + GNUNET_assert (GNUNET_YES == cc->mqm_ready); + GNUNET_assert (NULL == cc->keepalive_qe); LOG (GNUNET_ERROR_TYPE_INFO, - "Connection %s %s timed out. Destroying.\n", - GCC_2s (c), - GC_f2s (fwd)); - GCC_debug (c, GNUNET_ERROR_TYPE_DEBUG); - - if (GCC_is_origin (c, fwd)) /* Loopback? Something is wrong! */ - { - GNUNET_break (0); - return; - } - - /* If dest, send "broken" notification. */ - if (GCC_is_terminal (c, fwd)) - { - struct CadetPeer *next_hop; - - next_hop = fwd ? get_prev_hop (c) : get_next_hop (c); - send_broken_unknown (&c->id, &my_full_id, NULL, next_hop); - } - - GCC_destroy (c); - GCC_check_connections (); -} - - -/** - * Timeout function due to lack of keepalive/traffic from the owner. - * Destroys connection if called. - * - * @param cls Closure (connection to destroy). - */ -static void -connection_fwd_timeout (void *cls) -{ - struct CadetConnection *c = cls; - - c->fwd_maintenance_task = NULL; - GCC_check_connections (); - connection_timeout (c, GNUNET_YES); - GCC_check_connections (); -} - - -/** - * Timeout function due to lack of keepalive/traffic from the destination. - * Destroys connection if called. - * - * @param cls Closure (connection to destroy). - */ -static void -connection_bck_timeout (void *cls) -{ - struct CadetConnection *c = cls; + "Sending KEEPALIVE on behalf of %s via %s\n", + GCC_2s (cc), + GCT_2s (cc->ct->t)); + GNUNET_STATISTICS_update (stats, + "# keepalives sent", + 1, + GNUNET_NO); + msg.size = htons (sizeof (msg)); + msg.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE); - c->bck_maintenance_task = NULL; - GCC_check_connections (); - connection_timeout (c, GNUNET_NO); - GCC_check_connections (); + cc->keepalive_qe + = GCT_send (cc->ct->t, + &msg, + &keepalive_done, + cc); } /** - * Resets the connection timeout task, some other message has done the - * task's job. - * - For the first peer on the direction this means to send - * a keepalive or a path confirmation message (either create or ACK). - * - For all other peers, this means to destroy the connection, - * due to lack of activity. - * Starts the timeout if no timeout was running (connection just created). - * - * @param c Connection whose timeout to reset. - * @param fwd Is this forward? + * We sent a message for which we expect to receive an ACK via + * the connection identified by @a cti. * - * TODO use heap to improve efficiency of scheduler. + * @param cid connection identifier where we expect an ACK */ -static void -connection_reset_timeout (struct CadetConnection *c, int fwd) +void +GCC_ack_expected (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) { - LOG (GNUNET_ERROR_TYPE_DEBUG, "Connection %s reset timeout\n", GC_f2s (fwd)); - if (GCC_is_origin (c, fwd)) /* Startpoint */ - { - schedule_next_keepalive (c, fwd); - if (NULL != c->maintenance_q) - { - GCP_send_cancel (c->maintenance_q); - c->maintenance_q = NULL; /* Is set to NULL by conn_message_sent anyway */ - } - } - else /* Relay, endpoint. */ - { - struct GNUNET_TIME_Relative delay; - struct GNUNET_SCHEDULER_Task * *ti; - GNUNET_SCHEDULER_TaskCallback f; - - ti = fwd ? &c->fwd_maintenance_task : &c->bck_maintenance_task; + struct CadetConnection *cc; - if (NULL != *ti) - GNUNET_SCHEDULER_cancel (*ti); - delay = GNUNET_TIME_relative_saturating_multiply (refresh_connection_time, 4); - LOG (GNUNET_ERROR_TYPE_DEBUG, - " timing out in %s\n", - GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_NO)); - f = fwd ? &connection_fwd_timeout : &connection_bck_timeout; - *ti = GNUNET_SCHEDULER_add_delayed (delay, f, c); - } + cc = GCC_lookup (cid); + if (NULL == cc) + return; /* whopise, connection alredy down? */ + cc->metrics.num_acked_transmissions++; } /** - * Iterator to compare each connection's path with the path of a new connection. + * We observed an ACK for a message that was originally sent via + * the connection identified by @a cti. * - * If the connection coincides, the c member of path is set to the connection - * and the destroy flag of the connection is set. - * - * @param cls Closure (new path). - * @param c Connection in the tunnel to check. + * @param cti connection identifier where we got an ACK for a message + * that was originally sent via this connection (the ACK + * may have gotten back to us via a different connection). */ -static void -check_path (void *cls, struct CadetConnection *c) +void +GCC_ack_observed (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid) { - struct CadetConnection *new_conn = cls; - struct CadetPeerPath *path = new_conn->path; - - LOG (GNUNET_ERROR_TYPE_DEBUG, " checking %s (%p), length %u\n", - GCC_2s (c), c, c->path->length); + struct CadetConnection *cc; - if (c != new_conn - && GNUNET_NO == c->destroy - && CADET_CONNECTION_BROKEN != c->state - && CADET_CONNECTION_DESTROYED != c->state - && path_equivalent (path, c->path)) - { - new_conn->destroy = GNUNET_YES; /* Do not mark_destroyed, */ - new_conn->path->c = c; /* this is only a flag for the Iterator. */ - LOG (GNUNET_ERROR_TYPE_DEBUG, " MATCH!\n"); - } + cc = GCC_lookup (cid); + if (NULL == cc) + return; /* whopise, connection alredy down? */ + cc->metrics.num_successes++; } /** - * Finds out if this path is already being used by an existing connection. - * - * Checks the tunnel towards the destination to see if it contains - * any connection with the same path. + * We observed some the given @a latency on the connection + * identified by @a cti. (The same connection was taken + * in both directions.) * - * If the existing connection is ready, it is kept. - * Otherwise if the sender has a smaller ID that ours, we accept it (and - * the peer will eventually reject our attempt). - * - * @param path Path to check. - * @return #GNUNET_YES if the tunnel has a connection with the same path, - * #GNUNET_NO otherwise. + * @param cid connection identifier where we measured latency + * @param latency the observed latency */ -static int -does_connection_exist (struct CadetConnection *conn) +void +GCC_latency_observed (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid, + struct GNUNET_TIME_Relative latency) { - struct CadetPeer *p; - struct CadetTunnel *t; - struct CadetConnection *c; - - p = GCP_get_short (conn->path->peers[0], GNUNET_NO); - if (NULL == p) - return GNUNET_NO; - t = GCP_get_tunnel (p); - if (NULL == t) - return GNUNET_NO; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Checking for duplicates\n"); + struct CadetConnection *cc; + double weight; + double result; - GCT_iterate_connections (t, &check_path, conn); - - if (GNUNET_YES == conn->destroy) - { - c = conn->path->c; - conn->destroy = GNUNET_NO; - conn->path->c = conn; - LOG (GNUNET_ERROR_TYPE_DEBUG, " found duplicate of %s\n", GCC_2s (conn)); - LOG (GNUNET_ERROR_TYPE_DEBUG, " duplicate: %s\n", GCC_2s (c)); - GCC_debug (c, GNUNET_ERROR_TYPE_DEBUG); - if (CADET_CONNECTION_READY == c->state) - { - /* The other peer confirmed a live connection with this path, - * why are they trying to duplicate it? */ - GNUNET_STATISTICS_update (stats, "# duplicate connections", 1, GNUNET_NO); - return GNUNET_YES; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " duplicate not ready, connection unique\n"); - return GNUNET_NO; - } + cc = GCC_lookup (cid); + if (NULL == cc) + return; /* whopise, connection alredy down? */ + GNUNET_STATISTICS_update (stats, + "# latencies observed", + 1, + GNUNET_NO); + cc->latency_datapoints++; + if (cc->latency_datapoints >= 7) + weight = 7.0; else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " %s has no duplicates\n", GCC_2s (conn)); - return GNUNET_NO; - } + weight = cc->latency_datapoints; + /* Compute weighted average, giving at MOST weight 7 to the + existing values, or less if that value is based on fewer than 7 + measurements. */ + result = (weight * cc->metrics.aged_latency.rel_value_us) + 1.0 * latency.rel_value_us; + result /= (weight + 1.0); + cc->metrics.aged_latency.rel_value_us = (uint64_t) result; } /** - * @brief Check if the tunnel this connection belongs to has any other - * connection with the same path, and destroy one if so. + * A #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK was received for this connection, implying + * that the end-to-end connection is up. Process it. * - * @param cls Closure (connection to check). - */ -static void -check_duplicates (void *cls) -{ - struct CadetConnection *c = cls; - - c->check_duplicates_task = NULL; - if (GNUNET_YES == does_connection_exist (c)) - { - GCT_debug (c->t, GNUNET_ERROR_TYPE_DEBUG); - send_broken (c, &my_full_id, &my_full_id, GCC_is_origin (c, GNUNET_YES)); - GCC_destroy (c); - } -} - - -/** - * Wait for enough time to let any dead connections time out and check for - * any remaining duplicates. - * - * @param c Connection that is a potential duplicate. - */ -static void -schedule_check_duplicates (struct CadetConnection *c) -{ - struct GNUNET_TIME_Relative delay; - - if (NULL != c->check_duplicates_task) - return; - delay = GNUNET_TIME_relative_saturating_multiply (refresh_connection_time, 5); - c->check_duplicates_task = GNUNET_SCHEDULER_add_delayed (delay, - &check_duplicates, - c); -} - - -/** - * Add the connection to the list of both neighbors. - * - * @param c Connection. - * - * @return #GNUNET_OK if everything went fine - * #GNUNET_SYSERR if the was an error and @c c is malformed. - */ -static int -register_neighbors (struct CadetConnection *c) -{ - c->next_peer = get_next_hop (c); - c->prev_peer = get_prev_hop (c); - GNUNET_assert (c->next_peer != c->prev_peer); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "register neighbors for connection %s\n", - GCC_2s (c)); - path_debug (c->path); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "own pos %u\n", c->own_pos); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "putting connection %s to next peer %p\n", - GCC_2s (c), - c->next_peer); - LOG (GNUNET_ERROR_TYPE_DEBUG, "next peer %p %s\n", - c->next_peer, - GCP_2s (c->next_peer)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "putting connection %s to prev peer %p\n", - GCC_2s (c), - c->prev_peer); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "prev peer %p %s\n", - c->prev_peer, - GCP_2s (c->prev_peer)); - - if ( (GNUNET_NO == GCP_is_neighbor (c->next_peer)) || - (GNUNET_NO == GCP_is_neighbor (c->prev_peer)) ) - { - if (GCC_is_origin (c, GNUNET_YES)) - GNUNET_STATISTICS_update (stats, "# local bad paths", 1, GNUNET_NO); - GNUNET_STATISTICS_update (stats, "# bad paths", 1, GNUNET_NO); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - " register neighbors failed\n"); - LOG (GNUNET_ERROR_TYPE_DEBUG, - " prev: %s, neighbor?: %d\n", - GCP_2s (c->prev_peer), - GCP_is_neighbor (c->prev_peer)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - " next: %s, neighbor?: %d\n", - GCP_2s (c->next_peer), - GCP_is_neighbor (c->next_peer)); - return GNUNET_SYSERR; - } - GCP_add_connection (c->next_peer, c, GNUNET_NO); - GCP_add_connection (c->prev_peer, c, GNUNET_YES); - - return GNUNET_OK; -} - - -/** - * Remove the connection from the list of both neighbors. - * - * @param c Connection. - */ -static void -unregister_neighbors (struct CadetConnection *c) -{ -// struct CadetPeer *peer; FIXME dont use next_peer, prev_peer - /* Either already unregistered or never got registered, it's ok either way. */ - if (NULL == c->path) - return; - if (NULL != c->next_peer) - { - GCP_remove_connection (c->next_peer, c); - c->next_peer = NULL; - } - if (NULL != c->prev_peer) - { - GCP_remove_connection (c->prev_peer, c); - c->prev_peer = NULL; - } -} - - -/** - * Invalidates all paths towards all peers that comprise the connection which - * rely on the disconnected peer. - * - * ~O(n^3) (peers in connection * paths/peer * links/path) - * - * @param c Connection whose peers' paths to clean. - * @param disconnected Peer that disconnected. - */ -static void -invalidate_paths (struct CadetConnection *c, - struct CadetPeer *disconnected) -{ - struct CadetPeer *peer; - unsigned int i; - - for (i = 0; i < c->path->length; i++) - { - peer = GCP_get_short (c->path->peers[i], GNUNET_NO); - if (NULL != peer) - GCP_notify_broken_link (peer, &my_full_id, GCP_get_id (disconnected)); - } -} - - -/** - * Bind the connection to the peer and the tunnel to that peer. - * - * If the peer has no tunnel, create one. Update tunnel and connection - * data structres to reflect new status. - * - * @param c Connection. - * @param peer Peer. - */ -static void -add_to_peer (struct CadetConnection *c, - struct CadetPeer *peer) -{ - GCP_add_tunnel (peer); - c->t = GCP_get_tunnel (peer); - GCT_add_connection (c->t, c); -} - - -/** - * Log receipt of message on stderr (INFO level). - * - * @param message Message received. - * @param peer Peer who sent the message. - * @param conn_id Connection ID of the message. - */ -static void -log_message (const struct GNUNET_MessageHeader *message, - const struct CadetPeer *peer, - const struct GNUNET_CADET_ConnectionTunnelIdentifier *conn_id) -{ - uint16_t size; - uint16_t type; - char *arrow; - - size = ntohs (message->size); - type = ntohs (message->type); - switch (type) - { - case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE: - case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK: - case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN: - case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY: - arrow = "=="; - break; - default: - arrow = "--"; - } - LOG (GNUNET_ERROR_TYPE_INFO, - "<%s %s on conn %s from %s, %6u bytes\n", - arrow, - GC_m2s (type), - GNUNET_sh2s (&conn_id->connection_of_tunnel), - GCP_2s(peer), - (unsigned int) size); -} - -/******************************************************************************/ -/******************************** API ***********************************/ -/******************************************************************************/ - -/** - * Handler for connection creation. - * - * @param peer Message sender (neighbor). - * @param msg Message itself. + * @param cc the connection that got the ACK. */ void -GCC_handle_create (struct CadetPeer *peer, - const struct GNUNET_CADET_ConnectionCreateMessage *msg) +GCC_handle_connection_create_ack (struct CadetConnection *cc) { - static struct CadetEncryptedMessageIdentifier zero; - const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid; - struct GNUNET_PeerIdentity *id; - struct CadetPeerPath *path; - struct CadetPeer *dest_peer; - struct CadetPeer *orig_peer; - struct CadetConnection *c; - unsigned int own_pos; - uint16_t size; - - GCC_check_connections (); - size = ntohs (msg->header.size); - - /* Calculate hops */ - size -= sizeof (struct GNUNET_CADET_ConnectionCreateMessage); - if (0 != size % sizeof (struct GNUNET_PeerIdentity)) - { - GNUNET_break_op (0); - return; - } - size /= sizeof (struct GNUNET_PeerIdentity); - if (1 > size) - { - GNUNET_break_op (0); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " path has %u hops.\n", size); - - /* Get parameters */ - cid = &msg->cid; - log_message (&msg->header, peer, cid); - id = (struct GNUNET_PeerIdentity *) &msg[1]; - LOG (GNUNET_ERROR_TYPE_DEBUG, " origin: %s\n", GNUNET_i2s (id)); - - /* Create connection */ - c = connection_get (cid); - if (NULL == c) - { - path = path_build_from_peer_ids ((struct GNUNET_PeerIdentity *) &msg[1], - size, myid, &own_pos); - if (NULL == path) - { - /* Path was malformed, probably our own ID was not in it. */ - GNUNET_STATISTICS_update (stats, "# malformed paths", 1, GNUNET_NO); - GNUNET_break_op (0); - return; - } - if (0 == own_pos) - { - /* We received this request from a neighbor, we cannot be origin */ - GNUNET_STATISTICS_update (stats, "# fake paths", 1, GNUNET_NO); - GNUNET_break_op (0); - path_destroy (path); - return; - } - - LOG (GNUNET_ERROR_TYPE_DEBUG, " Own position: %u\n", own_pos); - LOG (GNUNET_ERROR_TYPE_DEBUG, " Creating connection\n"); - c = GCC_new (cid, NULL, path, own_pos); - if (NULL == c) - { - if (path->length - 1 == own_pos) - { - /* If we are destination, why did the creation fail? */ - GNUNET_break (0); - path_destroy (path); - GCC_check_connections (); - return; - } - send_broken_unknown (cid, &my_full_id, - GNUNET_PEER_resolve2 (path->peers[own_pos + 1]), - peer); - path_destroy (path); - GCC_check_connections (); - return; - } - GCP_add_path_to_all (path, GNUNET_NO); - connection_reset_timeout (c, GNUNET_YES); - } - else - { - path = path_duplicate (c->path); - } - if (CADET_CONNECTION_NEW == c->state) - connection_change_state (c, CADET_CONNECTION_SENT); - - /* Remember peers */ - dest_peer = GCP_get (&id[size - 1], GNUNET_YES); - orig_peer = GCP_get (&id[0], GNUNET_YES); - - /* Is it a connection to us? */ - if (c->own_pos == path->length - 1) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " It's for us!\n"); - GCP_add_path_to_origin (orig_peer, path_duplicate (path), GNUNET_YES); - - add_to_peer (c, orig_peer); - if (GNUNET_YES == does_connection_exist (c)) - { - /* Peer created a connection equal to one we think exists - * and is fine. - * Solution: Keep both and postpone disambiguation. In the meantime - * the connection will time out or peer will inform us it is broken. - * - * Other options: - * - Use explicit duplicate. - * - Accept new conn and destroy the old. (interruption in higher level) - * - Keep the one with higher ID / created by peer with higher ID. */ - schedule_check_duplicates (c); - } - - if (CADET_TUNNEL_NEW == GCT_get_cstate (c->t)) - GCT_change_cstate (c->t, CADET_TUNNEL_WAITING); - if (NULL == c->maintenance_q) - send_connection_ack (c, GNUNET_NO); - if (CADET_CONNECTION_SENT == c->state) - connection_change_state (c, CADET_CONNECTION_ACK); - } - else + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received CADET_CONNECTION_CREATE_ACK for %s in state %d (%s)\n", + GCC_2s (cc), + cc->state, + (GNUNET_YES == cc->mqm_ready) ? "MQM ready" : "MQM busy"); + if (CADET_CONNECTION_READY == cc->state) + return; /* Duplicate ACK, ignore */ + if (NULL != cc->task) { - LOG (GNUNET_ERROR_TYPE_DEBUG, " not for us, retransmitting...\n"); - GCP_add_path (dest_peer, path_duplicate (path), GNUNET_NO); - GCP_add_path_to_origin (orig_peer, path_duplicate (path), GNUNET_NO); - (void) GCC_send_prebuilt_message (&msg->header, - 0, - zero, - c, - GNUNET_YES, GNUNET_YES, - NULL, NULL); + GNUNET_SCHEDULER_cancel (cc->task); + cc->task = NULL; } - path_destroy (path); - GCC_check_connections (); + cc->metrics.age = GNUNET_TIME_absolute_get (); + update_state (cc, + CADET_CONNECTION_READY, + cc->mqm_ready); + if ( (NULL == cc->keepalive_qe) && + (GNUNET_YES == cc->mqm_ready) && + (NULL == cc->task) ) + cc->task = GNUNET_SCHEDULER_add_delayed (keepalive_period, + &send_keepalive, + cc); } /** - * Handler for connection confirmations. + * Handle KX message. * - * @param peer Message sender (neighbor). - * @param msg Message itself. + * @param cc connection that received encrypted message + * @param msg the key exchange message */ void -GCC_handle_confirm (struct CadetPeer *peer, - const struct GNUNET_CADET_ConnectionCreateAckMessage *msg) +GCC_handle_kx (struct CadetConnection *cc, + const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg) { - static struct CadetEncryptedMessageIdentifier zero; - struct CadetConnection *c; - enum CadetConnectionState oldstate; - int fwd; - - GCC_check_connections (); - log_message (&msg->header, peer, &msg->cid); - c = connection_get (&msg->cid); - if (NULL == c) - { - GNUNET_STATISTICS_update (stats, "# control on unknown connection", - 1, GNUNET_NO); - LOG (GNUNET_ERROR_TYPE_DEBUG, - " don't know the connection!\n"); - send_broken_unknown (&msg->cid, &my_full_id, NULL, peer); - GCC_check_connections (); - return; - } - if (GNUNET_NO != c->destroy) + if (CADET_CONNECTION_SENT == cc->state) { - GNUNET_assert (CADET_CONNECTION_DESTROYED == c->state); - GNUNET_STATISTICS_update (stats, "# control on dying connection", - 1, GNUNET_NO); + /* We didn't get the CADET_CONNECTION_CREATE_ACK, but instead got payload. That's fine, + clearly something is working, so pretend we got an ACK. */ LOG (GNUNET_ERROR_TYPE_DEBUG, - "connection %s being destroyed, ignoring confirm\n", - GCC_2s (c)); - GCC_check_connections (); - return; - } - - oldstate = c->state; - LOG (GNUNET_ERROR_TYPE_DEBUG, " via peer %s\n", GCP_2s (peer)); - if (get_next_hop (c) == peer) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " SYNACK\n"); - fwd = GNUNET_NO; - if (CADET_CONNECTION_SENT == oldstate) - connection_change_state (c, CADET_CONNECTION_ACK); - } - else if (get_prev_hop (c) == peer) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " FINAL ACK\n"); - fwd = GNUNET_YES; - connection_change_state (c, CADET_CONNECTION_READY); - } - else - { - GNUNET_STATISTICS_update (stats, "# control on connection from wrong peer", - 1, GNUNET_NO); - GNUNET_break_op (0); - return; - } - - connection_reset_timeout (c, fwd); - - GNUNET_assert (NULL != c->path); - GCP_add_path_to_all (c->path, GNUNET_YES); - - /* Message for us as creator? */ - if (GNUNET_YES == GCC_is_origin (c, GNUNET_YES)) - { - if (GNUNET_NO != fwd) - { - GNUNET_break (0); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " Connection (SYN)ACK for us!\n"); - - /* If just created, cancel the short timeout and start a long one */ - if (CADET_CONNECTION_SENT == oldstate) - { - c->create_retry = 1; - connection_reset_timeout (c, GNUNET_YES); - } - - /* Change connection state, send ACK */ - connection_change_state (c, CADET_CONNECTION_READY); - send_connection_ack (c, GNUNET_YES); - - /* Change tunnel state, trigger KX */ - if (CADET_TUNNEL_WAITING == GCT_get_cstate (c->t)) - GCT_change_cstate (c->t, CADET_TUNNEL_READY); - GCC_check_connections (); - return; - } - - /* Message for us as destination? */ - if (GCC_is_terminal (c, GNUNET_YES)) - { - if (GNUNET_YES != fwd) - { - GNUNET_break (0); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " Connection ACK for us!\n"); - - /* If just created, cancel the short timeout and start a long one */ - if (CADET_CONNECTION_ACK == oldstate) - connection_reset_timeout (c, GNUNET_NO); - - /* Change tunnel state */ - if (CADET_TUNNEL_WAITING == GCT_get_cstate (c->t)) - GCT_change_cstate (c->t, CADET_TUNNEL_READY); - GCC_check_connections (); + "Faking connection CADET_CONNECTION_CREATE_ACK for %s due to KX\n", + GCC_2s (cc)); + GCC_handle_connection_create_ack (cc); } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " not for us, retransmitting...\n"); - (void) GCC_send_prebuilt_message (&msg->header, 0, - zero, - c, - fwd, - GNUNET_YES, NULL, NULL); - } - GCC_check_connections (); + GCT_handle_kx (cc->ct, + msg); } /** - * Handler for notifications of broken connections. + * Handle KX_AUTH message. * - * @param peer Message sender (neighbor). - * @param msg Message itself. + * @param cc connection that received encrypted message + * @param msg the key exchange message */ void -GCC_handle_broken (struct CadetPeer *peer, - const struct GNUNET_CADET_ConnectionBrokenMessage *msg) +GCC_handle_kx_auth (struct CadetConnection *cc, + const struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg) { - static struct CadetEncryptedMessageIdentifier zero; - struct CadetConnection *c; - struct CadetTunnel *t; - int fwd; - - GCC_check_connections (); - log_message (&msg->header, peer, &msg->cid); - LOG (GNUNET_ERROR_TYPE_DEBUG, " regarding %s\n", GNUNET_i2s (&msg->peer1)); - LOG (GNUNET_ERROR_TYPE_DEBUG, " regarding %s\n", GNUNET_i2s (&msg->peer2)); - c = connection_get (&msg->cid); - if (NULL == c) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " duplicate CONNECTION_BROKEN\n"); - GNUNET_STATISTICS_update (stats, "# duplicate CONNECTION_BROKEN", - 1, GNUNET_NO); - GCC_check_connections (); - return; - } - - t = c->t; - - fwd = is_fwd (c, peer); - if (GNUNET_SYSERR == fwd) - { - GNUNET_break_op (0); - GCC_check_connections (); - return; - } - mark_destroyed (c); - if (GCC_is_terminal (c, fwd)) - { - struct CadetPeer *endpoint; - - if (NULL == t) - { - /* A terminal connection should not have 't' set to NULL. */ - GNUNET_break (0); - GCC_debug (c, GNUNET_ERROR_TYPE_ERROR); - return; - } - endpoint = GCP_get_short (c->path->peers[c->path->length - 1], GNUNET_YES); - if (2 < c->path->length) - path_invalidate (c->path); - GCP_notify_broken_link (endpoint, &msg->peer1, &msg->peer2); - - connection_change_state (c, CADET_CONNECTION_BROKEN); - GCT_remove_connection (t, c); - c->t = NULL; - - GCC_destroy (c); - } - else + if (CADET_CONNECTION_SENT == cc->state) { - (void) GCC_send_prebuilt_message (&msg->header, 0, - zero, c, fwd, - GNUNET_YES, NULL, NULL); - connection_cancel_queues (c, !fwd); + /* We didn't get the CADET_CONNECTION_CREATE_ACK, but instead got payload. That's fine, + clearly something is working, so pretend we got an ACK. */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Faking connection CADET_CONNECTION_CREATE_ACK for %s due to KX\n", + GCC_2s (cc)); + GCC_handle_connection_create_ack (cc); } - GCC_check_connections (); - return; + GCT_handle_kx_auth (cc->ct, + msg); } /** - * Handler for notifications of destroyed connections. + * Handle encrypted message. * - * @param peer Message sender (neighbor). - * @param msg Message itself. + * @param cc connection that received encrypted message + * @param msg the encrypted message to decrypt */ void -GCC_handle_destroy (struct CadetPeer *peer, - const struct GNUNET_CADET_ConnectionDestroyMessage *msg) +GCC_handle_encrypted (struct CadetConnection *cc, + const struct GNUNET_CADET_TunnelEncryptedMessage *msg) { - static struct CadetEncryptedMessageIdentifier zero; - struct CadetConnection *c; - int fwd; - - GCC_check_connections (); - log_message (&msg->header, peer, &msg->cid); - c = connection_get (&msg->cid); - if (NULL == c) + if (CADET_CONNECTION_SENT == cc->state) { - /* 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 connection", - 1, GNUNET_NO); + /* We didn't get the CREATE_ACK, but instead got payload. That's fine, + clearly something is working, so pretend we got an ACK. */ LOG (GNUNET_ERROR_TYPE_DEBUG, - " connection unknown destroyed: previously destroyed?\n"); - GCC_check_connections (); - return; - } - - fwd = is_fwd (c, peer); - if (GNUNET_SYSERR == fwd) - { - GNUNET_break_op (0); - GCC_check_connections (); - return; - } - - if (GNUNET_NO == GCC_is_terminal (c, fwd)) - { - (void) GCC_send_prebuilt_message (&msg->header, 0, - zero, c, fwd, - GNUNET_YES, NULL, NULL); + "Faking connection ACK for %s due to ENCRYPTED payload\n", + GCC_2s (cc)); + GCC_handle_connection_create_ack (cc); } - else if (0 == c->pending_messages) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " directly destroying connection!\n"); - GCC_destroy (c); - GCC_check_connections (); - return; - } - mark_destroyed (c); - if (NULL != c->t) - { - GCT_remove_connection (c->t, c); - c->t = NULL; - } - GCC_check_connections (); - return; + cc->metrics.last_use = GNUNET_TIME_absolute_get (); + GCT_handle_encrypted (cc->ct, + msg); } /** - * Handler for cadet network traffic hop-by-hop acks. + * Send a #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE message to the + * first hop. * - * @param peer Message sender (neighbor). - * @param msg Message itself. + * @param cls the `struct CadetConnection` to initiate */ -void -GCC_handle_ack (struct CadetPeer *peer, - const struct GNUNET_CADET_ConnectionEncryptedAckMessage *msg) -{ - struct CadetConnection *c; - struct CadetFlowControl *fc; - struct CadetEncryptedMessageIdentifier ack; - int fwd; - - GCC_check_connections (); - log_message (&msg->header, peer, &msg->cid); - c = connection_get (&msg->cid); - if (NULL == c) - { - GNUNET_STATISTICS_update (stats, - "# ack on unknown connection", - 1, - GNUNET_NO); - send_broken_unknown (&msg->cid, - &my_full_id, - NULL, - peer); - GCC_check_connections (); - return; - } - - /* Is this a forward or backward ACK? */ - if (get_next_hop (c) == peer) - { - fc = &c->fwd_fc; - fwd = GNUNET_YES; - } - else if (get_prev_hop (c) == peer) - { - fc = &c->bck_fc; - fwd = GNUNET_NO; - } - else - { - GNUNET_break_op (0); - return; - } - - ack = msg->cemi_max; - LOG (GNUNET_ERROR_TYPE_DEBUG, " %s ACK %u (was %u)\n", - GC_f2s (fwd), - ntohl (ack.pid), - ntohl (fc->last_ack_recv.pid)); - if (GC_is_pid_bigger (ntohl (ack.pid), - ntohl (fc->last_ack_recv.pid))) - fc->last_ack_recv = ack; - - /* Cancel polling if the ACK is big enough. */ - if ( (NULL != fc->poll_task) & - GC_is_pid_bigger (ntohl (fc->last_ack_recv.pid), - ntohl (fc->last_pid_sent.pid))) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " Cancel poll\n"); - GNUNET_SCHEDULER_cancel (fc->poll_task); - fc->poll_task = NULL; - fc->poll_time = GNUNET_TIME_UNIT_SECONDS; - } - - GCC_check_connections (); +static void +send_create (void *cls) +{ + struct CadetConnection *cc = cls; + struct GNUNET_CADET_ConnectionCreateMessage *create_msg; + struct GNUNET_PeerIdentity *pids; + struct GNUNET_MQ_Envelope *env; + unsigned int path_length; + + cc->task = NULL; + GNUNET_assert (GNUNET_YES == cc->mqm_ready); + path_length = GCPP_get_length (cc->path); + env = GNUNET_MQ_msg_extra (create_msg, + (1 + path_length) * sizeof (struct GNUNET_PeerIdentity), + GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE); + create_msg->options = htonl ((uint32_t) cc->options); + create_msg->cid = cc->cid; + pids = (struct GNUNET_PeerIdentity *) &create_msg[1]; + pids[0] = my_full_id; + for (unsigned int i=0;i<path_length;i++) + pids[i + 1] = *GCP_get_id (GCPP_get_peer_at_offset (cc->path, + i)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending CADET_CONNECTION_CREATE message for %s\n", + GCC_2s (cc)); + cc->env = env; + update_state (cc, + CADET_CONNECTION_SENT, + GNUNET_NO); + GCP_send (cc->mq_man, + env); } /** - * Handler for cadet network traffic hop-by-hop data counter polls. + * Send a CREATE_ACK message towards the origin. * - * @param peer Message sender (neighbor). - * @param msg Message itself. + * @param cls the `struct CadetConnection` to initiate */ -void -GCC_handle_poll (struct CadetPeer *peer, - const struct GNUNET_CADET_ConnectionHopByHopPollMessage *msg) +static void +send_create_ack (void *cls) { - struct CadetConnection *c; - struct CadetFlowControl *fc; - struct CadetEncryptedMessageIdentifier pid; - int fwd; - - GCC_check_connections (); - log_message (&msg->header, peer, &msg->cid); - c = connection_get (&msg->cid); - if (NULL == c) - { - GNUNET_STATISTICS_update (stats, "# poll on unknown connection", 1, - GNUNET_NO); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "POLL message on unknown connection %s!\n", - GNUNET_sh2s (&msg->cid.connection_of_tunnel)); - send_broken_unknown (&msg->cid, - &my_full_id, - NULL, - peer); - GCC_check_connections (); - return; - } - - /* Is this a forward or backward ACK? - * Note: a poll should never be needed in a loopback case, - * since there is no possiblility of packet loss there, so - * this way of discerining FWD/BCK should not be a problem. - */ - if (get_next_hop (c) == peer) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " FWD FC\n"); - fc = &c->fwd_fc; - } - else if (get_prev_hop (c) == peer) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " BCK FC\n"); - fc = &c->bck_fc; - } - else - { - GNUNET_break_op (0); - return; - } + struct CadetConnection *cc = cls; + struct GNUNET_CADET_ConnectionCreateAckMessage *ack_msg; + struct GNUNET_MQ_Envelope *env; - pid = msg->cemi; + cc->task = NULL; + GNUNET_assert (CADET_CONNECTION_CREATE_RECEIVED == cc->state); LOG (GNUNET_ERROR_TYPE_DEBUG, - " PID %u, OLD %u\n", - ntohl (pid.pid), - ntohl (fc->last_pid_recv.pid)); - fc->last_pid_recv = pid; - fwd = fc == &c->bck_fc; - GCC_send_ack (c, fwd, GNUNET_YES); - GCC_check_connections (); + "Sending CONNECTION_CREATE_ACK message for %s\n", + GCC_2s (cc)); + GNUNET_assert (GNUNET_YES == cc->mqm_ready); + env = GNUNET_MQ_msg (ack_msg, + GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK); + ack_msg->cid = cc->cid; + cc->env = env; + update_state (cc, + CADET_CONNECTION_READY, + GNUNET_NO); + GCP_send (cc->mq_man, + env); } /** - * Check the message against internal state and test if it goes FWD or BCK. - * - * Updates the PID, state and timeout values for the connection. - * - * @param message Message to check. It must belong to an existing connection. - * @param cid Connection ID (even if @a c is NULL, the ID is still needed). - * @param c Connection this message should belong. If NULL, check fails. - * @param sender Neighbor that sent the message. + * We got a #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE for a + * connection that we already have. Either our ACK got lost + * or something is fishy. Consider retransmitting the ACK. * - * @return #GNUNET_YES if the message goes FWD. - * #GNUNET_NO if it goes BCK. - * #GNUNET_SYSERR if there is an error (unauthorized sender, ...). + * @param cc connection that got the duplicate CREATE */ -static int -check_message (const struct GNUNET_MessageHeader *message, - const struct GNUNET_CADET_ConnectionTunnelIdentifier* cid, - struct CadetConnection *c, - struct CadetPeer *sender, - struct CadetEncryptedMessageIdentifier pid) +void +GCC_handle_duplicate_create (struct CadetConnection *cc) { - struct CadetFlowControl *fc; - struct CadetPeer *hop; - int fwd; - uint16_t type; - - /* Check connection */ - if (NULL == c) + if (GNUNET_YES == cc->mqm_ready) { - GNUNET_STATISTICS_update (stats, - "# unknown connection", - 1, GNUNET_NO); LOG (GNUNET_ERROR_TYPE_DEBUG, - "%s on unknown connection %s\n", - GC_m2s (ntohs (message->type)), - GNUNET_sh2s (&cid->connection_of_tunnel)); - GNUNET_break_op (0); - send_broken_unknown (cid, - &my_full_id, - NULL, - sender); - return GNUNET_SYSERR; - } - - /* Check if origin is as expected */ - hop = get_prev_hop (c); - if (sender == hop) - { - fwd = GNUNET_YES; + "Got duplicate CREATE for %s, scheduling another ACK (%s)\n", + GCC_2s (cc), + (GNUNET_YES == cc->mqm_ready) ? "MQM ready" : "MQM busy"); + /* Revert back to the state of having only received the 'CREATE', + and immediately proceed to send the CREATE_ACK. */ + update_state (cc, + CADET_CONNECTION_CREATE_RECEIVED, + cc->mqm_ready); + if (NULL != cc->task) + GNUNET_SCHEDULER_cancel (cc->task); + cc->task = GNUNET_SCHEDULER_add_now (&send_create_ack, + cc); } else { - hop = get_next_hop (c); - GNUNET_break (hop == c->next_peer); - if (sender == hop) - { - fwd = GNUNET_NO; - } - else - { - /* Unexpected peer sending traffic on a connection. */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - - /* Check PID for payload messages */ - type = ntohs (message->type); - if (GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED == type) - { - fc = fwd ? &c->bck_fc : &c->fwd_fc; - LOG (GNUNET_ERROR_TYPE_DEBUG, " PID %u (expected in interval [%u,%u])\n", - ntohl (pid.pid), - ntohl (fc->last_pid_recv.pid) + 1, - ntohl (fc->last_ack_sent.pid)); - if (GC_is_pid_bigger (ntohl (pid.pid), - ntohl (fc->last_ack_sent.pid))) - { - GNUNET_STATISTICS_update (stats, - "# unsolicited message", - 1, - GNUNET_NO); - LOG (GNUNET_ERROR_TYPE_WARNING, - "Received PID %u, (prev %u), ACK %u\n", - pid, fc->last_pid_recv, fc->last_ack_sent); - return GNUNET_SYSERR; - } - if (GC_is_pid_bigger (ntohl (pid.pid), - ntohl (fc->last_pid_recv.pid))) - { - unsigned int delta; - - delta = ntohl (pid.pid) - ntohl (fc->last_pid_recv.pid); - fc->last_pid_recv = pid; - fc->recv_bitmap <<= delta; - fc->recv_bitmap |= 1; - } - else - { - GNUNET_STATISTICS_update (stats, - "# out of order PID", - 1, - GNUNET_NO); - if (GNUNET_NO == is_ooo_ok (fc->last_pid_recv, - pid, - fc->recv_bitmap)) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - "PID %u unexpected (%u+), dropping!\n", - ntohl (pid.pid), - ntohl (fc->last_pid_recv.pid) - 31); - return GNUNET_SYSERR; - } - fc->recv_bitmap |= get_recv_bitmask (fc->last_pid_recv, - pid); - } - } - - /* Count as connection confirmation. */ - if ( (CADET_CONNECTION_SENT == c->state) || - (CADET_CONNECTION_ACK == c->state) ) - { - connection_change_state (c, CADET_CONNECTION_READY); - if (NULL != c->t) - { - if (CADET_TUNNEL_WAITING == GCT_get_cstate (c->t)) - GCT_change_cstate (c->t, CADET_TUNNEL_READY); - } + /* We are currently sending something else back, which + can only be an ACK or payload, either of which would + do. So actually no need to do anything. */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got duplicate CREATE for %s. MQ is busy, not queueing another ACK\n", + GCC_2s (cc)); } - connection_reset_timeout (c, fwd); - - return fwd; } /** - * Handler for key exchange traffic (Axolotl KX). + * There has been a change in the message queue existence for our + * peer at the first hop. Adjust accordingly. * - * @param peer Message sender (neighbor). - * @param msg Message itself. + * @param cls the `struct CadetConnection` + * @param available #GNUNET_YES if sending is now possible, + * #GNUNET_NO if sending is no longer possible + * #GNUNET_SYSERR if sending is no longer possible + * and the last envelope was discarded */ -void -GCC_handle_kx (struct CadetPeer *peer, - const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg) +static void +manage_first_hop_mq (void *cls, + int available) { - static struct CadetEncryptedMessageIdentifier zero; - const struct GNUNET_CADET_ConnectionTunnelIdentifier* cid; - struct CadetConnection *c; - int fwd; - - GCC_check_connections (); - cid = &msg->cid; - log_message (&msg->header, peer, cid); - - c = connection_get (cid); - fwd = check_message (&msg->header, - cid, - c, - peer, - zero); - - /* If something went wrong, discard message. */ - if (GNUNET_SYSERR == fwd) - { - GNUNET_break_op (0); - GCC_check_connections (); - return; - } + struct CadetConnection *cc = cls; - /* Is this message for us? */ - if (GCC_is_terminal (c, fwd)) + if (GNUNET_YES != available) { - LOG (GNUNET_ERROR_TYPE_DEBUG, " message for us!\n"); - GNUNET_STATISTICS_update (stats, "# received KX", 1, GNUNET_NO); - if (NULL == c->t) + /* Connection is down, for now... */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Core MQ for %s went down\n", + GCC_2s (cc)); + update_state (cc, + CADET_CONNECTION_NEW, + GNUNET_NO); + cc->retry_delay = GNUNET_TIME_UNIT_ZERO; + if (NULL != cc->task) { - GNUNET_break (0); - return; + GNUNET_SCHEDULER_cancel (cc->task); + cc->task = NULL; } - GCT_handle_kx (c->t, msg); - GCC_check_connections (); - return; - } - - /* Message not for us: forward to next hop */ - LOG (GNUNET_ERROR_TYPE_DEBUG, " not for us, retransmitting...\n"); - GNUNET_STATISTICS_update (stats, "# messages forwarded", 1, GNUNET_NO); - (void) GCC_send_prebuilt_message (&msg->header, 0, - zero, c, fwd, - GNUNET_NO, NULL, NULL); - GCC_check_connections (); -} - - -/** - * Handler for encrypted cadet network traffic (channel mgmt, data). - * - * @param peer Message sender (neighbor). - * @param msg Message itself. - */ -void -GCC_handle_encrypted (struct CadetPeer *peer, - const struct GNUNET_CADET_TunnelEncryptedMessage *msg) -{ - static struct CadetEncryptedMessageIdentifier zero; - const struct GNUNET_CADET_ConnectionTunnelIdentifier* cid; - struct CadetConnection *c; - struct CadetEncryptedMessageIdentifier pid; - int fwd; - - GCC_check_connections (); - cid = &msg->cid; - pid = msg->cemi; - log_message (&msg->header, peer, cid); - - c = connection_get (cid); - fwd = check_message (&msg->header, - cid, - c, - peer, - pid); - - /* If something went wrong, discard message. */ - if (GNUNET_SYSERR == fwd) - { - GCC_check_connections (); return; } - /* Is this message for us? */ - if (GCC_is_terminal (c, fwd)) - { - GNUNET_STATISTICS_update (stats, "# received encrypted", 1, GNUNET_NO); - - if (NULL == c->t) + update_state (cc, + cc->state, + GNUNET_YES); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Core MQ for %s became available in state %d\n", + GCC_2s (cc), + cc->state); + switch (cc->state) + { + case CADET_CONNECTION_NEW: + /* Transmit immediately */ + cc->task = GNUNET_SCHEDULER_add_now (&send_create, + cc); + break; + case CADET_CONNECTION_SENDING_CREATE: + /* Should not be possible to be called in this state. */ + GNUNET_assert (0); + break; + case CADET_CONNECTION_SENT: + /* Retry a bit later... */ + cc->retry_delay = GNUNET_TIME_STD_BACKOFF (cc->retry_delay); + cc->task = GNUNET_SCHEDULER_add_delayed (cc->retry_delay, + &send_create, + cc); + break; + case CADET_CONNECTION_CREATE_RECEIVED: + /* We got the 'CREATE' (incoming connection), should send the CREATE_ACK */ + cc->metrics.age = GNUNET_TIME_absolute_get (); + cc->task = GNUNET_SCHEDULER_add_now (&send_create_ack, + cc); + break; + case CADET_CONNECTION_READY: + if ( (NULL == cc->keepalive_qe) && + (GNUNET_YES == cc->mqm_ready) && + (NULL == cc->task) ) { - GNUNET_break (GNUNET_NO != c->destroy); - return; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Scheduling keepalive for %s in %s\n", + GCC_2s (cc), + GNUNET_STRINGS_relative_time_to_string (keepalive_period, + GNUNET_YES)); + cc->task = GNUNET_SCHEDULER_add_delayed (keepalive_period, + &send_keepalive, + cc); } - GCT_handle_encrypted (c->t, msg); - GCC_send_ack (c, fwd, GNUNET_NO); - GCC_check_connections (); - return; + break; } - - /* Message not for us: forward to next hop */ - LOG (GNUNET_ERROR_TYPE_DEBUG, " not for us, retransmitting...\n"); - GNUNET_STATISTICS_update (stats, "# messages forwarded", 1, GNUNET_NO); - (void) GCC_send_prebuilt_message (&msg->header, 0, - zero, c, fwd, - GNUNET_NO, NULL, NULL); - GCC_check_connections (); } /** - * Initialize the connections subsystem + * Create a connection to @a destination via @a path and notify @a cb + * whenever we are ready for more data. Shared logic independent of + * who is initiating the connection. * - * @param c Configuration handle. + * @param destination where to go + * @param path which path to take (may not be the full path) + * @param off offset of @a destination on @a path + * @param options options for the connection + * @param ct which tunnel uses this connection + * @param init_state initial state for the connection + * @param ready_cb function to call when ready to transmit + * @param ready_cb_cls closure for @a cb + * @return handle to the connection */ -void -GCC_init (const struct GNUNET_CONFIGURATION_Handle *c) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, "init\n"); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (c, "CADET", "MAX_MSGS_QUEUE", - &max_msgs_queue)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "CADET", "MAX_MSGS_QUEUE", "MISSING"); - GNUNET_SCHEDULER_shutdown (); - return; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (c, "CADET", "MAX_CONNECTIONS", - &max_connections)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "CADET", "MAX_CONNECTIONS", "MISSING"); - GNUNET_SCHEDULER_shutdown (); - return; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (c, "CADET", "REFRESH_CONNECTION_TIME", - &refresh_connection_time)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - "CADET", "REFRESH_CONNECTION_TIME", "MISSING"); - GNUNET_SCHEDULER_shutdown (); - return; - } - create_connection_time = GNUNET_TIME_relative_min (GNUNET_TIME_UNIT_SECONDS, - refresh_connection_time); - connections = GNUNET_CONTAINER_multishortmap_create (1024, - GNUNET_YES); -} - - -/** - * Destroy each connection on shutdown. - * - * @param cls Closure (unused). - * @param key Current key code (CID, unused). - * @param value Value in the hash map (`struct CadetConnection`) - * - * @return #GNUNET_YES, because we should continue to iterate - */ -static int -shutdown_iterator (void *cls, - const struct GNUNET_ShortHashCode *key, - void *value) -{ - struct CadetConnection *c = value; - - c->state = CADET_CONNECTION_DESTROYED; - GCC_destroy (c); - return GNUNET_YES; -} - - -/** - * Shut down the connections subsystem. - */ -void -GCC_shutdown (void) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down connections\n"); - GCC_check_connections (); - GNUNET_CONTAINER_multishortmap_iterate (connections, - &shutdown_iterator, - NULL); - GNUNET_CONTAINER_multishortmap_destroy (connections); - connections = NULL; -} - - -/** - * Create a connection. - * - * @param cid Connection ID (either created locally or imposed remotely). - * @param t Tunnel this connection belongs to (or NULL for transit connections); - * @param path Path this connection has to use (copy is made). - * @param own_pos Own position in the @c path path. - * - * @return Newly created connection. - * NULL in case of error: own id not in path, wrong neighbors, ... -*/ -struct CadetConnection * -GCC_new (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid, - struct CadetTunnel *t, - struct CadetPeerPath *path, - unsigned int own_pos) -{ - struct CadetConnection *c; - struct CadetPeerPath *cpath; - - GCC_check_connections (); - cpath = path_duplicate (path); - GNUNET_assert (NULL != cpath); - c = GNUNET_new (struct CadetConnection); - c->id = *cid; +static struct CadetConnection * +connection_create (struct CadetPeer *destination, + struct CadetPeerPath *path, + unsigned int off, + enum GNUNET_CADET_ChannelOption options, + struct CadetTConnection *ct, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid, + enum CadetConnectionState init_state, + GCC_ReadyCallback ready_cb, + void *ready_cb_cls) +{ + struct CadetConnection *cc; + struct CadetPeer *first_hop; + + cc = GNUNET_new (struct CadetConnection); + cc->options = options; + cc->state = init_state; + cc->ct = ct; + cc->cid = *cid; GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multishortmap_put (connections, - &c->id.connection_of_tunnel, - c, + &GCC_get_id (cc)->connection_of_tunnel, + cc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - fc_init (&c->fwd_fc); - fc_init (&c->bck_fc); - c->fwd_fc.c = c; - c->bck_fc.c = c; - - c->t = t; - GNUNET_assert (own_pos <= cpath->length - 1); - c->own_pos = own_pos; - c->path = cpath; - cpath->c = c; - if (GNUNET_OK != register_neighbors (c)) - { - if (0 == own_pos) - { - /* We were the origin of this request, this means we have invalid - * info about the paths to reach the destination. We must invalidate - * the *original* path to avoid trying it again in the next minute. - */ - if (2 < path->length) - path_invalidate (path); - else - { - GNUNET_break (0); - GCT_debug(t, GNUNET_ERROR_TYPE_WARNING); - } - c->t = NULL; - } - path_destroy (c->path); - c->path = NULL; - GCC_destroy (c); - return NULL; - } - LOG (GNUNET_ERROR_TYPE_INFO, "New connection %s\n", GCC_2s (c)); - GCC_check_connections (); - return c; -} - - -/** - * Connection is no longer needed: destroy it. - * - * Cancels all pending traffic (including possible DESTROY messages), all - * maintenance tasks and removes the connection from neighbor peers and tunnel. - * - * @param c Connection to destroy. - */ -void -GCC_destroy (struct CadetConnection *c) -{ - GCC_check_connections (); - if (NULL == c) - { - GNUNET_break (0); - return; - } - - if (2 == c->destroy) /* cancel queues -> GCP_queue_cancel -> q_destroy -> */ - return; /* -> message_sent -> GCC_destroy. Don't loop. */ - c->destroy = 2; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "destroying connection %s\n", - GCC_2s (c)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - " fc's f: %p, b: %p\n", - &c->fwd_fc, &c->bck_fc); - LOG (GNUNET_ERROR_TYPE_DEBUG, - " fc tasks f: %u, b: %u\n", - c->fwd_fc.poll_task, - c->bck_fc.poll_task); - - /* Cancel all traffic */ - if (NULL != c->path) - { - connection_cancel_queues (c, GNUNET_YES); - connection_cancel_queues (c, GNUNET_NO); - if (NULL != c->maintenance_q) - { - GCP_send_cancel (c->maintenance_q); - c->maintenance_q = NULL; - } - } - unregister_neighbors (c); - path_destroy (c->path); - c->path = NULL; - - /* Delete from tunnel */ - if (NULL != c->t) - GCT_remove_connection (c->t, c); - - if (NULL != c->check_duplicates_task) - GNUNET_SCHEDULER_cancel (c->check_duplicates_task); - if (NULL != c->fwd_maintenance_task) - GNUNET_SCHEDULER_cancel (c->fwd_maintenance_task); - if (NULL != c->bck_maintenance_task) - GNUNET_SCHEDULER_cancel (c->bck_maintenance_task); - - if (GNUNET_NO == c->was_removed) - { - GNUNET_break (GNUNET_YES == - GNUNET_CONTAINER_multishortmap_remove (connections, - &c->id.connection_of_tunnel, - c)); - } - GNUNET_STATISTICS_update (stats, - "# connections", - -1, - GNUNET_NO); - GNUNET_free (c); - GCC_check_connections (); -} - - -/** - * Get the connection ID. - * - * @param c Connection to get the ID from. - * - * @return ID of the connection. - */ -const struct GNUNET_CADET_ConnectionTunnelIdentifier * -GCC_get_id (const struct CadetConnection *c) -{ - return &c->id; -} - - -/** - * Get the connection path. - * - * @param c Connection to get the path from. - * - * @return path used by the connection. - */ -const struct CadetPeerPath * -GCC_get_path (const struct CadetConnection *c) -{ - if (GNUNET_NO == c->destroy) - return c->path; - return NULL; -} - - -/** - * Get the connection state. - * - * @param c Connection to get the state from. - * - * @return state of the connection. - */ -enum CadetConnectionState -GCC_get_state (const struct CadetConnection *c) -{ - return c->state; -} - -/** - * Get the connection tunnel. - * - * @param c Connection to get the tunnel from. - * - * @return tunnel of the connection. - */ -struct CadetTunnel * -GCC_get_tunnel (const struct CadetConnection *c) -{ - return c->t; -} - - -/** - * Get free buffer space in a connection. - * - * @param c Connection. - * @param fwd Is query about FWD traffic? - * - * @return Free buffer space [0 - max_msgs_queue/max_connections] - */ -unsigned int -GCC_get_buffer (struct CadetConnection *c, int fwd) -{ - struct CadetFlowControl *fc; - - fc = fwd ? &c->fwd_fc : &c->bck_fc; - - LOG (GNUNET_ERROR_TYPE_DEBUG, " Get %s buffer on %s: %u - %u\n", - GC_f2s (fwd), GCC_2s (c), fc->queue_max, fc->queue_n); - GCC_debug (c, GNUNET_ERROR_TYPE_DEBUG); - - return (fc->queue_max - fc->queue_n); -} - - -/** - * Get how many messages have we allowed to send to us from a direction. - * - * @param c Connection. - * @param fwd Are we asking about traffic from FWD (BCK messages)? - * - * @return last_ack_sent - last_pid_recv - */ -unsigned int -GCC_get_allowed (struct CadetConnection *c, int fwd) -{ - struct CadetFlowControl *fc; - - fc = fwd ? &c->fwd_fc : &c->bck_fc; - if ( (CADET_CONNECTION_READY != c->state) || - GC_is_pid_bigger (ntohl (fc->last_pid_recv.pid), - ntohl (fc->last_ack_sent.pid)) ) - { - return 0; - } - return (ntohl (fc->last_ack_sent.pid) - ntohl (fc->last_pid_recv.pid)); -} - - -/** - * Get messages queued in a connection. - * - * @param c Connection. - * @param fwd Is query about FWD traffic? - * - * @return Number of messages queued. - */ -unsigned int -GCC_get_qn (struct CadetConnection *c, int fwd) -{ - struct CadetFlowControl *fc; - - fc = fwd ? &c->fwd_fc : &c->bck_fc; - - return fc->queue_n; -} - - -/** - * Get next PID to use. - * - * @param c Connection. - * @param fwd Is query about FWD traffic? - * @return Next PID to use. - */ -struct CadetEncryptedMessageIdentifier -GCC_get_pid (struct CadetConnection *c, int fwd) -{ - struct CadetFlowControl *fc; - struct CadetEncryptedMessageIdentifier pid; - - fc = fwd ? &c->fwd_fc : &c->bck_fc; - pid = fc->next_pid; - fc->next_pid.pid = htonl (1 + ntohl (pid.pid)); - return pid; -} - - -/** - * Allow the connection to advertise a buffer of the given size. - * - * The connection will send an @c fwd ACK message (so: in direction !fwd) - * allowing up to last_pid_recv + buffer. - * - * @param c Connection. - * @param buffer How many more messages the connection can accept. - * @param fwd Is this about FWD traffic? (The ack will go dest->root). - */ -void -GCC_allow (struct CadetConnection *c, unsigned int buffer, int fwd) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, " allowing %s %u messages %s\n", - GCC_2s (c), buffer, GC_f2s (fwd)); - send_ack (c, buffer, fwd, GNUNET_NO); -} - - -/** - * Notify other peers on a connection of a broken link. Mark connections - * to destroy after all traffic has been sent. - * - * @param c Connection on which there has been a disconnection. - * @param peer Peer that disconnected. - */ -void -GCC_neighbor_disconnected (struct CadetConnection *c, struct CadetPeer *peer) -{ - struct CadetFlowControl *fc; - char peer_name[16]; - int fwd; - - GCC_check_connections (); - strncpy (peer_name, GCP_2s (peer), 16); - peer_name[15] = '\0'; + cc->ready_cb = ready_cb; + cc->ready_cb_cls = ready_cb_cls; + cc->path = path; + cc->off = off; LOG (GNUNET_ERROR_TYPE_DEBUG, - "shutting down %s, %s disconnected\n", - GCC_2s (c), peer_name); - - invalidate_paths (c, peer); - - fwd = is_fwd (c, peer); - if (GNUNET_SYSERR == fwd) - { - GNUNET_break (0); - return; - } - if ( (GNUNET_YES == GCC_is_terminal (c, fwd)) || - (GNUNET_NO != c->destroy) ) - { - /* Local shutdown, or other peer already down (hence 'c->destroy'); - so there is no one to notify about this, just clean up. */ - GCC_destroy (c); - GCC_check_connections (); - return; - } - /* Mark FlowControl towards the peer as unavaliable. */ - fc = fwd ? &c->bck_fc : &c->fwd_fc; - fc->queue_max = 0; - - send_broken (c, &my_full_id, GCP_get_id (peer), fwd); - - /* Connection will have at least one pending message - * (the one we just scheduled), so delay destruction - * and remove from map so we don't use accidentally. */ - mark_destroyed (c); - GNUNET_assert (GNUNET_NO == c->was_removed); - c->was_removed = GNUNET_YES; - GNUNET_break (GNUNET_YES == - GNUNET_CONTAINER_multishortmap_remove (connections, - &c->id.connection_of_tunnel, - c)); - /* Cancel queue in the direction that just died. */ - connection_cancel_queues (c, ! fwd); - GCC_stop_poll (c, ! fwd); - unregister_neighbors (c); - GCC_check_connections (); -} - - -/** - * Is this peer the first one on the connection? - * - * @param c Connection. - * @param fwd Is this about fwd traffic? - * - * @return #GNUNET_YES if origin, #GNUNET_NO if relay/terminal. + "Creating %s using path %s\n", + GCC_2s (cc), + GCPP_2s (path)); + GCPP_add_connection (path, + off, + cc); + for (unsigned int i=0;i<off;i++) + GCP_add_connection (GCPP_get_peer_at_offset (path, + i), + cc); + + first_hop = GCPP_get_peer_at_offset (path, + 0); + cc->mq_man = GCP_request_mq (first_hop, + &manage_first_hop_mq, + cc); + return cc; +} + + +/** + * Create a connection to @a destination via @a path and + * notify @a cb whenever we are ready for more data. This + * is an inbound tunnel, so we must use the existing @a cid + * + * @param destination where to go + * @param path which path to take (may not be the full path) + * @param options options for the connection + * @param ct which tunnel uses this connection + * @param ready_cb function to call when ready to transmit + * @param ready_cb_cls closure for @a cb + * @return handle to the connection, NULL if we already have + * a connection that takes precedence on @a path */ -int -GCC_is_origin (struct CadetConnection *c, int fwd) -{ - if (!fwd && c->path->length - 1 == c->own_pos ) - return GNUNET_YES; - if (fwd && 0 == c->own_pos) - return GNUNET_YES; - return GNUNET_NO; -} - - -/** - * Is this peer the last one on the connection? - * - * @param c Connection. - * @param fwd Is this about fwd traffic? - * Note that the ROOT is the terminal for BCK traffic! - * - * @return #GNUNET_YES if terminal, #GNUNET_NO if relay/origin. - */ -int -GCC_is_terminal (struct CadetConnection *c, int fwd) -{ - return GCC_is_origin (c, ! fwd); -} - - -/** - * See if we are allowed to send by the next hop in the given direction. - * - * @param c Connection. - * @param fwd Is this about fwd traffic? - * - * @return #GNUNET_YES in case it's OK to send. - */ -int -GCC_is_sendable (struct CadetConnection *c, int fwd) -{ - struct CadetFlowControl *fc; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - " checking sendability of %s traffic on %s\n", - GC_f2s (fwd), GCC_2s (c)); - if (NULL == c) - { - GNUNET_break (0); - return GNUNET_YES; - } - fc = fwd ? &c->fwd_fc : &c->bck_fc; - LOG (GNUNET_ERROR_TYPE_DEBUG, - " last ack recv: %u, last pid sent: %u\n", - ntohl (fc->last_ack_recv.pid), - ntohl (fc->last_pid_sent.pid)); - if (GC_is_pid_bigger (ntohl (fc->last_ack_recv.pid), - ntohl (fc->last_pid_sent.pid))) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " sendable\n"); - return GNUNET_YES; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " not sendable\n"); - return GNUNET_NO; -} - - -/** - * Check if this connection is a direct one (never trim a direct connection). - * - * @param c Connection. - * - * @return #GNUNET_YES in case it's a direct connection, #GNUNET_NO otherwise. - */ -int -GCC_is_direct (struct CadetConnection *c) -{ - return (c->path->length == 2) ? GNUNET_YES : GNUNET_NO; -} - - -/** - * Sends a completely built message on a connection, properly registering - * all used resources. - * - * @param message Message to send. - * @param payload_type Type of payload, in case the message is encrypted. - * 0 for restransmissions (when type is no longer known) - * UINT16_MAX when not applicable. - * @param payload_id ID of the payload (PID, ACK, ...). - * @param c Connection on which this message is transmitted. - * @param fwd Is this a fwd message? - * @param force Force the connection to accept the message (buffer overfill). - * @param cont Continuation called once message is sent. Can be NULL. - * @param cont_cls Closure for @c cont. - * - * @return Handle to cancel the message before it's sent. - * NULL on error. - * Invalid on @c cont call. - */ -struct CadetConnectionQueue * -GCC_send_prebuilt_message (const struct GNUNET_MessageHeader *message, - uint16_t payload_type, - struct CadetEncryptedMessageIdentifier payload_id, - struct CadetConnection *c, int fwd, int force, - GCC_sent cont, void *cont_cls) -{ - struct CadetFlowControl *fc; - struct CadetConnectionQueue *q; - uint16_t size; - uint16_t type; - - size = ntohs (message->size); - type = ntohs (message->type); - - GCC_check_connections (); - fc = fwd ? &c->fwd_fc : &c->bck_fc; - if (0 == fc->queue_max) - { - GNUNET_break (0); - return NULL; - } - - LOG (GNUNET_ERROR_TYPE_INFO, - "--> %s (%s %4u) on conn %s (%p) %s [%5u]\n", - GC_m2s (type), GC_m2s (payload_type), payload_id, GCC_2s (c), c, - GC_f2s(fwd), size); - switch (type) - { - case GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED: - LOG (GNUNET_ERROR_TYPE_DEBUG, " Q_N+ %p %u, PIDsnt: %u, ACKrcv: %u\n", - fc, - fc->queue_n, - ntohl (fc->last_pid_sent.pid), - ntohl (fc->last_ack_recv.pid)); - if (GNUNET_NO == force) - { - fc->queue_n++; - } - break; - - case GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX: - /* nothing to do here */ - break; - - case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE: - case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK: - /* Should've only be used for restransmissions. */ - GNUNET_break (0 == payload_type); - break; - - case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_HOP_BY_HOP_ENCRYPTED_ACK: - case GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED_POLL: - case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY: - case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN: - GNUNET_assert (GNUNET_YES == force); - break; - - default: - GNUNET_break (0); +struct CadetConnection * +GCC_create_inbound (struct CadetPeer *destination, + struct CadetPeerPath *path, + enum GNUNET_CADET_ChannelOption options, + struct CadetTConnection *ct, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid, + GCC_ReadyCallback ready_cb, + void *ready_cb_cls) +{ + struct CadetConnection *cc; + unsigned int off; + + off = GCPP_find_peer (path, + destination); + GNUNET_assert (UINT_MAX != off); + cc = GCPP_get_connection (path, + destination, + off); + if (NULL != cc) + { + int cmp; + + cmp = memcmp (cid, + &cc->cid, + sizeof (*cid)); + if (0 == cmp) + { + /* Two peers picked the SAME random connection identifier at the + same time for the same path? Must be malicious. Drop + connection (existing and inbound), even if it is the only + one. */ + GNUNET_break_op (0); + GCT_connection_lost (cc->ct); + GCC_destroy_without_tunnel (cc); return NULL; - } - - if (fc->queue_n > fc->queue_max && GNUNET_NO == force) - { - GNUNET_STATISTICS_update (stats, "# messages dropped (buffer full)", - 1, GNUNET_NO); - GNUNET_break (0); - LOG (GNUNET_ERROR_TYPE_DEBUG, "queue full: %u/%u\n", - fc->queue_n, fc->queue_max); - if (GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED == type) + } + if (0 < cmp) { - fc->queue_n--; + /* drop existing */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got two connections on %s, dropping my existing %s\n", + GCPP_2s (path), + GCC_2s (cc)); + GCT_connection_lost (cc->ct); + GCC_destroy_without_tunnel (cc); } - return NULL; /* Drop this message */ - } - - LOG (GNUNET_ERROR_TYPE_DEBUG, " C_P+ %s %u\n", - GCC_2s (c), c->pending_messages); - c->pending_messages++; - - q = GNUNET_new (struct CadetConnectionQueue); - q->cont = cont; - q->cont_cls = cont_cls; - q->forced = force; - GNUNET_CONTAINER_DLL_insert (fc->q_head, fc->q_tail, q); - q->peer_q = GCP_send (get_hop (c, fwd), - message, - payload_type, - payload_id, - c, - fwd, - &conn_message_sent, q); - if (NULL == q->peer_q) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "dropping msg on %s, NULL q\n", GCC_2s (c)); - GNUNET_CONTAINER_DLL_remove (fc->q_head, fc->q_tail, q); - GNUNET_free (q); - GCC_check_connections (); - return NULL; - } - GCC_check_connections (); - return q; -} - - -/** - * Cancel a previously sent message while it's in the queue. - * - * ONLY can be called before the continuation given to the send function - * is called. Once the continuation is called, the message is no longer in the - * queue. - * - * @param q Handle to the queue. - */ -void -GCC_cancel (struct CadetConnectionQueue *q) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, "! GCC cancel message\n"); - - /* send_cancel calls message_sent, which calls q->cont and frees q */ - GCP_send_cancel (q->peer_q); - GCC_check_connections (); -} - - -/** - * Sends a CREATE CONNECTION message for a path to a peer. - * Changes the connection and tunnel states if necessary. - * - * @param c Connection to create. - */ -void -GCC_send_create (struct CadetConnection *c) -{ - static struct CadetEncryptedMessageIdentifier zero; - enum CadetTunnelCState state; - size_t size; - - GCC_check_connections (); - size = sizeof (struct GNUNET_CADET_ConnectionCreateMessage); - size += c->path->length * sizeof (struct GNUNET_PeerIdentity); - { - /* Allocate message on the stack */ - unsigned char cbuf[size]; - struct GNUNET_CADET_ConnectionCreateMessage *msg; - struct GNUNET_PeerIdentity *peers; - - - msg = (struct GNUNET_CADET_ConnectionCreateMessage *) cbuf; - msg->header.size = htons (size); - msg->header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE); - msg->options = htonl (0); - msg->cid = *GCC_get_id (c); - peers = (struct GNUNET_PeerIdentity *) &msg[1]; - for (int i = 0; i < c->path->length; i++) + else { - GNUNET_PEER_resolve (c->path->peers[i], peers++); + /* keep existing */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got two connections on %s, keeping my existing %s\n", + GCPP_2s (path), + GCC_2s (cc)); + return NULL; } - GNUNET_assert (NULL == c->maintenance_q); - c->maintenance_q = GCP_send (get_next_hop (c), - &msg->header, - GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE, - zero, - c, GNUNET_YES, - &conn_message_sent, NULL); } - LOG (GNUNET_ERROR_TYPE_INFO, "==> %s %19s on conn %s (%p) FWD [%5u]\n", - GC_m2s (GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE), "", - GCC_2s (c), c, size); - LOG (GNUNET_ERROR_TYPE_DEBUG, " C_P+ %p %u (create)\n", - c, c->pending_messages); - c->pending_messages++; - - state = GCT_get_cstate (c->t); - if (CADET_TUNNEL_SEARCHING == state || CADET_TUNNEL_NEW == state) - GCT_change_cstate (c->t, CADET_TUNNEL_WAITING); - if (CADET_CONNECTION_NEW == c->state) - connection_change_state (c, CADET_CONNECTION_SENT); - GCC_check_connections (); + return connection_create (destination, + path, + off, + options, + ct, + cid, + CADET_CONNECTION_CREATE_RECEIVED, + ready_cb, + ready_cb_cls); } /** - * Send an ACK on the appropriate connection/channel, depending on - * the direction and the position of the peer. + * Create a connection to @a destination via @a path and + * notify @a cb whenever we are ready for more data. * - * @param c Which connection to send the hop-by-hop ACK. - * @param fwd Is this a fwd ACK? (will go dest->root). - * @param force Send the ACK even if suboptimal (e.g. requested by POLL). + * @param destination where to go + * @param path which path to take (may not be the full path) + * @param off offset of @a destination on @a path + * @param options options for the connection + * @param ct tunnel that uses the connection + * @param ready_cb function to call when ready to transmit + * @param ready_cb_cls closure for @a cb + * @return handle to the connection */ -void -GCC_send_ack (struct CadetConnection *c, int fwd, int force) +struct CadetConnection * +GCC_create (struct CadetPeer *destination, + struct CadetPeerPath *path, + unsigned int off, + enum GNUNET_CADET_ChannelOption options, + struct CadetTConnection *ct, + GCC_ReadyCallback ready_cb, + void *ready_cb_cls) { - unsigned int buffer; - - GCC_check_connections (); - LOG (GNUNET_ERROR_TYPE_DEBUG, "GCC send %s ACK on %s\n", - GC_f2s (fwd), GCC_2s (c)); - - if (NULL == c) - { - GNUNET_break (0); - return; - } + struct GNUNET_CADET_ConnectionTunnelIdentifier cid; - if (GNUNET_NO != c->destroy) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " being destroyed, why bother...\n"); - GCC_check_connections (); - return; - } - - /* Get available buffer space */ - if (GCC_is_terminal (c, fwd)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " getting from all channels\n"); - buffer = GCT_get_channels_buffer (c->t); - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " getting from one connection\n"); - buffer = GCC_get_buffer (c, fwd); - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " buffer available: %u\n", buffer); - if (0 == buffer && GNUNET_NO == force) - { - GCC_check_connections (); - return; - } - - /* Send available buffer space */ - if (GNUNET_YES == GCC_is_origin (c, fwd)) - { - GNUNET_assert (NULL != c->t); - LOG (GNUNET_ERROR_TYPE_DEBUG, " sending on channels...\n"); - GCT_unchoke_channels (c->t); - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " sending on connection\n"); - send_ack (c, buffer, fwd, force); - } - GCC_check_connections (); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &cid, + sizeof (cid)); + return connection_create (destination, + path, + off, + options, + ct, + &cid, + CADET_CONNECTION_NEW, + ready_cb, + ready_cb_cls); } /** - * Send a message to all peers in this connection that the connection - * is no longer valid. + * Transmit message @a msg via connection @a cc. Must only be called + * (once) after the connection has signalled that it is ready via the + * `ready_cb`. Clients can also use #GCC_is_ready() to check if the + * connection is right now ready for transmission. * - * If some peer should not receive the message, it should be zero'ed out - * before calling this function. - * - * @param c The connection whose peers to notify. + * @param cc connection identification + * @param env envelope with message to transmit; must NOT + * yet have a #GNUNET_MQ_notify_sent() callback attached to it */ void -GCC_send_destroy (struct CadetConnection *c) +GCC_transmit (struct CadetConnection *cc, + struct GNUNET_MQ_Envelope *env) { - static struct CadetEncryptedMessageIdentifier zero; - struct GNUNET_CADET_ConnectionDestroyMessage msg; - - if (GNUNET_YES == c->destroy) - return; - GCC_check_connections (); - msg.header.size = htons (sizeof (msg)); - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY); - msg.cid = c->id; - msg.reserved = htonl (0); LOG (GNUNET_ERROR_TYPE_DEBUG, - " sending connection destroy for connection %s\n", - GCC_2s (c)); - - if (GNUNET_NO == GCC_is_terminal (c, GNUNET_YES)) - (void) GCC_send_prebuilt_message (&msg.header, - UINT16_MAX, - zero, - c, - GNUNET_YES, GNUNET_YES, NULL, NULL); - if (GNUNET_NO == GCC_is_terminal (c, GNUNET_NO)) - (void) GCC_send_prebuilt_message (&msg.header, - UINT16_MAX, - zero, - c, - GNUNET_NO, GNUNET_YES, NULL, NULL); - mark_destroyed (c); - GCC_check_connections (); + "Scheduling message for transmission on %s\n", + GCC_2s (cc)); + GNUNET_assert (GNUNET_YES == cc->mqm_ready); + GNUNET_assert (CADET_CONNECTION_READY == cc->state); + cc->metrics.last_use = GNUNET_TIME_absolute_get (); + cc->mqm_ready = GNUNET_NO; + if (NULL != cc->task) + { + GNUNET_SCHEDULER_cancel (cc->task); + cc->task = NULL; + } + GCP_send (cc->mq_man, + env); } /** - * @brief Start a polling timer for the connection. - * - * When a neighbor does not accept more traffic on the connection it could be - * caused by a simple congestion or by a lost ACK. Polling enables to check - * for the lastest ACK status for a connection. + * Obtain the path used by this connection. * - * @param c Connection. - * @param fwd Should we poll in the FWD direction? + * @param cc connection + * @return path to @a cc */ -void -GCC_start_poll (struct CadetConnection *c, int fwd) +struct CadetPeerPath * +GCC_get_path (struct CadetConnection *cc) { - struct CadetFlowControl *fc; - - fc = fwd ? &c->fwd_fc : &c->bck_fc; - LOG (GNUNET_ERROR_TYPE_DEBUG, "POLL %s requested\n", - GC_f2s (fwd)); - if (NULL != fc->poll_task || NULL != fc->poll_msg) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " POLL already in progress (t: %p, m: %p)\n", - fc->poll_task, fc->poll_msg); - return; - } - if (0 == fc->queue_max) - { - /* Should not be needed, traffic should've been cancelled. */ - GNUNET_break (0); - LOG (GNUNET_ERROR_TYPE_DEBUG, " POLL not possible, peer disconnected\n"); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, "POLL started on request\n"); - fc->poll_task = GNUNET_SCHEDULER_add_delayed (fc->poll_time, &send_poll, fc); + return cc->path; } /** - * @brief Stop polling a connection for ACKs. + * Obtain unique ID for the connection. * - * Once we have enough ACKs for future traffic, polls are no longer necessary. - * - * @param c Connection. - * @param fwd Should we stop the poll in the FWD direction? + * @param cc connection. + * @return unique number of the connection */ -void -GCC_stop_poll (struct CadetConnection *c, int fwd) +const struct GNUNET_CADET_ConnectionTunnelIdentifier * +GCC_get_id (struct CadetConnection *cc) { - struct CadetFlowControl *fc; - - fc = fwd ? &c->fwd_fc : &c->bck_fc; - if (NULL != fc->poll_task) - { - GNUNET_SCHEDULER_cancel (fc->poll_task); - fc->poll_task = NULL; - } - if (NULL != fc->poll_msg) - { - GCC_cancel (fc->poll_msg); - fc->poll_msg = NULL; - } + return &cc->cid; } /** * Get a (static) string for a connection. * - * @param c Connection. + * @param cc Connection. */ const char * -GCC_2s (const struct CadetConnection *c) +GCC_2s (const struct CadetConnection *cc) { - if (NULL == c) - return "NULL"; + static char buf[128]; - if (NULL != c->t) - { - static char buf[128]; + if (NULL == cc) + return "Connection(NULL)"; - SPRINTF (buf, "%s (->%s)", - GNUNET_sh2s (&GCC_get_id (c)->connection_of_tunnel), - GCT_2s (c->t)); + if (NULL != cc->ct) + { + GNUNET_snprintf (buf, + sizeof (buf), + "Connection %s (%s)", + GNUNET_sh2s (&cc->cid.connection_of_tunnel), + GCT_2s (cc->ct->t)); return buf; } - return GNUNET_sh2s (&c->id.connection_of_tunnel); + GNUNET_snprintf (buf, + sizeof (buf), + "Connection %s", + GNUNET_sh2s (&cc->cid.connection_of_tunnel)); + return buf; } +#define LOG2(level, ...) GNUNET_log_from_nocheck(level,"cadet-con",__VA_ARGS__) + + /** - * Log all possible info about the connection state. + * Log connection info. * - * @param c Connection to debug. + * @param cc connection * @param level Debug level to use. */ void -GCC_debug (const struct CadetConnection *c, enum GNUNET_ErrorType level) +GCC_debug (struct CadetConnection *cc, + enum GNUNET_ErrorType level) { int do_log; - char *s; do_log = GNUNET_get_log_call_status (level & (~GNUNET_ERROR_TYPE_BULK), "cadet-con", __FILE__, __FUNCTION__, __LINE__); if (0 == do_log) return; - - if (NULL == c) + if (NULL == cc) { - LOG2 (level, "CCC DEBUG NULL CONNECTION\n"); + LOG2 (level, + "Connection (NULL)\n"); return; } - - LOG2 (level, "CCC DEBUG CONNECTION %s\n", GCC_2s (c)); - s = path_2s (c->path); - LOG2 (level, "CCC path %s, own pos: %u\n", s, c->own_pos); - GNUNET_free (s); - LOG2 (level, "CCC state: %s, destroy: %u\n", - GCC_state2s (c->state), c->destroy); - LOG2 (level, "CCC pending messages: %u\n", c->pending_messages); - if (NULL != c->perf) - LOG2 (level, "CCC us/byte: %f\n", c->perf->avg); - - LOG2 (level, "CCC FWD flow control:\n"); - LOG2 (level, "CCC queue: %u/%u\n", c->fwd_fc.queue_n, c->fwd_fc.queue_max); - LOG2 (level, "CCC last PID sent: %5u, recv: %5u\n", - ntohl (c->fwd_fc.last_pid_sent.pid), - ntohl (c->fwd_fc.last_pid_recv.pid)); - LOG2 (level, "CCC last ACK sent: %5u, recv: %5u\n", - ntohl (c->fwd_fc.last_ack_sent.pid), - ntohl (c->fwd_fc.last_ack_recv.pid)); - LOG2 (level, "CCC recv PID bitmap: %X\n", c->fwd_fc.recv_bitmap); - LOG2 (level, "CCC poll: task %d, msg %p, msg_ack %p)\n", - c->fwd_fc.poll_task, c->fwd_fc.poll_msg, c->fwd_fc.ack_msg); - - LOG2 (level, "CCC BCK flow control:\n"); - LOG2 (level, "CCC queue: %u/%u\n", c->bck_fc.queue_n, c->bck_fc.queue_max); - LOG2 (level, "CCC last PID sent: %5u, recv: %5u\n", - ntohl (c->bck_fc.last_pid_sent.pid), - ntohl (c->bck_fc.last_pid_recv.pid)); - LOG2 (level, "CCC last ACK sent: %5u, recv: %5u\n", - ntohl (c->bck_fc.last_ack_sent.pid), - ntohl (c->bck_fc.last_ack_recv.pid)); - LOG2 (level, "CCC recv PID bitmap: %X\n", c->bck_fc.recv_bitmap); - LOG2 (level, "CCC poll: task %d, msg %p, msg_ack %p)\n", - c->bck_fc.poll_task, c->bck_fc.poll_msg, c->bck_fc.ack_msg); - - LOG2 (level, "CCC DEBUG CONNECTION END\n"); + LOG2 (level, + "%s to %s via path %s in state %d is %s\n", + GCC_2s (cc), + GCP_2s (cc->destination), + GCPP_2s (cc->path), + cc->state, + (GNUNET_YES == cc->mqm_ready) ? "ready" : "busy"); } + +/* end of gnunet-service-cadet-new_connection.c */ diff --git a/src/cadet/gnunet-service-cadet_connection.h b/src/cadet/gnunet-service-cadet_connection.h index 307cb42c2b..fdb1843661 100644 --- a/src/cadet/gnunet-service-cadet_connection.h +++ b/src/cadet/gnunet-service-cadet_connection.h @@ -1,6 +1,7 @@ + /* This file is part of GNUnet. - Copyright (C) 2013 GNUnet e.V. + Copyright (C) 2001-2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -20,557 +21,319 @@ /** * @file cadet/gnunet-service-cadet_connection.h - * @brief cadet service; dealing with connections + * @brief A connection is a live end-to-end messaging mechanism + * where the peers are identified by a path and know how + * to forward along the route using a connection identifier + * for routing the data. * @author Bartlomiej Polot - * - * All functions in this file use the prefix GCC (GNUnet Cadet Connection) + * @author Christian Grothoff */ - #ifndef GNUNET_SERVICE_CADET_CONNECTION_H #define GNUNET_SERVICE_CADET_CONNECTION_H -#ifdef __cplusplus -extern "C" -{ -#if 0 /* keep Emacsens' auto-indent happy */ -} -#endif -#endif - #include "gnunet_util_lib.h" - - -/** - * All the states a connection can be in. - */ -enum CadetConnectionState -{ - /** - * Uninitialized status, should never appear in operation. - */ - CADET_CONNECTION_NEW, - - /** - * Connection create message sent, waiting for ACK. - */ - CADET_CONNECTION_SENT, - - /** - * Connection ACK sent, waiting for ACK. - */ - CADET_CONNECTION_ACK, - - /** - * Connection confirmed, ready to carry traffic. - */ - CADET_CONNECTION_READY, - - /** - * Connection to be destroyed, just waiting to empty queues. - */ - CADET_CONNECTION_DESTROYED, - - /** - * Connection to be destroyed because of a distant peer, same as DESTROYED. - */ - CADET_CONNECTION_BROKEN, -}; - - -/** - * Struct containing all information regarding a connection to a peer. - */ -struct CadetConnection; - -/** - * Handle for messages queued but not yet sent. - */ -struct CadetConnectionQueue; - -#include "cadet_path.h" -#include "gnunet-service-cadet_channel.h" +#include "gnunet-service-cadet.h" #include "gnunet-service-cadet_peer.h" +#include "cadet_protocol.h" /** - * Check invariants for all connections using #check_neighbours(). - */ -void -GCC_check_connections (void); - - -/** - * Callback called when a queued message is sent. + * Function called to notify tunnel about change in our readyness. * - * @param cls Closure. - * @param c Connection this message was on. - * @param type Type of message sent. - * @param fwd Was this a FWD going message? - * @param size Size of the message. + * @param cls closure + * @param is_ready #GNUNET_YES if the connection is now ready for transmission, + * #GNUNET_NO if the connection is no longer ready for transmission */ typedef void -(*GCC_sent) (void *cls, - struct CadetConnection *c, - struct CadetConnectionQueue *q, - uint16_t type, - int fwd, - size_t size); +(*GCC_ReadyCallback)(void *cls, + int is_ready); /** - * Handler for connection creation. + * Destroy a connection, called when the CORE layer is already done + * (i.e. has received a BROKEN message), but if we still have to + * communicate the destruction of the connection to the tunnel (if one + * exists). * - * @param peer Message sender (neighbor). - * @param msg Message itself. + * @param cc connection to destroy */ void -GCC_handle_create (struct CadetPeer *peer, - const struct GNUNET_CADET_ConnectionCreateMessage *msg); +GCC_destroy_without_core (struct CadetConnection *cc); /** - * Handler for connection confirmations. + * Destroy a connection, called if the tunnel association with the + * connection was already broken, but we still need to notify the CORE + * layer about the breakage. * - * @param peer Message sender (neighbor). - * @param msg Message itself. + * @param cc connection to destroy */ void -GCC_handle_confirm (struct CadetPeer *peer, - const struct GNUNET_CADET_ConnectionCreateAckMessage *msg); +GCC_destroy_without_tunnel (struct CadetConnection *cc); /** - * Handler for notifications of broken connections. + * Lookup a connection by its identifier. * - * @param peer Message sender (neighbor). - * @param msg Message itself. + * @param cid identifier to resolve + * @return NULL if connection was not found */ -void -GCC_handle_broken (struct CadetPeer *peer, - const struct GNUNET_CADET_ConnectionBrokenMessage *msg); +struct CadetConnection * +GCC_lookup (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid); -/** - * Handler for notifications of destroyed connections. - * - * @param peer Message sender (neighbor). - * @param msg Message itself. - */ -void -GCC_handle_destroy (struct CadetPeer *peer, - const struct GNUNET_CADET_ConnectionDestroyMessage *msg); /** - * Handler for cadet network traffic hop-by-hop acks. + * Create a connection to @a destination via @a path and + * notify @a cb whenever we are ready for more data. * - * @param peer Message sender (neighbor). - * @param msg Message itself. + * @param destination where to go + * @param path which path to take (may not be the full path) + * @param off offset of @a destination on @a path + * @param options options for the connection + * @param ct which tunnel uses this connection + * @param ready_cb function to call when ready to transmit + * @param ready_cb_cls closure for @a cb + * @return handle to the connection */ -void -GCC_handle_ack (struct CadetPeer *peer, - const struct GNUNET_CADET_ConnectionEncryptedAckMessage *msg); +struct CadetConnection * +GCC_create (struct CadetPeer *destination, + struct CadetPeerPath *path, + unsigned int off, + enum GNUNET_CADET_ChannelOption options, + struct CadetTConnection *ct, + GCC_ReadyCallback ready_cb, + void *ready_cb_cls); -/** - * Handler for cadet network traffic hop-by-hop data counter polls. - * - * @param peer Message sender (neighbor). - * @param msg Message itself. - */ -void -GCC_handle_poll (struct CadetPeer *peer, - const struct GNUNET_CADET_ConnectionHopByHopPollMessage *msg); /** - * Handler for key exchange traffic (Axolotl KX). + * Create a connection to @a destination via @a path and + * notify @a cb whenever we are ready for more data. This + * is an inbound tunnel, so we must use the existing @a cid * - * @param peer Message sender (neighbor). - * @param msg Message itself. + * @param destination where to go + * @param path which path to take (may not be the full path) + * @param options options for the connection + * @param ct which tunnel uses this connection + * @param ready_cb function to call when ready to transmit + * @param ready_cb_cls closure for @a cb + * @return handle to the connection, NULL if we already have + * a connection that takes precedence on @a path */ -void -GCC_handle_kx (struct CadetPeer *peer, - const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg); +struct CadetConnection * +GCC_create_inbound (struct CadetPeer *destination, + struct CadetPeerPath *path, + enum GNUNET_CADET_ChannelOption options, + struct CadetTConnection *ct, + const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid, + GCC_ReadyCallback ready_cb, + void *ready_cb_cls); + /** - * Handler for encrypted cadet network traffic (channel mgmt, data). + * Transmit message @a msg via connection @a cc. Must only be called + * (once) after the connection has signalled that it is ready via the + * `ready_cb`. Clients can also use #GCC_is_ready() to check if the + * connection is right now ready for transmission. * - * @param peer Message sender (neighbor). - * @param msg Message itself. + * @param cc connection identification + * @param env envelope with message to transmit; + * the #GNUNET_MQ_notify_send() must not have yet been used + * for the envelope. Also, the message better match the + * connection identifier of this connection... */ void -GCC_handle_encrypted (struct CadetPeer *peer, - const struct GNUNET_CADET_TunnelEncryptedMessage *msg); +GCC_transmit (struct CadetConnection *cc, + struct GNUNET_MQ_Envelope *env); -/** - * Core handler for axolotl key exchange traffic. - * - * @param cls Closure (unused). - * @param message Message received. - * @param peer Neighbor who sent the message. - * - * @return GNUNET_OK, to keep the connection open. - */ -int -GCC_handle_ax_kx (void *cls, const struct GNUNET_PeerIdentity *peer, - const struct GNUNET_MessageHeader *message); /** - * Core handler for axolotl encrypted cadet network traffic. + * A CREATE_ACK was received for this connection, process it. * - * @param cls Closure (unused). - * @param message Message received. - * @param peer Neighbor who sent the message. - * - * @return GNUNET_OK, to keep the connection open. + * @param cc the connection that got the ACK. */ -int -GCC_handle_ax (void *cls, const struct GNUNET_PeerIdentity *peer, - struct GNUNET_MessageHeader *message); +void +GCC_handle_connection_create_ack (struct CadetConnection *cc); -/** - * Core handler for cadet keepalives. - * - * @param cls closure - * @param message message - * @param peer peer identity this notification is about - * @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. - */ -int -GCC_handle_keepalive (void *cls, const struct GNUNET_PeerIdentity *peer, - const struct GNUNET_MessageHeader *message); /** - * Send an ACK on the appropriate connection/channel, depending on - * the direction and the position of the peer. + * We got a #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE for a + * connection that we already have. Either our ACK got lost + * or something is fishy. Consider retransmitting the ACK. * - * @param c Which connection to send the hop-by-hop ACK. - * @param fwd Is this a fwd ACK? (will go dest->root). - * @param force Send the ACK even if suboptimal (e.g. requested by POLL). + * @param cc connection that got the duplicate CREATE */ void -GCC_send_ack (struct CadetConnection *c, int fwd, int force); +GCC_handle_duplicate_create (struct CadetConnection *cc); -/** - * Initialize the connections subsystem - * - * @param c Configuration handle. - */ -void -GCC_init (const struct GNUNET_CONFIGURATION_Handle *c); /** - * Shut down the connections subsystem. + * Handle KX message. + * + * @param cc connection that received encrypted message + * @param msg the key exchange message */ void -GCC_shutdown (void); +GCC_handle_kx (struct CadetConnection *cc, + const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg); -/** - * Create a connection. - * - * @param cid Connection ID (either created locally or imposed remotely). - * @param t Tunnel this connection belongs to (or NULL for transit connections); - * @param path Path this connection has to use (copy is made). - * @param own_pos Own position in the @c path path. - * - * @return Newly created connection. - * NULL in case of error: own id not in path, wrong neighbors, ... - */ -struct CadetConnection * -GCC_new (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid, - struct CadetTunnel *t, - struct CadetPeerPath *path, - unsigned int own_pos); /** - * Connection is no longer needed: destroy it. - * - * Cancels all pending traffic (including possible DESTROY messages), all - * maintenance tasks and removes the connection from neighbor peers and tunnel. + * Handle KX_AUTH message. * - * @param c Connection to destroy. + * @param cc connection that received encrypted message + * @param msg the key exchange message */ void -GCC_destroy (struct CadetConnection *c); - -/** - * Get the connection ID. - * - * @param c Connection to get the ID from. - * - * @return ID of the connection. - */ -const struct GNUNET_CADET_ConnectionTunnelIdentifier * -GCC_get_id (const struct CadetConnection *c); +GCC_handle_kx_auth (struct CadetConnection *cc, + const struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg); /** - * Get the connection path. - * - * @param c Connection to get the path from. - * - * @return path used by the connection. + * Performance metrics for a connection. */ -const struct CadetPeerPath * -GCC_get_path (const struct CadetConnection *c); +struct CadetConnectionMetrics +{ -/** - * Get the connection state. - * - * @param c Connection to get the state from. - * - * @return state of the connection. - */ -enum CadetConnectionState -GCC_get_state (const struct CadetConnection *c); + /** + * Our current best estimate of the latency, based on a weighted + * average of at least @a latency_datapoints values. + */ + struct GNUNET_TIME_Relative aged_latency; -/** - * Get the connection tunnel. - * - * @param c Connection to get the tunnel from. - * - * @return tunnel of the connection. - */ -struct CadetTunnel * -GCC_get_tunnel (const struct CadetConnection *c); + /** + * When was this connection first established? (by us sending or + * receiving the CREATE_ACK for the first time) + */ + struct GNUNET_TIME_Absolute age; -/** - * Get free buffer space in a connection. - * - * @param c Connection. - * @param fwd Is query about FWD traffic? - * - * @return Free buffer space [0 - max_msgs_queue/max_connections] - */ -unsigned int -GCC_get_buffer (struct CadetConnection *c, int fwd); + /** + * When was this connection last used? (by us sending or + * receiving a PAYLOAD message on it) + */ + struct GNUNET_TIME_Absolute last_use; -/** - * Get how many messages have we allowed to send to us from a direction. - * - * @param c Connection. - * @param fwd Are we asking about traffic from FWD (BCK messages)? - * - * @return last_ack_sent - last_pid_recv - */ -unsigned int -GCC_get_allowed (struct CadetConnection *c, int fwd); + /** + * How many packets that ought to generate an ACK did we send via + * this connection? + */ + unsigned long long num_acked_transmissions; -/** - * Get messages queued in a connection. - * - * @param c Connection. - * @param fwd Is query about FWD traffic? - * - * @return Number of messages queued. - */ -unsigned int -GCC_get_qn (struct CadetConnection *c, int fwd); + /** + * Number of packets that were sent via this connection did actually + * receive an ACK? (Note: ACKs may be transmitted and lost via + * other connections, so this value should only be interpreted + * relative to @e num_acked_transmissions and in relation to other + * connections.) + */ + unsigned long long num_successes; -/** - * Get next PID to use. - * - * @param c Connection. - * @param fwd Is query about FWD traffic? - * @return Next PID to use. - */ -struct CadetEncryptedMessageIdentifier -GCC_get_pid (struct CadetConnection *c, int fwd); +}; -/** - * Allow the connection to advertise a buffer of the given size. - * - * The connection will send an @c fwd ACK message (so: in direction !fwd) - * allowing up to last_pid_recv + buffer. - * - * @param c Connection. - * @param buffer How many more messages the connection can accept. - * @param fwd Is this about FWD traffic? (The ack will go dest->root). - */ -void -GCC_allow (struct CadetConnection *c, unsigned int buffer, int fwd); /** - * Send FWD keepalive packets for a connection. + * Obtain performance @a metrics from @a cc. * - * @param cls Closure (connection for which to send the keepalive). - * @param tc Notification context. + * @param cc connection to query + * @return the metrics */ -void -GCC_fwd_keepalive (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); +const struct CadetConnectionMetrics * +GCC_get_metrics (struct CadetConnection *cc); + /** - * Send BCK keepalive packets for a connection. + * Handle encrypted message. * - * @param cls Closure (connection for which to send the keepalive). - * @param tc Notification context. + * @param cc connection that received encrypted message + * @param msg the encrypted message to decrypt */ void -GCC_bck_keepalive (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); +GCC_handle_encrypted (struct CadetConnection *cc, + const struct GNUNET_CADET_TunnelEncryptedMessage *msg); /** - * Notify other peers on a connection of a broken link. Mark connections - * to destroy after all traffic has been sent. + * We sent a message for which we expect to receive an ACK via + * the connection identified by @a cti. * - * @param c Connection on which there has been a disconnection. - * @param peer Peer that disconnected. + * @param cid connection identifier where we expect an ACK */ void -GCC_neighbor_disconnected (struct CadetConnection *c, struct CadetPeer *peer); +GCC_ack_expected (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid); -/** - * Is this peer the first one on the connection? - * - * @param c Connection. - * @param fwd Is this about fwd traffic? - * - * @return #GNUNET_YES if origin, #GNUNET_NO if relay/terminal. - */ -int -GCC_is_origin (struct CadetConnection *c, int fwd); /** - * Is this peer the last one on the connection? - * - * @param c Connection. - * @param fwd Is this about fwd traffic? - * Note that the ROOT is the terminal for BCK traffic! + * We observed an ACK for a message that was originally sent via + * the connection identified by @a cti. * - * @return #GNUNET_YES if terminal, #GNUNET_NO if relay/origin. + * @param cid connection identifier where we got an ACK for a message + * that was originally sent via this connection (the ACK + * may have gotten back to us via a different connection). */ -int -GCC_is_terminal (struct CadetConnection *c, int fwd); - -/** - * See if we are allowed to send by the next hop in the given direction. - * - * @param c Connection. - * @param fwd Is this about fwd traffic? - * - * @return #GNUNET_YES in case it's OK to send. - */ -int -GCC_is_sendable (struct CadetConnection *c, int fwd); +void +GCC_ack_observed (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid); -/** - * Check if this connection is a direct one (never trim a direct connection). - * - * @param c Connection. - * - * @return #GNUNET_YES in case it's a direct connection, #GNUNET_NO otherwise. - */ -int -GCC_is_direct (struct CadetConnection *c); /** - * Cancel a previously sent message while it's in the queue. + * We observed some the given @a latency on the connection + * identified by @a cti. (The same connection was taken + * in both directions.) * - * ONLY can be called before the continuation given to the send function - * is called. Once the continuation is called, the message is no longer in the - * queue. - * - * @param q Handle to the queue. + * @param cti connection identifier where we measured latency + * @param latency the observed latency */ void -GCC_cancel (struct CadetConnectionQueue *q); +GCC_latency_observed (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cti, + struct GNUNET_TIME_Relative latency); -/** - * Sends an already built message on a connection, properly registering - * all used resources. - * - * @param message Message to send. - * @param payload_type Type of payload, in case the message is encrypted. - * 0 for restransmissions (when type is no longer known) - * UINT16_MAX when not applicable. - * @param payload_id ID of the payload (PID, ACK, ...). - * @param c Connection on which this message is transmitted. - * @param fwd Is this a fwd message? - * @param force Force the connection to accept the message (buffer overfill). - * @param cont Continuation called once message is sent. Can be NULL. - * @param cont_cls Closure for @c cont. - * - * @return Handle to cancel the message before it's sent. - * NULL on error. - * Invalid on @c cont call. - */ -struct CadetConnectionQueue * -GCC_send_prebuilt_message (const struct GNUNET_MessageHeader *message, - uint16_t payload_type, - struct CadetEncryptedMessageIdentifier payload_id, - struct CadetConnection *c, int fwd, int force, - GCC_sent cont, void *cont_cls); /** - * Sends a CREATE CONNECTION message for a path to a peer. - * Changes the connection and tunnel states if necessary. + * Return the tunnel associated with this connection. * - * @param connection Connection to create. + * @param cc connection to query + * @return corresponding entry in the tunnel's connection list */ -void -GCC_send_create (struct CadetConnection *connection); +struct CadetTConnection * +GCC_get_ct (struct CadetConnection *cc); -/** - * Send a message to all peers in this connection that the connection - * is no longer valid. - * - * If some peer should not receive the message, it should be zero'ed out - * before calling this function. - * - * @param c The connection whose peers to notify. - */ -void -GCC_send_destroy (struct CadetConnection *c); /** - * @brief Start a polling timer for the connection. + * Obtain the path used by this connection. * - * When a neighbor does not accept more traffic on the connection it could be - * caused by a simple congestion or by a lost ACK. Polling enables to check - * for the lastest ACK status for a connection. - * - * @param c Connection. - * @param fwd Should we poll in the FWD direction? + * @param cc connection + * @return path to @a cc */ -void -GCC_start_poll (struct CadetConnection *c, int fwd); +struct CadetPeerPath * +GCC_get_path (struct CadetConnection *cc); /** - * @brief Stop polling a connection for ACKs. - * - * Once we have enough ACKs for future traffic, polls are no longer necessary. + * Obtain unique ID for the connection. * - * @param c Connection. - * @param fwd Should we stop the poll in the FWD direction? + * @param cc connection. + * @return unique number of the connection */ -void -GCC_stop_poll (struct CadetConnection *c, int fwd); +const struct GNUNET_CADET_ConnectionTunnelIdentifier * +GCC_get_id (struct CadetConnection *cc); + /** * Get a (static) string for a connection. * - * @param c Connection. + * @param cc Connection. */ const char * -GCC_2s (const struct CadetConnection *c); +GCC_2s (const struct CadetConnection *cc); + /** - * Log all possible info about the connection state. + * Log connection info. * - * @param c Connection to debug. + * @param cc connection * @param level Debug level to use. */ void -GCC_debug (const struct CadetConnection *c, enum GNUNET_ErrorType level); +GCC_debug (struct CadetConnection *cc, + enum GNUNET_ErrorType level); -#if 0 /* keep Emacsens' auto-indent happy */ -{ -#endif -#ifdef __cplusplus -} -#endif -/* ifndef GNUNET_SERVICE_CADET_CONNECTION_H */ #endif -/* end of gnunet-service-cadet_connection.h */ diff --git a/src/cadet/gnunet-service-cadet-new_core.c b/src/cadet/gnunet-service-cadet_core.c index 3768c36a56..ae03b4f355 100644 --- a/src/cadet/gnunet-service-cadet-new_core.c +++ b/src/cadet/gnunet-service-cadet_core.c @@ -30,11 +30,11 @@ * - Optimization: given BROKEN messages, destroy paths (?) */ #include "platform.h" -#include "gnunet-service-cadet-new_core.h" -#include "gnunet-service-cadet-new_paths.h" -#include "gnunet-service-cadet-new_peer.h" -#include "gnunet-service-cadet-new_connection.h" -#include "gnunet-service-cadet-new_tunnels.h" +#include "gnunet-service-cadet_core.h" +#include "gnunet-service-cadet_paths.h" +#include "gnunet-service-cadet_peer.h" +#include "gnunet-service-cadet_connection.h" +#include "gnunet-service-cadet_tunnels.h" #include "gnunet_core_service.h" #include "gnunet_statistics_service.h" #include "cadet_protocol.h" diff --git a/src/cadet/gnunet-service-cadet-new_core.h b/src/cadet/gnunet-service-cadet_core.h index 65b0a6ba5f..65b0a6ba5f 100644 --- a/src/cadet/gnunet-service-cadet-new_core.h +++ b/src/cadet/gnunet-service-cadet_core.h diff --git a/src/cadet/gnunet-service-cadet_dht.c b/src/cadet/gnunet-service-cadet_dht.c index 22673b1670..f00c0caf3b 100644 --- a/src/cadet/gnunet-service-cadet_dht.c +++ b/src/cadet/gnunet-service-cadet_dht.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2013 GNUnet e.V. + Copyright (C) 2013, 2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -17,25 +17,41 @@ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - +/** + * @file cadet/gnunet-service-cadet_dht.c + * @brief Information we track per peer. + * @author Bartlomiej Polot + * @author Christian Grothoff + */ #include "platform.h" #include "gnunet_util_lib.h" - #include "gnunet_dht_service.h" #include "gnunet_statistics_service.h" - -#include "cadet_path.h" +#include "gnunet-service-cadet.h" #include "gnunet-service-cadet_dht.h" -#include "gnunet-service-cadet_peer.h" #include "gnunet-service-cadet_hello.h" +#include "gnunet-service-cadet_peer.h" +#include "gnunet-service-cadet_paths.h" -#define LOG(level, ...) GNUNET_log_from (level,"cadet-dht",__VA_ARGS__) +/** + * How long do we wait before first announcing our presence to the DHT. + * Used to wait for our HELLO to be available. Note that we also get + * notifications when our HELLO is ready, so this is just the maximum + * we wait for the first notification. + */ +#define STARTUP_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 500) +/** + * How long do we wait after we get an updated HELLO before publishing? + * Allows for the HELLO to be updated again quickly, for example in + * case multiple addresses changed and we got a partial update. + */ +#define CHANGE_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100) + + +#define LOG(level, ...) GNUNET_log_from (level,"cadet-dht",__VA_ARGS__) -/******************************************************************************/ -/******************************** STRUCTS **********************************/ -/******************************************************************************/ /** * Handle for DHT searches. @@ -47,42 +63,9 @@ struct GCD_search_handle */ struct GNUNET_DHT_GetHandle *dhtget; - /** - * Provided callback to call when a path is found. - */ - GCD_search_callback callback; - - /** - * Provided closure. - */ - void *cls; - - /** - * Peer ID searched for - */ - GNUNET_PEER_Id peer_id; }; -/******************************************************************************/ -/******************************* GLOBALS ***********************************/ -/******************************************************************************/ - -/** - * Global handle to the statistics service. - */ -extern struct GNUNET_STATISTICS_Handle *stats; - -/** - * Own ID (short value). - */ -extern GNUNET_PEER_Id myid; - -/** - * Own ID (full value). - */ -extern struct GNUNET_PeerIdentity my_full_id; - /** * Handle to use DHT. */ @@ -94,69 +77,20 @@ static struct GNUNET_DHT_Handle *dht_handle; static struct GNUNET_TIME_Relative id_announce_time; /** - * DHT replication level, see DHT API: GNUNET_DHT_get_start, GNUNET_DHT_put. + * DHT replication level, see DHT API: #GNUNET_DHT_get_start(), #GNUNET_DHT_put(). */ static unsigned long long dht_replication_level; /** * Task to periodically announce itself in the network. */ -static struct GNUNET_SCHEDULER_Task * announce_id_task; +static struct GNUNET_SCHEDULER_Task *announce_id_task; /** * Delay for the next ID announce. */ static struct GNUNET_TIME_Relative announce_delay; -/** - * GET requests to stop on shutdown. - */ -static struct GNUNET_CONTAINER_MultiHashMap32 *get_requests; - -/******************************************************************************/ -/******************************** STATIC ***********************************/ -/******************************************************************************/ - - -/** - * 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 - * - * FIXME refactor and use build_path_from_peer_ids - */ -static struct CadetPeerPath * -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) -{ - size_t size = get_path_length + put_path_length + 1; - struct GNUNET_PeerIdentity peers[size]; - const struct GNUNET_PeerIdentity *peer; - struct CadetPeerPath *p; - unsigned int own_pos; - int i; - - peers[0] = my_full_id; - LOG (GNUNET_ERROR_TYPE_DEBUG, " GET has %d hops.\n", get_path_length); - for (i = 0 ; i < get_path_length; i++) - { - peer = &get_path[get_path_length - i - 1]; - LOG (GNUNET_ERROR_TYPE_DEBUG, " From GET: %s\n", GNUNET_i2s (peer)); - peers[i + 1] = *peer; - } - for (i = 0 ; i < put_path_length; i++) - { - peer = &put_path[put_path_length - i - 1]; - LOG (GNUNET_ERROR_TYPE_DEBUG, " From PUT: %s\n", GNUNET_i2s (peer)); - peers[i + get_path_length + 1] = *peer; - } - p = path_build_from_peer_ids (peers, size, myid, &own_pos); - return p; -} - /** * Function to process paths received for a new peer addition. The recorded @@ -176,42 +110,34 @@ path_build_from_dht (const struct GNUNET_PeerIdentity *get_path, */ static void dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp, - const struct 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) + unsigned int put_path_length, + enum GNUNET_BLOCK_Type type, + size_t size, + const void *data) { - struct GCD_search_handle *h = cls; - struct GNUNET_HELLO_Message *hello; - struct CadetPeerPath *p; + const struct GNUNET_HELLO_Message *hello = data; struct CadetPeer *peer; - char *s; - p = path_build_from_dht (get_path, get_path_length, - put_path, put_path_length); - if (NULL == p) + GCPP_try_path_from_dht (get_path, + get_path_length, + put_path, + put_path_length); + if ( (size >= sizeof (struct GNUNET_HELLO_Message)) && + (ntohs (hello->header.size) == size) && + (size == GNUNET_HELLO_size (hello)) ) { - GNUNET_break_op (0); - return; + peer = GCP_get (&put_path[0], + GNUNET_YES); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got HELLO for %s\n", + GCP_2s (peer)); + GCP_set_hello (peer, + hello); } - - s = path_2s (p); - LOG (GNUNET_ERROR_TYPE_INFO, - "Got path from DHT: %s\n", - s); - GNUNET_free_non_null (s); - - peer = GCP_get_short (p->peers[p->length - 1], GNUNET_YES); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got HELLO for %s\n", - GCP_2s (peer)); - h->callback (h->cls, p); - path_destroy (p); - hello = (struct GNUNET_HELLO_Message *) data; - GCP_set_hello (peer, hello); - GCP_try_connect (peer); } @@ -229,19 +155,10 @@ announce_id (void *cls) struct GNUNET_TIME_Absolute expiration; struct GNUNET_TIME_Relative next_put; - announce_id_task = NULL; - LOG (GNUNET_ERROR_TYPE_DEBUG, "Announce ID\n"); hello = GCH_get_mine (); size = (NULL != hello) ? GNUNET_HELLO_size (hello) : 0; - if ( (NULL == hello) || (0 == size) ) + if (0 == size) { - /* Peerinfo gave us no hello yet, try again soon. */ - LOG (GNUNET_ERROR_TYPE_INFO, - " no hello, waiting!\n"); - GNUNET_STATISTICS_update (stats, - "# DHT announce skipped (no hello)", - 1, - GNUNET_NO); expiration = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), announce_delay); announce_delay = GNUNET_TIME_STD_BACKOFF (announce_delay); @@ -252,71 +169,64 @@ announce_id (void *cls) announce_delay = GNUNET_TIME_UNIT_SECONDS; } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Hello %p size: %u\n", - hello, - size); - if (NULL != hello) - { - GNUNET_STATISTICS_update (stats, - "# DHT announce", - 1, GNUNET_NO); - memset (&phash, - 0, - sizeof (phash)); - GNUNET_memcpy (&phash, - &my_full_id, - sizeof (my_full_id)); - GNUNET_DHT_put (dht_handle, /* DHT handle */ - &phash, /* Key to use */ - dht_replication_level, /* Replication level */ - GNUNET_DHT_RO_RECORD_ROUTE - | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, /* DHT options */ - GNUNET_BLOCK_TYPE_DHT_HELLO, /* Block type */ - size, /* Size of the data */ - (const char *) hello, /* Data itself */ - expiration, /* Data expiration */ - NULL, /* Continuation */ - NULL); /* Continuation closure */ - } /* Call again in id_announce_time, unless HELLO expires first, * but wait at least 1s. */ - next_put = GNUNET_TIME_absolute_get_remaining (expiration); - next_put = GNUNET_TIME_relative_min (next_put, - id_announce_time); - next_put = GNUNET_TIME_relative_max (next_put, - GNUNET_TIME_UNIT_SECONDS); - announce_id_task = GNUNET_SCHEDULER_add_delayed (next_put, - &announce_id, - cls); + next_put + = GNUNET_TIME_absolute_get_remaining (expiration); + next_put + = GNUNET_TIME_relative_min (next_put, + id_announce_time); + next_put + = GNUNET_TIME_relative_max (next_put, + GNUNET_TIME_UNIT_SECONDS); + announce_id_task + = GNUNET_SCHEDULER_add_delayed (next_put, + &announce_id, + cls); + GNUNET_STATISTICS_update (stats, + "# DHT announce", + 1, + GNUNET_NO); + memset (&phash, + 0, + sizeof (phash)); + GNUNET_memcpy (&phash, + &my_full_id, + sizeof (my_full_id)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Announcing my HELLO (%u bytes) in the DHT\n", + size); + GNUNET_DHT_put (dht_handle, /* DHT handle */ + &phash, /* Key to use */ + dht_replication_level, /* Replication level */ + GNUNET_DHT_RO_RECORD_ROUTE + | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, /* DHT options */ + GNUNET_BLOCK_TYPE_DHT_HELLO, /* Block type */ + size, /* Size of the data */ + (const char *) hello, /* Data itself */ + expiration, /* Data expiration */ + NULL, /* Continuation */ + NULL); /* Continuation closure */ } + /** - * Iterator over hash map entries and stop GET requests before disconnecting - * from the DHT. - * - * @param cls Closure (unused) - * @param key Current peer ID. - * @param value Value in the hash map (GCD_search_handle). - * - * @return #GNUNET_YES, we should continue to iterate, + * Function called by the HELLO subsystem whenever OUR hello + * changes. Re-triggers the DHT PUT immediately. */ -int -stop_get (void *cls, - uint32_t key, - void *value) +void +GCD_hello_update () { - struct GCD_search_handle *h = value; - - GCD_search_stop (h); - return GNUNET_YES; + if (NULL == announce_id_task) + return; /* too early */ + GNUNET_SCHEDULER_cancel (announce_id_task); + announce_id_task + = GNUNET_SCHEDULER_add_delayed (CHANGE_DELAY, + &announce_id, + NULL); } -/******************************************************************************/ -/******************************** API ***********************************/ -/******************************************************************************/ - /** * Initialize the DHT subsystem. * @@ -325,36 +235,40 @@ stop_get (void *cls, void GCD_init (const struct GNUNET_CONFIGURATION_Handle *c) { - LOG (GNUNET_ERROR_TYPE_DEBUG, "init\n"); if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (c, "CADET", + GNUNET_CONFIGURATION_get_value_number (c, + "CADET", "DHT_REPLICATION_LEVEL", &dht_replication_level)) { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, "CADET", - "DHT_REPLICATION_LEVEL", "USING DEFAULT"); + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, + "CADET", + "DHT_REPLICATION_LEVEL", + "USING DEFAULT"); dht_replication_level = 3; } if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (c, "CADET", "ID_ANNOUNCE_TIME", + GNUNET_CONFIGURATION_get_value_time (c, + "CADET", + "ID_ANNOUNCE_TIME", &id_announce_time)) { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, "CADET", - "ID_ANNOUNCE_TIME", "MISSING"); + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + "CADET", + "ID_ANNOUNCE_TIME", + "MISSING"); GNUNET_SCHEDULER_shutdown (); return; } - dht_handle = GNUNET_DHT_connect (c, 64); - if (NULL == dht_handle) - { - GNUNET_break (0); - } - + dht_handle = GNUNET_DHT_connect (c, + 64); + GNUNET_break (NULL != dht_handle); announce_delay = GNUNET_TIME_UNIT_SECONDS; - announce_id_task = GNUNET_SCHEDULER_add_now (&announce_id, NULL); - get_requests = GNUNET_CONTAINER_multihashmap32_create (32); + announce_id_task = GNUNET_SCHEDULER_add_delayed (STARTUP_DELAY, + &announce_id, + NULL); } @@ -364,10 +278,7 @@ GCD_init (const struct GNUNET_CONFIGURATION_Handle *c) void GCD_shutdown (void) { - LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down DHT\n"); - GNUNET_CONTAINER_multihashmap32_iterate (get_requests, &stop_get, NULL); - GNUNET_CONTAINER_multihashmap32_destroy (get_requests); - if (dht_handle != NULL) + if (NULL != dht_handle) { GNUNET_DHT_disconnect (dht_handle); dht_handle = NULL; @@ -379,22 +290,31 @@ GCD_shutdown (void) } } + +/** + * Search DHT for paths to @a peeR_id + * + * @param peer_id peer to search for + * @return handle to abort search + */ struct GCD_search_handle * -GCD_search (const struct GNUNET_PeerIdentity *peer_id, - GCD_search_callback callback, void *cls) +GCD_search (const struct GNUNET_PeerIdentity *peer_id) { struct GNUNET_HashCode phash; struct GCD_search_handle *h; - LOG (GNUNET_ERROR_TYPE_DEBUG, "Starting DHT GET for peer %s\n", - GNUNET_i2s (peer_id)); - GNUNET_STATISTICS_update (stats, "# DHT search", 1, GNUNET_NO); - memset (&phash, 0, sizeof (phash)); - GNUNET_memcpy (&phash, peer_id, sizeof (*peer_id)); + GNUNET_STATISTICS_update (stats, + "# DHT search", + 1, + GNUNET_NO); + memset (&phash, + 0, + sizeof (phash)); + GNUNET_memcpy (&phash, + peer_id, + sizeof (*peer_id)); + h = GNUNET_new (struct GCD_search_handle); - h->peer_id = GNUNET_PEER_intern (peer_id); - h->callback = callback; - h->cls = cls; h->dhtget = GNUNET_DHT_get_start (dht_handle, /* handle */ GNUNET_BLOCK_TYPE_DHT_HELLO, /* type */ &phash, /* key to search */ @@ -405,20 +325,27 @@ GCD_search (const struct GNUNET_PeerIdentity *peer_id, 0, /* xquery bits */ &dht_get_id_handler, h); - GNUNET_CONTAINER_multihashmap32_put (get_requests, - h->peer_id, - h, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Starting DHT GET for peer %s (%p)\n", + GNUNET_i2s (peer_id), + h); return h; } +/** + * Stop DHT search started with #GCD_search(). + * + * @param h handle to search to stop + */ void GCD_search_stop (struct GCD_search_handle *h) { - GNUNET_break (GNUNET_OK == - GNUNET_CONTAINER_multihashmap32_remove (get_requests, - h->peer_id, h)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Stopping DHT GET %p\n", + h); GNUNET_DHT_get_stop (h->dhtget); GNUNET_free (h); } + +/* end of gnunet-service-cadet_dht.c */ diff --git a/src/cadet/gnunet-service-cadet_dht.h b/src/cadet/gnunet-service-cadet_dht.h index b70dfe975f..5d7ab29a06 100644 --- a/src/cadet/gnunet-service-cadet_dht.h +++ b/src/cadet/gnunet-service-cadet_dht.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2013 GNUnet e.V. + Copyright (C) 2013, 2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -22,10 +22,10 @@ * @file cadet/gnunet-service-cadet_dht.h * @brief cadet service; dealing with DHT requests and results * @author Bartlomiej Polot + * @author Christian Grothoff * - * All functions in this file should use the prefix GMD (Gnunet Cadet Dht) + * All functions in this file should use the prefix GCD (Gnunet Cadet Dht) */ - #ifndef GNUNET_SERVICE_CADET_DHT_H #define GNUNET_SERVICE_CADET_DHT_H @@ -40,23 +40,11 @@ extern "C" #include "platform.h" #include "gnunet_util_lib.h" -struct GCD_search_handle; - - /** - * Callback called on each path found over the DHT. - * - * @param cls Closure. - * @param path An unchecked, unoptimized path to the target node. - * After callback will no longer be valid! + * Handle for DHT search operation. */ -typedef void -(*GCD_search_callback) (void *cls, - const struct CadetPeerPath *path); +struct GCD_search_handle; -/******************************************************************************/ -/******************************** API ***********************************/ -/******************************************************************************/ /** * Initialize the DHT subsystem. @@ -66,6 +54,7 @@ typedef void void GCD_init (const struct GNUNET_CONFIGURATION_Handle *c); + /** * Shut down the DHT subsystem. */ @@ -73,14 +62,32 @@ void GCD_shutdown (void); +/** + * Function called by the HELLO subsystem whenever OUR hello + * changes. Re-triggers the DHT PUT immediately. + */ +void +GCD_hello_update (void); + +/** + * Search DHT for paths to @a peeR_id + * + * @param peer_id peer to search for + * @return handle to abort search + */ struct GCD_search_handle * -GCD_search (const struct GNUNET_PeerIdentity *peer_id, - GCD_search_callback callback, void *cls); +GCD_search (const struct GNUNET_PeerIdentity *peer_id); +/** + * Stop DHT search started with #GCD_search(). + * + * @param h handle to search to stop + */ void GCD_search_stop (struct GCD_search_handle *h); + #if 0 /* keep Emacsens' auto-indent happy */ { #endif @@ -88,6 +95,6 @@ GCD_search_stop (struct GCD_search_handle *h); } #endif -/* ifndef GNUNET_CADET_SERVICE_LOCAL_H */ +/* ifndef GNUNET_CADET_SERVICE_DHT_H */ #endif -/* end of gnunet-cadet-service_LOCAL.h */ +/* end of gnunet-service-cadet_dht.h */ diff --git a/src/cadet/gnunet-service-cadet_hello.c b/src/cadet/gnunet-service-cadet_hello.c index 3c63f3551b..6d85de39f6 100644 --- a/src/cadet/gnunet-service-cadet_hello.c +++ b/src/cadet/gnunet-service-cadet_hello.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2014 GNUnet e.V. + Copyright (C) 2014, 2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -17,58 +17,33 @@ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - +/** + * @file cadet/gnunet-service-cadet_hello.c + * @brief spread knowledge about how to contact other peers from PEERINFO + * @author Bartlomiej Polot + * @author Christian Grothoff + * + * TODO: + * - is most of this necessary/helpful? + * - should we not simply restrict this to OUR hello? + */ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_statistics_service.h" #include "gnunet_peerinfo_service.h" - #include "cadet_protocol.h" -#include "cadet_path.h" - +#include "gnunet-service-cadet.h" +#include "gnunet-service-cadet_dht.h" #include "gnunet-service-cadet_hello.h" #include "gnunet-service-cadet_peer.h" #define LOG(level, ...) GNUNET_log_from(level,"cadet-hll",__VA_ARGS__) - -/******************************************************************************/ -/******************************** STRUCTS **********************************/ -/******************************************************************************/ - - - -/******************************************************************************/ -/******************************* GLOBALS ***********************************/ -/******************************************************************************/ - -/** - * Global handle to the statistics service. - */ -extern struct GNUNET_STATISTICS_Handle *stats; - -/** - * Local peer own ID (memory efficient handle). - */ -extern GNUNET_PEER_Id myid; - -/** - * Local peer own ID (full value). - */ -extern struct GNUNET_PeerIdentity my_full_id; - - -/** - * Don't try to recover tunnels if shutting down. - */ -extern int shutting_down; - - /** * Hello message of local peer. */ -const struct GNUNET_HELLO_Message *mine; +static struct GNUNET_HELLO_Message *mine; /** * Handle to peerinfo service. @@ -78,13 +53,9 @@ static struct GNUNET_PEERINFO_Handle *peerinfo; /** * Iterator context. */ -struct GNUNET_PEERINFO_NotifyContext* nc; +static struct GNUNET_PEERINFO_NotifyContext *nc; -/******************************************************************************/ -/******************************** STATIC ***********************************/ -/******************************************************************************/ - /** * Process each hello message received from peerinfo. * @@ -94,31 +65,37 @@ struct GNUNET_PEERINFO_NotifyContext* nc; * @param err_msg Error message. */ static void -got_hello (void *cls, const struct GNUNET_PeerIdentity *id, +got_hello (void *cls, + const struct GNUNET_PeerIdentity *id, const struct GNUNET_HELLO_Message *hello, const char *err_msg) { struct CadetPeer *peer; - if (NULL == id || NULL == hello) + if ( (NULL == id) || + (NULL == hello) ) + return; + if (0 == memcmp (id, + &my_full_id, + sizeof (struct GNUNET_PeerIdentity))) { - LOG (GNUNET_ERROR_TYPE_DEBUG, " hello with id %p and msg %p\n", id, hello); + GNUNET_free_non_null (mine); + mine = (struct GNUNET_HELLO_Message *) GNUNET_copy_message (&hello->header); + GCD_hello_update (); return; } - LOG (GNUNET_ERROR_TYPE_DEBUG, " hello for %s (%d bytes), expires on %s\n", - GNUNET_i2s (id), GNUNET_HELLO_size (hello), - GNUNET_STRINGS_absolute_time_to_string (GNUNET_HELLO_get_last_expiration(hello))); - peer = GCP_get (id, GNUNET_YES); - GCP_set_hello (peer, hello); - - if (GCP_get_short_id (peer) == myid) - mine = GCP_get_hello (peer); -} + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Hello for %s (%d bytes), expires on %s\n", + GNUNET_i2s (id), + GNUNET_HELLO_size (hello), + GNUNET_STRINGS_absolute_time_to_string (GNUNET_HELLO_get_last_expiration (hello))); + peer = GCP_get (id, + GNUNET_YES); + GCP_set_hello (peer, + hello); +} -/******************************************************************************/ -/******************************** API ***********************************/ -/******************************************************************************/ /** * Initialize the hello subsystem. @@ -128,10 +105,12 @@ got_hello (void *cls, const struct GNUNET_PeerIdentity *id, void GCH_init (const struct GNUNET_CONFIGURATION_Handle *c) { - LOG (GNUNET_ERROR_TYPE_DEBUG, "init\n"); GNUNET_assert (NULL == nc); peerinfo = GNUNET_PEERINFO_connect (c); - nc = GNUNET_PEERINFO_notify (c, GNUNET_NO, &got_hello, NULL); + nc = GNUNET_PEERINFO_notify (c, + GNUNET_NO, + &got_hello, + NULL); } @@ -141,7 +120,6 @@ GCH_init (const struct GNUNET_CONFIGURATION_Handle *c) void GCH_shutdown () { - LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down channels\n"); if (NULL != nc) { GNUNET_PEERINFO_notify_cancel (nc); @@ -152,6 +130,11 @@ GCH_shutdown () GNUNET_PEERINFO_disconnect (peerinfo); peerinfo = NULL; } + if (NULL != mine) + { + GNUNET_free (mine); + mine = NULL; + } } @@ -166,35 +149,4 @@ GCH_get_mine (void) return mine; } - -/** - * Get another peer's hello message. - * - * @param id ID of the peer whose hello message is requested. - * - * @return Hello message, if any (NULL possible). - */ -const struct GNUNET_HELLO_Message * -GCH_get (const struct GNUNET_PeerIdentity *id) -{ - struct CadetPeer *p; - - p = GCP_get (id, GNUNET_NO); - if (NULL == p) - return NULL; - return GCP_get_hello (p); -} - - -/** - * Convert a hello message to a string. - * - * @param h Hello message. - */ -char * -GCH_2s (const struct GNUNET_HELLO_Message *h) -{ - return "hello (TODO)"; -} - - +/* end of gnunet-service-cadet-new_hello.c */ diff --git a/src/cadet/gnunet-service-cadet_hello.h b/src/cadet/gnunet-service-cadet_hello.h index 34121e1e01..4291ae985d 100644 --- a/src/cadet/gnunet-service-cadet_hello.h +++ b/src/cadet/gnunet-service-cadet_hello.h @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2014 GNUnet e.V. + Copyright (C) 2014, 2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -22,8 +22,9 @@ * @file cadet/gnunet-service-cadet_hello.h * @brief cadet service; dealing with hello messages * @author Bartlomiej Polot + * @author Christian Grothoff * - * All functions in this file should use the prefix GMH (Gnunet Cadet Hello) + * All functions in this file should use the prefix GCH (Gnunet Cadet Hello) */ #ifndef GNUNET_SERVICE_CADET_HELLO_H diff --git a/src/cadet/gnunet-service-cadet_local.c b/src/cadet/gnunet-service-cadet_local.c deleted file mode 100644 index dea6681df8..0000000000 --- a/src/cadet/gnunet-service-cadet_local.c +++ /dev/null @@ -1,1553 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - - -#include "platform.h" -#include "gnunet_util_lib.h" - -#include "gnunet_statistics_service.h" - -#include "cadet.h" -#include "cadet_protocol.h" /* GNUNET_CADET_Data is shared */ - -#include "gnunet-service-cadet_local.h" -#include "gnunet-service-cadet_channel.h" - -/* INFO DEBUG */ -#include "gnunet-service-cadet_tunnel.h" -#include "gnunet-service-cadet_peer.h" - -#define LOG(level, ...) GNUNET_log_from(level,"cadet-loc",__VA_ARGS__) - -/******************************************************************************/ -/******************************** STRUCTS **********************************/ -/******************************************************************************/ - -/** - * Struct containing information about a client of the service - * - * TODO: add a list of 'waiting' ports - */ -struct CadetClient -{ - /** - * Linked list next - */ - struct CadetClient *next; - - /** - * Linked list prev - */ - struct CadetClient *prev; - - /** - * Tunnels that belong to this client, indexed by local id - */ - struct GNUNET_CONTAINER_MultiHashMap32 *own_channels; - - /** - * Tunnels this client has accepted, indexed by incoming local id - */ - struct GNUNET_CONTAINER_MultiHashMap32 *incoming_channels; - - /** - * Channel ID for the next incoming channel. - */ - struct GNUNET_CADET_ClientChannelNumber next_ccn; - - /** - * Handle to communicate with the client - */ - struct GNUNET_SERVER_Client *handle; - - /** - * Ports that this client has declared interest in. - * Indexed by port, contains *Client. - */ - struct GNUNET_CONTAINER_MultiHashMap *ports; - - /** - * 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; -}; - -/******************************************************************************/ -/******************************* GLOBALS ***********************************/ -/******************************************************************************/ - -/** - * Global handle to the statistics service. - */ -extern struct GNUNET_STATISTICS_Handle *stats; - -/** - * Handle to server lib. - */ -static struct GNUNET_SERVER_Handle *server_handle; - -/** - * DLL with all the clients, head. - */ -static struct CadetClient *clients_head; - -/** - * DLL with all the clients, tail. - */ -static struct CadetClient *clients_tail; - -/** - * Next ID to assign to a client. - */ -unsigned int next_client_id; - -/** - * All ports clients of this peer have opened. - */ -static struct GNUNET_CONTAINER_MultiHashMap *ports; - -/** - * Notification context, to send messages to local clients. - */ -static struct GNUNET_SERVER_NotificationContext *nc; - - -/******************************************************************************/ -/******************************** STATIC ***********************************/ -/******************************************************************************/ - -/** - * Remove client's ports from the global hashmap on disconnect. - * - * @param cls Closure (unused). - * @param key Port. - * @param value Client structure. - * - * @return #GNUNET_OK, keep iterating. - */ -static int -client_release_ports (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - int res; - - res = GNUNET_CONTAINER_multihashmap_remove (ports, key, value); - if (GNUNET_YES != res) - { - GNUNET_break (0); - LOG (GNUNET_ERROR_TYPE_WARNING, - "Port %s by client %p was not registered.\n", - GNUNET_h2s (key), value); - } - return GNUNET_OK; -} - - -/** - * Iterator for deleting each channel whose client endpoint disconnected. - * - * @param cls Closure (client that has disconnected). - * @param key The local channel id (used to access the hashmap). - * @param value The value stored at the key (channel to destroy). - * - * @return #GNUNET_OK, keep iterating. - */ -static int -channel_destroy_iterator (void *cls, - uint32_t key, - void *value) -{ - struct CadetChannel *ch = value; - struct CadetClient *c = cls; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - " Channel %s destroy, due to client %s shutdown.\n", - GCCH_2s (ch), GML_2s (c)); - - GCCH_handle_local_destroy (ch, - c, - key < GNUNET_CADET_LOCAL_CHANNEL_ID_CLI); - return GNUNET_OK; -} - - -/** - * Unregister data and free memory for a client. - * - * @param c Client to destroy. No longer valid after call. - */ -static void -client_destroy (struct CadetClient *c) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, " client destroy: %p/%u\n", c, c->id); - GNUNET_SERVER_client_drop (c->handle); - c->shutting_down = GNUNET_YES; - - if (NULL != c->own_channels) - { - GNUNET_CONTAINER_multihashmap32_iterate (c->own_channels, - &channel_destroy_iterator, c); - GNUNET_CONTAINER_multihashmap32_destroy (c->own_channels); - } - if (NULL != c->incoming_channels) - { - GNUNET_CONTAINER_multihashmap32_iterate (c->incoming_channels, - &channel_destroy_iterator, c); - GNUNET_CONTAINER_multihashmap32_destroy (c->incoming_channels); - } - if (NULL != c->ports) - { - GNUNET_CONTAINER_multihashmap_iterate (c->ports, - &client_release_ports, c); - GNUNET_CONTAINER_multihashmap_destroy (c->ports); - } - - GNUNET_CONTAINER_DLL_remove (clients_head, clients_tail, c); - GNUNET_STATISTICS_update (stats, "# clients", -1, GNUNET_NO); - GNUNET_SERVER_client_set_user_context (c->handle, NULL); - GNUNET_free (c); -} - - -/** - * Create a client record, register data and initialize memory. - * - * @param client Client's handle. - */ -static struct CadetClient * -client_new (struct GNUNET_SERVER_Client *client) -{ - struct CadetClient *c; - - GNUNET_SERVER_client_keep (client); - GNUNET_SERVER_notification_context_add (nc, client); - - c = GNUNET_new (struct CadetClient); - c->handle = client; - c->id = next_client_id++; /* overflow not important: just for debug */ - - c->own_channels = GNUNET_CONTAINER_multihashmap32_create (32); - c->incoming_channels = GNUNET_CONTAINER_multihashmap32_create (32); - - GNUNET_SERVER_client_set_user_context (client, c); - GNUNET_CONTAINER_DLL_insert (clients_head, clients_tail, c); - GNUNET_STATISTICS_update (stats, "# clients", +1, GNUNET_NO); - - LOG (GNUNET_ERROR_TYPE_DEBUG, " client created: %p/%u\n", c, c->id); - - return c; -} - - -/******************************************************************************/ -/******************************** HANDLES ***********************************/ -/******************************************************************************/ - -/** - * Handler for client connection. - * - * @param cls Closure (unused). - * @param client Client handler. - */ -static void -handle_client_connect (void *cls, struct GNUNET_SERVER_Client *client) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, "Client connected: %p\n", client); - if (NULL == client) - return; - - (void) client_new (client); -} - - -/** - * 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_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client) -{ - struct CadetClient *c; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected: %p\n", client); - - c = GML_client_get (client); - if (NULL != c) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "matching client found (%u, %p)\n", - c->id, c); - client_destroy (c); - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " disconnecting client's context NULL\n"); - } - return; -} - - -/** - * Handler for port open requests. - * - * @param cls Closure. - * @param client Identification of the client. - * @param message The actual message. - */ -static void -handle_port_open (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - struct CadetClient *c; - struct GNUNET_CADET_PortMessage *pmsg; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "open port requested\n"); - - /* Sanity check for client registration */ - if (NULL == (c = GML_client_get (client))) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); - - /* Message size sanity check */ - if (sizeof (struct GNUNET_CADET_PortMessage) != ntohs (message->size)) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - - pmsg = (struct GNUNET_CADET_PortMessage *) message; - if (NULL == c->ports) - { - c->ports = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO); - } - /* store in client's hashmap */ - if (GNUNET_OK != - GNUNET_CONTAINER_multihashmap_put (c->ports, &pmsg->port, c, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - /* store in global hashmap */ - /* FIXME only allow one client to have the port open, - * have a backup hashmap with waiting clients */ - GNUNET_CONTAINER_multihashmap_put (ports, &pmsg->port, c, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); - - GNUNET_SERVER_receive_done (client, GNUNET_OK); -} - - -/** - * Handler for port close requests. - * - * @param cls Closure. - * @param client Identification of the client. - * @param message The actual message. - */ -static void -handle_port_close (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - struct CadetClient *c; - struct GNUNET_CADET_PortMessage *pmsg; - int removed; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "close port requested\n"); - - /* Sanity check for client registration */ - if (NULL == (c = GML_client_get (client))) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); - - /* Message size sanity check */ - if (sizeof (struct GNUNET_CADET_PortMessage) != ntohs (message->size)) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - - pmsg = (struct GNUNET_CADET_PortMessage *) message; - removed = GNUNET_CONTAINER_multihashmap_remove (c->ports, &pmsg->port, c); - GNUNET_break_op (GNUNET_YES == removed); - removed = GNUNET_CONTAINER_multihashmap_remove (ports, &pmsg->port, c); - GNUNET_break_op (GNUNET_YES == removed); - - GNUNET_SERVER_receive_done (client, GNUNET_OK); -} - - -/** - * Handler for requests of new channels. - * - * @param cls Closure. - * @param client Identification of the client. - * @param message The actual message. - */ -static void -handle_channel_create (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - struct CadetClient *c; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "\n"); - LOG (GNUNET_ERROR_TYPE_DEBUG, "new channel requested\n"); - - /* Sanity check for client registration */ - if (NULL == (c = GML_client_get (client))) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); - - /* Message size sanity check */ - if (sizeof (struct GNUNET_CADET_LocalChannelCreateMessage) - != ntohs (message->size)) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - - if (GNUNET_OK != - GCCH_handle_local_create (c, - (struct GNUNET_CADET_LocalChannelCreateMessage *) - message)) - { - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - - GNUNET_SERVER_receive_done (client, GNUNET_OK); -} - - -/** - * Handler for requests of deleting tunnels - * - * @param cls closure - * @param client identification of the client - * @param message the actual message - */ -static void -handle_channel_destroy (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - const struct GNUNET_CADET_LocalChannelDestroyMessage *msg; - struct CadetClient *c; - struct CadetChannel *ch; - struct GNUNET_CADET_ClientChannelNumber ccn; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Got a DESTROY CHANNEL from client!\n"); - - /* Sanity check for client registration */ - if (NULL == (c = GML_client_get (client))) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); - - /* Message sanity check */ - if (sizeof (struct GNUNET_CADET_LocalChannelDestroyMessage) - != ntohs (message->size)) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - - msg = (const struct GNUNET_CADET_LocalChannelDestroyMessage *) message; - - /* Retrieve tunnel */ - ccn = msg->ccn; - ch = GML_channel_get (c, ccn); - - LOG (GNUNET_ERROR_TYPE_INFO, "Client %u is destroying channel %X\n", - c->id, ccn); - - if (NULL == ch) - { - LOG (GNUNET_ERROR_TYPE_WARNING, " channel %X not found\n", ccn); - GNUNET_STATISTICS_update (stats, - "# client destroy messages on unknown channel", - 1, GNUNET_NO); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } - - GCCH_handle_local_destroy (ch, - c, - ntohl (ccn.channel_of_client) < GNUNET_CADET_LOCAL_CHANNEL_ID_CLI); - - GNUNET_SERVER_receive_done (client, GNUNET_OK); -} - - -/** - * Handler for client traffic - * - * @param cls closure - * @param client identification of the client - * @param message the actual message - */ -static void -handle_data (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - const struct GNUNET_MessageHeader *payload; - struct GNUNET_CADET_LocalData *msg; - struct CadetClient *c; - struct CadetChannel *ch; - struct GNUNET_CADET_ClientChannelNumber ccn; - size_t message_size; - size_t payload_size; - size_t payload_claimed_size; - int fwd; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "\n"); - LOG (GNUNET_ERROR_TYPE_DEBUG, "\n"); - LOG (GNUNET_ERROR_TYPE_DEBUG, "Got data from a client\n"); - - /* Sanity check for client registration */ - if (NULL == (c = GML_client_get (client))) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - - /* Sanity check for message size */ - message_size = ntohs (message->size); - if (sizeof (struct GNUNET_CADET_LocalData) - + sizeof (struct GNUNET_MessageHeader) > message_size - || GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE < message_size) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - - /* Sanity check for payload size */ - payload_size = message_size - sizeof (struct GNUNET_CADET_LocalData); - msg = (struct GNUNET_CADET_LocalData *) message; - payload = (struct GNUNET_MessageHeader *) &msg[1]; - payload_claimed_size = ntohs (payload->size); - if (sizeof (struct GNUNET_MessageHeader) > payload_claimed_size - || GNUNET_CONSTANTS_MAX_CADET_MESSAGE_SIZE < payload_claimed_size - || payload_claimed_size > payload_size) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - "client claims to send %u bytes in %u payload\n", - payload_claimed_size, payload_size); - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - - ccn = msg->ccn; - LOG (GNUNET_ERROR_TYPE_DEBUG, " %u bytes (%u payload) by client %u\n", - payload_size, payload_claimed_size, c->id); - - /* Channel exists? */ - fwd = ntohl (ccn.channel_of_client) >= GNUNET_CADET_LOCAL_CHANNEL_ID_CLI; - ch = GML_channel_get (c, ccn); - if (NULL == ch) - { - GNUNET_STATISTICS_update (stats, - "# client data messages on unknown channel", - 1, GNUNET_NO); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } - - if (GNUNET_OK != GCCH_handle_local_data (ch, c, fwd, payload, payload_size)) - { - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - - LOG (GNUNET_ERROR_TYPE_DEBUG, "receive done OK\n"); - GNUNET_SERVER_receive_done (client, 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_ack (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - struct GNUNET_CADET_LocalAck *msg; - struct CadetChannel *ch; - struct CadetClient *c; - struct GNUNET_CADET_ClientChannelNumber ccn; - int fwd; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "\n"); - LOG (GNUNET_ERROR_TYPE_DEBUG, "Got a local ACK\n"); - - /* Sanity check for client registration */ - if (NULL == (c = GML_client_get (client))) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " by client %u\n", c->id); - - msg = (struct GNUNET_CADET_LocalAck *) message; - - /* Channel exists? */ - ccn = msg->ccn; - LOG (GNUNET_ERROR_TYPE_DEBUG, " on channel %X\n", - ntohl (ccn.channel_of_client)); - ch = GML_channel_get (c, ccn); - LOG (GNUNET_ERROR_TYPE_DEBUG, " -- ch %p\n", ch); - if (NULL == ch) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Channel %X unknown.\n", - ntohl (ccn.channel_of_client)); - LOG (GNUNET_ERROR_TYPE_DEBUG, " for client %u.\n", c->id); - GNUNET_STATISTICS_update (stats, - "# client ack messages on unknown channel", - 1, GNUNET_NO); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } - - /* If client is root, the ACK is going FWD, therefore this is "BCK ACK". */ - /* If client is dest, the ACK is going BCK, therefore this is "FWD ACK" */ - fwd = ntohl (ccn.channel_of_client) < GNUNET_CADET_LOCAL_CHANNEL_ID_CLI; - - GCCH_handle_local_ack (ch, fwd); - GNUNET_SERVER_receive_done (client, GNUNET_OK); -} - - -/** - * Iterator over all peers to send a monitoring client info about each peer. - * - * @param cls Closure (). - * @param peer Peer ID (tunnel remote peer). - * @param value Peer info. - * - * @return #GNUNET_YES, to keep iterating. - */ -static int -get_all_peers_iterator (void *cls, - const struct GNUNET_PeerIdentity * peer, - void *value) -{ - struct GNUNET_SERVER_Client *client = cls; - struct CadetPeer *p = value; - struct GNUNET_CADET_LocalInfoPeer msg; - - msg.header.size = htons (sizeof (msg)); - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEERS); - msg.destination = *peer; - msg.paths = htons (GCP_count_paths (p)); - msg.tunnel = htons (NULL != GCP_get_tunnel (p)); - - LOG (GNUNET_ERROR_TYPE_DEBUG, "sending info about peer %s\n", - GNUNET_i2s (peer)); - - GNUNET_SERVER_notification_context_unicast (nc, client, - &msg.header, GNUNET_NO); - return GNUNET_YES; -} - - -/** - * Iterator over all peers to dump info for each peer. - * - * @param cls Closure (unused). - * @param peer Peer ID (tunnel remote peer). - * @param value Peer info. - * - * @return #GNUNET_YES, to keep iterating. - */ -static int -show_peer_iterator (void *cls, - const struct GNUNET_PeerIdentity * peer, - void *value) -{ - struct CadetPeer *p = value; - struct CadetTunnel *t; - - t = GCP_get_tunnel (p); - if (NULL != t) - GCT_debug (t, GNUNET_ERROR_TYPE_ERROR); - - LOG (GNUNET_ERROR_TYPE_ERROR, "\n"); - - return GNUNET_YES; -} - - -/** - * Iterator over all paths of a peer to build an InfoPeer message. - * - * Message contains blocks of peers, first not included. - * - * @param cls Closure (message to build). - * @param peer Peer this path is towards. - * @param path Path itself - * @return #GNUNET_YES if should keep iterating. - * #GNUNET_NO otherwise. - */ -static int -path_info_iterator (void *cls, - struct CadetPeer *peer, - struct CadetPeerPath *path) -{ - struct GNUNET_CADET_LocalInfoPeer *resp = cls; - struct GNUNET_PeerIdentity *id; - uint16_t msg_size; - uint16_t path_size; - unsigned int i; - - msg_size = ntohs (resp->header.size); - path_size = sizeof (struct GNUNET_PeerIdentity) * (path->length - 1); - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Info Path %u\n", path->length); - if (msg_size + path_size > UINT16_MAX) - { - LOG (GNUNET_ERROR_TYPE_WARNING, "path too long for info message\n"); - return GNUNET_NO; - } - - i = msg_size - sizeof (struct GNUNET_CADET_LocalInfoPeer); - i = i / sizeof (struct GNUNET_PeerIdentity); - - /* Set id to the address of the first free peer slot. */ - id = (struct GNUNET_PeerIdentity *) &resp[1]; - id = &id[i]; - - /* Don't copy first peers. - * First peer is always the local one. - * Last peer is always the destination (leave as 0, EOL). - */ - for (i = 0; i < path->length - 1; i++) - { - GNUNET_PEER_resolve (path->peers[i + 1], &id[i]); - LOG (GNUNET_ERROR_TYPE_DEBUG, " %s\n", GNUNET_i2s (&id[i])); - } - - resp->header.size = htons (msg_size + path_size); - - return GNUNET_YES; -} - - -/** - * Handler for client's INFO PEERS request. - * - * @param cls Closure (unused). - * @param client Identification of the client. - * @param message The actual message. - */ -static void -handle_get_peers (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - struct CadetClient *c; - struct GNUNET_MessageHeader reply; - - /* Sanity check for client registration */ - if (NULL == (c = GML_client_get (client))) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received get peers request from client %u (%p)\n", - c->id, client); - - GCP_iterate_all (get_all_peers_iterator, client); - reply.size = htons (sizeof (reply)); - reply.type = htons (GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEERS); - GNUNET_SERVER_notification_context_unicast (nc, client, &reply, GNUNET_NO); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Get peers request from client %u completed\n", c->id); - GNUNET_SERVER_receive_done (client, GNUNET_OK); -} - - -/** - * Handler for client's SHOW_PEER request. - * - * @param cls Closure (unused). - * @param client Identification of the client. - * @param message The actual message. - */ -void -handle_show_peer (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - const struct GNUNET_CADET_LocalInfo *msg; - struct GNUNET_CADET_LocalInfoPeer *resp; - struct CadetPeer *p; - struct CadetClient *c; - unsigned char cbuf[64 * 1024]; - - /* Sanity check for client registration */ - if (NULL == (c = GML_client_get (client))) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - - msg = (struct GNUNET_CADET_LocalInfo *) message; - resp = (struct GNUNET_CADET_LocalInfoPeer *) cbuf; - LOG (GNUNET_ERROR_TYPE_INFO, - "Received peer info request from client %u for peer %s\n", - c->id, GNUNET_i2s_full (&msg->peer)); - - resp->header.type = htons (GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEER); - resp->header.size = htons (sizeof (struct GNUNET_CADET_LocalInfoPeer)); - resp->destination = msg->peer; - p = GCP_get (&msg->peer, GNUNET_NO); - if (NULL == p) - { - /* We don't know the peer */ - - LOG (GNUNET_ERROR_TYPE_INFO, "Peer %s unknown\n", - GNUNET_i2s_full (&msg->peer)); - resp->paths = htons (0); - resp->tunnel = htons (NULL != GCP_get_tunnel (p)); - - GNUNET_SERVER_notification_context_unicast (nc, client, - &resp->header, - GNUNET_NO); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } - - resp->paths = htons (GCP_count_paths (p)); - resp->tunnel = htons (NULL != GCP_get_tunnel (p)); - GCP_iterate_paths (p, &path_info_iterator, resp); - - GNUNET_SERVER_notification_context_unicast (nc, c->handle, - &resp->header, GNUNET_NO); - - LOG (GNUNET_ERROR_TYPE_INFO, "Show peer from client %u completed.\n", c->id); - GNUNET_SERVER_receive_done (client, GNUNET_OK); -} - - -/** - * Iterator over all tunnels to send a monitoring client info about each tunnel. - * - * @param cls Closure (). - * @param peer Peer ID (tunnel remote peer). - * @param value Tunnel info. - * - * @return #GNUNET_YES, to keep iterating. - */ -static int -get_all_tunnels_iterator (void *cls, - const struct GNUNET_PeerIdentity * peer, - void *value) -{ - struct GNUNET_SERVER_Client *client = cls; - struct CadetTunnel *t = value; - struct GNUNET_CADET_LocalInfoTunnel msg; - - msg.header.size = htons (sizeof (msg)); - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNELS); - msg.destination = *peer; - msg.channels = htonl (GCT_count_channels (t)); - msg.connections = htonl (GCT_count_any_connections (t)); - msg.cstate = htons ((uint16_t) GCT_get_cstate (t)); - msg.estate = htons ((uint16_t) GCT_get_estate (t)); - - LOG (GNUNET_ERROR_TYPE_DEBUG, "sending info about tunnel ->%s\n", - GNUNET_i2s (peer)); - - GNUNET_SERVER_notification_context_unicast (nc, client, - &msg.header, GNUNET_NO); - return GNUNET_YES; -} - - -/** - * Handler for client's INFO TUNNELS request. - * - * @param cls Closure (unused). - * @param client Identification of the client. - * @param message The actual message. - */ -static void -handle_get_tunnels (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - struct CadetClient *c; - struct GNUNET_MessageHeader reply; - - /* Sanity check for client registration */ - if (NULL == (c = GML_client_get (client))) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received get tunnels request from client %u (%p)\n", - c->id, client); - - GCT_iterate_all (get_all_tunnels_iterator, client); - reply.size = htons (sizeof (reply)); - reply.type = htons (GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNELS); - GNUNET_SERVER_notification_context_unicast (nc, client, &reply, GNUNET_NO); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Get tunnels request from client %u completed\n", c->id); - GNUNET_SERVER_receive_done (client, GNUNET_OK); -} - - -static void -iter_connection (void *cls, struct CadetConnection *c) -{ - struct GNUNET_CADET_LocalInfoTunnel *msg = cls; - struct GNUNET_CADET_ConnectionTunnelIdentifier *h; - - h = (struct GNUNET_CADET_ConnectionTunnelIdentifier *) &msg[1]; - h[msg->connections] = *(GCC_get_id (c)); - msg->connections++; -} - -static void -iter_channel (void *cls, struct CadetChannel *ch) -{ - struct GNUNET_CADET_LocalInfoTunnel *msg = cls; - struct GNUNET_CADET_ConnectionTunnelIdentifier *h = (struct GNUNET_CADET_ConnectionTunnelIdentifier *) &msg[1]; - struct GNUNET_CADET_ChannelTunnelNumber *chn = (struct GNUNET_CADET_ChannelTunnelNumber *) &h[msg->connections]; - - chn[msg->channels] = GCCH_get_id (ch); - msg->channels++; -} - - -/** - * Handler for client's SHOW_TUNNEL request. - * - * @param cls Closure (unused). - * @param client Identification of the client. - * @param message The actual message. - */ -void -handle_show_tunnel (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - const struct GNUNET_CADET_LocalInfo *msg; - struct GNUNET_CADET_LocalInfoTunnel *resp; - struct CadetClient *c; - struct CadetTunnel *t; - unsigned int ch_n; - unsigned int c_n; - size_t size; - - /* Sanity check for client registration */ - if (NULL == (c = GML_client_get (client))) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - - msg = (struct GNUNET_CADET_LocalInfo *) message; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received tunnel info request from client %u for tunnel %s\n", - c->id, GNUNET_i2s_full(&msg->peer)); - - t = GCP_get_tunnel (GCP_get (&msg->peer, GNUNET_NO)); - if (NULL == t) - { - /* We don't know the tunnel */ - struct GNUNET_CADET_LocalInfoTunnel warn; - - LOG (GNUNET_ERROR_TYPE_INFO, "Tunnel %s unknown %u\n", - GNUNET_i2s_full(&msg->peer), sizeof (warn)); - warn.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNEL); - warn.header.size = htons (sizeof (warn)); - warn.destination = msg->peer; - warn.channels = htonl (0); - warn.connections = htonl (0); - warn.cstate = htons (0); - warn.estate = htons (0); - - GNUNET_SERVER_notification_context_unicast (nc, client, - &warn.header, - GNUNET_NO); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } - - /* Initialize context */ - ch_n = GCT_count_channels (t); - c_n = GCT_count_any_connections (t); - - size = sizeof (struct GNUNET_CADET_LocalInfoTunnel); - size += c_n * sizeof (struct GNUNET_CADET_ConnectionTunnelIdentifier); - size += ch_n * sizeof (struct GNUNET_CADET_ChannelTunnelNumber); - - resp = GNUNET_malloc (size); - resp->header.type = htons (GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNEL); - resp->header.size = htons (size); - resp->destination = msg->peer; - /* Do not interleave with iterators, iter_channel needs conn in HBO */ - GCT_iterate_connections (t, &iter_connection, resp); - GCT_iterate_channels (t, &iter_channel, resp); - resp->connections = htonl (resp->connections); - resp->channels = htonl (resp->channels); - /* Do not interleave end */ - resp->cstate = htons (GCT_get_cstate (t)); - resp->estate = htons (GCT_get_estate (t)); - GNUNET_SERVER_notification_context_unicast (nc, c->handle, - &resp->header, GNUNET_NO); - GNUNET_free (resp); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Show tunnel request from client %u completed. %u conn, %u ch\n", - c->id, c_n, ch_n); - GNUNET_SERVER_receive_done (client, GNUNET_OK); -} - - -/** - * Handler for client's INFO_DUMP request. - * - * @param cls Closure (unused). - * @param client Identification of the client. - * @param message The actual message. - */ -void -handle_info_dump (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - struct CadetClient *c; - - /* Sanity check for client registration */ - if (NULL == (c = GML_client_get (client))) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } - - LOG (GNUNET_ERROR_TYPE_INFO, "Received dump info request from client %u\n", - c->id); - LOG (GNUNET_ERROR_TYPE_ERROR, - "*************************** DUMP START ***************************\n"); - - for (c = clients_head; NULL != c; c = c->next) - { - LOG (GNUNET_ERROR_TYPE_ERROR, "Client %u (%p), handle: %p\n", - c->id, c, c->handle); - if (NULL != c->ports) - LOG (GNUNET_ERROR_TYPE_ERROR, "\t%3u ports registered\n", - GNUNET_CONTAINER_multihashmap_size (c->ports)); - else - LOG (GNUNET_ERROR_TYPE_ERROR, "\t no ports registered\n"); - LOG (GNUNET_ERROR_TYPE_ERROR, "\t%3u own channles\n", - GNUNET_CONTAINER_multihashmap32_size (c->own_channels)); - LOG (GNUNET_ERROR_TYPE_ERROR, "\t%3u incoming channles\n", - GNUNET_CONTAINER_multihashmap32_size (c->incoming_channels)); - } - LOG (GNUNET_ERROR_TYPE_ERROR, "***************************\n"); - GCP_iterate_all (&show_peer_iterator, NULL); - - LOG (GNUNET_ERROR_TYPE_ERROR, - "**************************** DUMP END ****************************\n"); - - GNUNET_SERVER_receive_done (client, GNUNET_OK); -} - - -/** - * Functions to handle messages from clients - */ -static struct GNUNET_SERVER_MessageHandler client_handlers[] = { - {&handle_port_open, NULL, GNUNET_MESSAGE_TYPE_CADET_LOCAL_PORT_OPEN, - sizeof (struct GNUNET_CADET_PortMessage)}, - {&handle_port_close, NULL, GNUNET_MESSAGE_TYPE_CADET_LOCAL_PORT_CLOSE, - sizeof (struct GNUNET_CADET_PortMessage)}, - {&handle_channel_create, NULL, GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_CREATE, - sizeof (struct GNUNET_CADET_LocalChannelCreateMessage)}, - {&handle_channel_destroy, NULL, GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_DESTROY, - sizeof (struct GNUNET_CADET_LocalChannelDestroyMessage)}, - {&handle_data, NULL, GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA, 0}, - {&handle_ack, NULL, GNUNET_MESSAGE_TYPE_CADET_LOCAL_ACK, - sizeof (struct GNUNET_CADET_LocalAck)}, - {&handle_get_peers, NULL, GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEERS, - sizeof (struct GNUNET_MessageHeader)}, - {&handle_show_peer, NULL, GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_PEER, - sizeof (struct GNUNET_CADET_LocalInfo)}, - {&handle_get_tunnels, NULL, GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNELS, - sizeof (struct GNUNET_MessageHeader)}, - {&handle_show_tunnel, NULL, GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_TUNNEL, - sizeof (struct GNUNET_CADET_LocalInfo)}, - {&handle_info_dump, NULL, GNUNET_MESSAGE_TYPE_CADET_LOCAL_INFO_DUMP, - sizeof (struct GNUNET_MessageHeader)}, - {NULL, NULL, 0, 0} -}; - - - -/******************************************************************************/ -/******************************** API ***********************************/ -/******************************************************************************/ - -/** - * Initialize server subsystem. - * - * @param handle Server handle. - */ -void -GML_init (struct GNUNET_SERVER_Handle *handle) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, "init\n"); - server_handle = handle; - GNUNET_SERVER_suspend (server_handle); - ports = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_NO); -} - - -/** - * Install server (service) handlers and start listening to clients. - */ -void -GML_start (void) -{ - GNUNET_SERVER_add_handlers (server_handle, client_handlers); - GNUNET_SERVER_connect_notify (server_handle, &handle_client_connect, NULL); - GNUNET_SERVER_disconnect_notify (server_handle, &handle_client_disconnect, - NULL); - nc = GNUNET_SERVER_notification_context_create (server_handle, 1); - - clients_head = NULL; - clients_tail = NULL; - next_client_id = 0; - GNUNET_SERVER_resume (server_handle); -} - - -/** - * Shutdown server. - */ -void -GML_shutdown (void) -{ - struct CadetClient *c; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down local\n"); - - for (c = clients_head; NULL != clients_head; c = clients_head) - client_destroy (c); - - if (nc != NULL) - { - GNUNET_SERVER_notification_context_destroy (nc); - nc = NULL; - } - -} - - -/** - * Get a channel from a client. - * - * @param c Client to check. - * @param ccn Channel ID, must be local (> 0x800...). - * - * @return non-NULL if channel exists in the clients lists - */ -struct CadetChannel * -GML_channel_get (struct CadetClient *c, - struct GNUNET_CADET_ClientChannelNumber ccn) -{ - struct GNUNET_CONTAINER_MultiHashMap32 *map; - - if (ntohl (ccn.channel_of_client) >= GNUNET_CADET_LOCAL_CHANNEL_ID_CLI) - map = c->own_channels; - else - map = c->incoming_channels; - - if (NULL == map) - { - GNUNET_break (0); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Client %s does no t have a valid map for CCN %X\n", - GML_2s (c), ccn); - return NULL; - } - return GNUNET_CONTAINER_multihashmap32_get (map, - ccn.channel_of_client); -} - - -/** - * Add a channel to a client - * - * @param client Client. - * @param ccn Channel ID. - * @param ch Channel. - */ -void -GML_channel_add (struct CadetClient *client, - struct GNUNET_CADET_ClientChannelNumber ccn, - struct CadetChannel *ch) -{ - if (ntohl (ccn.channel_of_client) >= GNUNET_CADET_LOCAL_CHANNEL_ID_CLI) - GNUNET_CONTAINER_multihashmap32_put (client->own_channels, - ccn.channel_of_client, - ch, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); - else - GNUNET_CONTAINER_multihashmap32_put (client->incoming_channels, - ccn.channel_of_client, - ch, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); -} - - -/** - * Remove a channel from a client. - * - * @param client Client. - * @param ccn Channel ID. - * @param ch Channel. - */ -void -GML_channel_remove (struct CadetClient *client, - struct GNUNET_CADET_ClientChannelNumber ccn, - struct CadetChannel *ch) -{ - if (ntohl (ccn.channel_of_client) >= GNUNET_CADET_LOCAL_CHANNEL_ID_CLI) - GNUNET_CONTAINER_multihashmap32_remove (client->own_channels, - ccn.channel_of_client, - ch); - else - GNUNET_CONTAINER_multihashmap32_remove (client->incoming_channels, - ccn.channel_of_client, - ch); -} - - -/** - * Get the tunnel's next free local channel ID. - * - * @param c Client. - * - * @return LID of a channel free to use. - */ -struct GNUNET_CADET_ClientChannelNumber -GML_get_next_ccn (struct CadetClient *c) -{ - struct GNUNET_CADET_ClientChannelNumber ccn; - - while (NULL != GML_channel_get (c, - c->next_ccn)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Channel %u exists...\n", - c->next_ccn); - c->next_ccn.channel_of_client - = htonl (1 + (ntohl (c->next_ccn.channel_of_client))); - if (ntohl (c->next_ccn.channel_of_client) >= - GNUNET_CADET_LOCAL_CHANNEL_ID_CLI) - c->next_ccn.channel_of_client = htonl (0); - } - ccn = c->next_ccn; - c->next_ccn.channel_of_client - = htonl (1 + (ntohl (c->next_ccn.channel_of_client))); - - return ccn; -} - - -/** - * 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 - */ -struct CadetClient * -GML_client_get (struct GNUNET_SERVER_Client *client) -{ - if (NULL == client) - return NULL; - return GNUNET_SERVER_client_get_user_context (client, - struct CadetClient); -} - - -/** - * Find a client that has opened a port - * - * @param port Port to check. - * - * @return non-NULL if a client has the port. - */ -struct CadetClient * -GML_client_get_by_port (const struct GNUNET_HashCode *port) -{ - return GNUNET_CONTAINER_multihashmap_get (ports, port); -} - - -/** - * Deletes a channel from a client (either owner or destination). - * - * @param c Client whose tunnel to delete. - * @param ch Channel which should be deleted. - * @param id Channel ID. - */ -void -GML_client_delete_channel (struct CadetClient *c, - struct CadetChannel *ch, - struct GNUNET_CADET_ClientChannelNumber id) -{ - int res; - - if (ntohl (id.channel_of_client) >= GNUNET_CADET_LOCAL_CHANNEL_ID_CLI) - { - res = GNUNET_CONTAINER_multihashmap32_remove (c->own_channels, - id.channel_of_client, - ch); - if (GNUNET_YES != res) - LOG (GNUNET_ERROR_TYPE_DEBUG, "client_delete_tunnel root KO\n"); - } - else - { - res = GNUNET_CONTAINER_multihashmap32_remove (c->incoming_channels, - id.channel_of_client, - ch); - if (GNUNET_YES != res) - LOG (GNUNET_ERROR_TYPE_DEBUG, "client_delete_channel dest KO\n"); - } -} - -/** - * Build a local ACK message and send it to a local client, if needed. - * - * If the client was already allowed to send data, do nothing. - * - * @param c Client to whom send the ACK. - * @param ccn Channel ID to use - */ -void -GML_send_ack (struct CadetClient *c, - struct GNUNET_CADET_ClientChannelNumber ccn) -{ - struct GNUNET_CADET_LocalAck msg; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "send local %s ack on %X towards %p\n", - ntohl (ccn.channel_of_client) < GNUNET_CADET_LOCAL_CHANNEL_ID_CLI - ? "FWD" : "BCK", - ntohl (ccn.channel_of_client), - c); - - msg.header.size = htons (sizeof (msg)); - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_LOCAL_ACK); - msg.ccn = ccn; - GNUNET_SERVER_notification_context_unicast (nc, - c->handle, - &msg.header, - GNUNET_NO); - -} - - - -/** - * Notify the client that a new incoming channel was created. - * - * @param c Client to notify. - * @param ccn Channel ID. - * @param port Channel's destination port. - * @param opt Options (bit array). - * @param peer Origin peer. - */ -void -GML_send_channel_create (struct CadetClient *c, - struct GNUNET_CADET_ClientChannelNumber ccn, - const struct GNUNET_HashCode *port, - uint32_t opt, - const struct GNUNET_PeerIdentity *peer) -{ - struct GNUNET_CADET_LocalChannelCreateMessage msg; - - msg.header.size = htons (sizeof (msg)); - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_CREATE); - msg.ccn = ccn; - msg.port = *port; - msg.opt = htonl (opt); - msg.peer = *peer; - GNUNET_SERVER_notification_context_unicast (nc, c->handle, - &msg.header, GNUNET_NO); -} - - -/** - * Build a local channel NACK message and send it to a local client. - * - * @param c Client to whom send the NACK. - * @param ccn Channel ID to use - */ -void -GML_send_channel_nack (struct CadetClient *c, - struct GNUNET_CADET_ClientChannelNumber ccn) -{ - struct GNUNET_CADET_LocalAck msg; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "send local nack on %X towards %p\n", - ntohl (ccn.channel_of_client), - c); - - msg.header.size = htons (sizeof (msg)); - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_NACK_DEPRECATED); - msg.ccn = ccn; - GNUNET_SERVER_notification_context_unicast (nc, - c->handle, - &msg.header, - GNUNET_NO); - -} - -/** - * Notify a client that a channel is no longer valid. - * - * @param c Client. - * @param ccn ID of the channel that is destroyed. - */ -void -GML_send_channel_destroy (struct CadetClient *c, - struct GNUNET_CADET_ClientChannelNumber ccn) -{ - struct GNUNET_CADET_LocalChannelDestroyMessage msg; - - if (NULL == c) - { - GNUNET_break (0); - return; - } - if (GNUNET_YES == c->shutting_down) - return; - msg.header.size = htons (sizeof (msg)); - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_LOCAL_CHANNEL_DESTROY); - msg.ccn = ccn; - GNUNET_SERVER_notification_context_unicast (nc, c->handle, - &msg.header, GNUNET_NO); -} - - -/** - * Modify the cadet message ID from global to local and send to client. - * - * @param c Client to send to. - * @param msg Message to modify and send. - * @param ccn Channel ID to use (c can be both owner and client). - */ -void -GML_send_data (struct CadetClient *c, - const struct GNUNET_CADET_ChannelAppDataMessage *msg, - struct GNUNET_CADET_ClientChannelNumber ccn) -{ - struct GNUNET_CADET_LocalData *copy; - uint16_t size = ntohs (msg->header.size) - sizeof (struct GNUNET_CADET_ChannelAppDataMessage); - char cbuf[size + sizeof (struct GNUNET_CADET_LocalData)]; - - if (size < sizeof (struct GNUNET_MessageHeader)) - { - GNUNET_break_op (0); - return; - } - if (NULL == c) - { - GNUNET_break (0); - return; - } - copy = (struct GNUNET_CADET_LocalData *) cbuf; - GNUNET_memcpy (©[1], &msg[1], size); - copy->header.size = htons (sizeof (struct GNUNET_CADET_LocalData) + size); - copy->header.type = htons (GNUNET_MESSAGE_TYPE_CADET_LOCAL_DATA); - copy->ccn = ccn; - GNUNET_SERVER_notification_context_unicast (nc, c->handle, - ©->header, GNUNET_NO); -} - - -/** - * Get the static string to represent a client. - * - * @param c Client. - * - * @return Static string for the client. - */ -const char * -GML_2s (const struct CadetClient *c) -{ - static char buf[32]; - - SPRINTF (buf, "%u", c->id); - return buf; -} diff --git a/src/cadet/gnunet-service-cadet_local.h b/src/cadet/gnunet-service-cadet_local.h deleted file mode 100644 index 113c2f489c..0000000000 --- a/src/cadet/gnunet-service-cadet_local.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file cadet/gnunet-service-cadet_local.h - * @brief cadet service; dealing with local clients - * @author Bartlomiej Polot - * - * All functions in this file should use the prefix GML (Gnunet Cadet Local) - */ - -#ifndef GNUNET_SERVICE_CADET_LOCAL_H -#define GNUNET_SERVICE_CADET_LOCAL_H - -#ifdef __cplusplus -extern "C" -{ -#if 0 /* keep Emacsens' auto-indent happy */ -} -#endif -#endif - -#include "platform.h" -#include "gnunet_util_lib.h" - -/** - * Struct containing information about a client of the service - */ -struct CadetClient; - -#include "gnunet-service-cadet_channel.h" - -/******************************************************************************/ -/******************************** API ***********************************/ -/******************************************************************************/ - -/** - * Initialize server subsystem. - * - * @param handle Server handle. - */ -void -GML_init (struct GNUNET_SERVER_Handle *handle); - -/** - * Install server (service) handlers and start listening to clients. - */ -void -GML_start (void); - -/** - * Shutdown server. - */ -void -GML_shutdown (void); - -/** - * Get a channel from a client. - * - * @param c Client to check. - * @param ccn Channel ID, must be local (> 0x800...). - * - * @return non-NULL if channel exists in the clients lists - */ -struct CadetChannel * -GML_channel_get (struct CadetClient *c, - struct GNUNET_CADET_ClientChannelNumber ccn); - -/** - * Add a channel to a client - * - * @param client Client. - * @param ccn Channel ID. - * @param ch Channel. - */ -void -GML_channel_add (struct CadetClient *client, - struct GNUNET_CADET_ClientChannelNumber ccn, - struct CadetChannel *ch); - -/** - * Remove a channel from a client - * - * @param client Client. - * @param ccn Channel ID. - * @param ch Channel. - */ -void -GML_channel_remove (struct CadetClient *client, - struct GNUNET_CADET_ClientChannelNumber ccn, - struct CadetChannel *ch); - -/** - * Get the tunnel's next free local channel ID. - * - * @param c Client. - * - * @return LID of a channel free to use. - */ -struct GNUNET_CADET_ClientChannelNumber -GML_get_next_ccn (struct CadetClient *c); - -/** - * 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 - */ -struct CadetClient * -GML_client_get (struct GNUNET_SERVER_Client *client); - -/** - * Find a client that has opened a port - * - * @param port Port to check. - * - * @return non-NULL if a client has the port. - */ -struct CadetClient * -GML_client_get_by_port (const struct GNUNET_HashCode *port); - -/** - * Deletes a tunnel from a client (either owner or destination). - * - * @param c Client whose tunnel to delete. - * @param ch Channel which should be deleted. - * @param id Channel ID. - */ -void -GML_client_delete_channel (struct CadetClient *c, - struct CadetChannel *ch, - struct GNUNET_CADET_ClientChannelNumber id); - -/** - * Build a local ACK message and send it to a local client, if needed. - * - * If the client was already allowed to send data, do nothing. - * - * @param c Client to whom send the ACK. - * @param id Channel ID to use - */ -void -GML_send_ack (struct CadetClient *c, - struct GNUNET_CADET_ClientChannelNumber id); - -/** - * Notify the appropriate client that a new incoming channel was created. - * - * @param c Client to notify. - * @param id Channel ID. - * @param port Channel's destination port. - * @param opt Options (bit array). - * @param peer Origin peer. - */ -void -GML_send_channel_create (struct CadetClient *c, - struct GNUNET_CADET_ClientChannelNumber id, - const struct GNUNET_HashCode *port, - uint32_t opt, - const struct GNUNET_PeerIdentity *peer); - -/** - * Build a local channel NACK message and send it to a local client. - * - * @param c Client to whom send the NACK. - * @param id Channel ID to use - */ -void -GML_send_channel_nack (struct CadetClient *c, - struct GNUNET_CADET_ClientChannelNumber id); - - -/** - * Notify a client that a channel is no longer valid. - * - * @param c Client. - * @param id ID of the channel that is destroyed. - */ -void -GML_send_channel_destroy (struct CadetClient *c, - struct GNUNET_CADET_ClientChannelNumber id); - - -/** - * Modify the cadet message ID from global to local and send to client. - * - * @param c Client to send to. - * @param msg Message to modify and send. - * @param id Channel ID to use (c can be both owner and client). - */ -void -GML_send_data (struct CadetClient *c, - const struct GNUNET_CADET_ChannelAppDataMessage *msg, - struct GNUNET_CADET_ClientChannelNumber id); - -/** - * Get the static string to represent a client. - * - * @param c Client. - * - * @return Static string for the client. - */ -const char * -GML_2s (const struct CadetClient *c); - - -#if 0 /* keep Emacsens' auto-indent happy */ -{ -#endif -#ifdef __cplusplus -} -#endif - -/* ifndef GNUNET_CADET_SERVICE_LOCAL_H */ -#endif -/* end of gnunet-cadet-service_LOCAL.h */ diff --git a/src/cadet/gnunet-service-cadet-new_paths.c b/src/cadet/gnunet-service-cadet_paths.c index c6121a1336..13752643c3 100644 --- a/src/cadet/gnunet-service-cadet-new_paths.c +++ b/src/cadet/gnunet-service-cadet_paths.c @@ -18,16 +18,16 @@ Boston, MA 02110-1301, USA. */ /** - * @file cadet/gnunet-service-cadet-new_paths.c + * @file cadet/gnunet-service-cadet_paths.c * @brief Information we track per path. * @author Bartlomiej Polot * @author Christian Grothoff */ #include "platform.h" -#include "gnunet-service-cadet-new_connection.h" -#include "gnunet-service-cadet-new_tunnels.h" -#include "gnunet-service-cadet-new_peer.h" -#include "gnunet-service-cadet-new_paths.h" +#include "gnunet-service-cadet_connection.h" +#include "gnunet-service-cadet_tunnels.h" +#include "gnunet-service-cadet_peer.h" +#include "gnunet-service-cadet_paths.h" #define LOG(level, ...) GNUNET_log_from(level,"cadet-pat",__VA_ARGS__) diff --git a/src/cadet/gnunet-service-cadet-new_paths.h b/src/cadet/gnunet-service-cadet_paths.h index 7310d75e64..6b7bef640b 100644 --- a/src/cadet/gnunet-service-cadet-new_paths.h +++ b/src/cadet/gnunet-service-cadet_paths.h @@ -29,7 +29,7 @@ #define GNUNET_SERVICE_CADET_PATHS_H #include "gnunet_util_lib.h" -#include "gnunet-service-cadet-new.h" +#include "gnunet-service-cadet.h" /** * Create a peer path based on the result of a DHT lookup. If we diff --git a/src/cadet/gnunet-service-cadet_peer.c b/src/cadet/gnunet-service-cadet_peer.c index fa3f2be806..71c7c67d0c 100644 --- a/src/cadet/gnunet-service-cadet_peer.c +++ b/src/cadet/gnunet-service-cadet_peer.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2013, 2015 GNUnet e.V. + Copyright (C) 2001-2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -17,2193 +17,1461 @@ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + /** * @file cadet/gnunet-service-cadet_peer.c - * @brief GNUnet CADET service connection handling + * @brief Information we track per peer. * @author Bartlomiej Polot + * @author Christian Grothoff + * + * TODO: + * - optimize stopping/restarting DHT search to situations + * where we actually need it (i.e. not if we have a direct connection, + * or if we already have plenty of good short ones, or maybe even + * to take a break if we have some connections and have searched a lot (?)) */ #include "platform.h" #include "gnunet_util_lib.h" +#include "gnunet_hello_lib.h" #include "gnunet_signatures.h" #include "gnunet_transport_service.h" #include "gnunet_ats_service.h" #include "gnunet_core_service.h" #include "gnunet_statistics_service.h" #include "cadet_protocol.h" -#include "gnunet-service-cadet_peer.h" -#include "gnunet-service-cadet_dht.h" #include "gnunet-service-cadet_connection.h" -#include "gnunet-service-cadet_tunnel.h" -#include "cadet_path.h" +#include "gnunet-service-cadet_dht.h" +#include "gnunet-service-cadet_peer.h" +#include "gnunet-service-cadet_paths.h" +#include "gnunet-service-cadet_tunnels.h" -#define LOG(level, ...) GNUNET_log_from (level,"cadet-p2p",__VA_ARGS__) -#define LOG2(level, ...) GNUNET_log_from_nocheck(level,"cadet-p2p",__VA_ARGS__) +#define LOG(level, ...) GNUNET_log_from(level,"cadet-per",__VA_ARGS__) -/******************************************************************************/ -/******************************** STRUCTS **********************************/ -/******************************************************************************/ /** - * Information about a queued message on the peer level. + * How long do we wait until tearing down an idle peer? */ -struct CadetPeerQueue { - - struct CadetPeerQueue *next; - struct CadetPeerQueue *prev; - - /** - * Envelope to cancel message before MQ sends it. - */ - struct GNUNET_MQ_Envelope *env; - - /** - * Peer (neighbor) this message is being sent to. - */ - struct CadetPeer *peer; - - /** - * Continuation to call to notify higher layers about message sent. - */ - GCP_sent cont; - - /** - * Closure for @a cont. - */ - void *cont_cls; - - /** - * Task to asynchronously run the drop continuation. - */ - struct GNUNET_SCHEDULER_Task *drop_task; - - /** - * Time when message was queued for sending. - */ - struct GNUNET_TIME_Absolute queue_timestamp; - - /** - * #GNUNET_YES if message was management traffic (POLL, ACK, ...). - */ - int management_traffic; - - /** - * Message type. - */ - uint16_t type; - - /** - * Message size. - */ - uint16_t size; - - /** - * Type of the message's payload, if it was encrypted data. - */ - uint16_t payload_type; - - /** - * ID of the payload (PID, ACK #, ...). - */ - struct CadetEncryptedMessageIdentifier payload_id; - - /** - * Connection this message was sent on. - */ - struct CadetConnection *c; - - /** - * Direction in @a c this message was send on (#GNUNET_YES = FWD). - */ - int c_fwd; -}; - +#define IDLE_PEER_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 5) /** - * Struct containing all information regarding a given peer + * How long do we keep paths around if we no longer care about the peer? */ -struct CadetPeer -{ - /** - * ID of the peer - */ - GNUNET_PEER_Id id; - - struct CadetPeerQueue *q_head; - struct CadetPeerQueue *q_tail; - - /** - * Last time we heard from this peer - */ - struct GNUNET_TIME_Absolute last_contact; - - /** - * Paths to reach the peer, ordered by ascending hop count - */ - struct CadetPeerPath *path_head; - - /** - * Paths to reach the peer, ordered by ascending hop count - */ - struct CadetPeerPath *path_tail; - - /** - * Handle to stop the DHT search for paths to this peer - */ - struct GCD_search_handle *search_h; - - /** - * Handle to stop the DHT search for paths to this peer - */ - struct GNUNET_SCHEDULER_Task *search_delayed; - - /** - * Tunnel to this peer, if any. - */ - struct CadetTunnel *tunnel; - - /** - * Connections that go through this peer; indexed by tid. - */ - struct GNUNET_CONTAINER_MultiShortmap *connections; - - /** - * Handle for core transmissions. - */ - struct GNUNET_MQ_Handle *core_mq; - - /** - * How many messages are in the queue to this peer. - */ - unsigned int queue_n; - - /** - * Hello message. - */ - struct GNUNET_HELLO_Message* hello; - - /** - * Handle to us offering the HELLO to the transport. - */ - struct GNUNET_TRANSPORT_OfferHelloHandle *hello_offer; - - /** - * Handle to our ATS request asking ATS to suggest an address - * to TRANSPORT for this peer (to establish a direct link). - */ - struct GNUNET_ATS_ConnectivitySuggestHandle *connectivity_suggestion; +#define IDLE_PATH_TIMEOUT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MINUTES, 2) -}; - - -/******************************************************************************/ -/******************************* GLOBALS ***********************************/ -/******************************************************************************/ - -/** - * Global handle to the statistics service. - */ -extern struct GNUNET_STATISTICS_Handle *stats; -/** - * Local peer own ID (full value). - */ -extern struct GNUNET_PeerIdentity my_full_id; -/** - * Local peer own ID (short) - */ -extern GNUNET_PEER_Id myid; /** - * Peers known, indexed by PeerIdentity, values of type `struct CadetPeer`. + * Data structure used to track whom we have to notify about changes + * to our message queue. */ -static struct GNUNET_CONTAINER_MultiPeerMap *peers; - -/** - * How many peers do we want to remember? - */ -static unsigned long long max_peers; +struct GCP_MessageQueueManager +{ -/** - * Percentage of messages that will be dropped (for test purposes only). - */ -static unsigned long long drop_percent; + /** + * Kept in a DLL. + */ + struct GCP_MessageQueueManager *next; -/** - * Handle to communicate with CORE. - */ -static struct GNUNET_CORE_Handle *core_handle; + /** + * Kept in a DLL. + */ + struct GCP_MessageQueueManager *prev; -/** - * Our configuration; - */ -static const struct GNUNET_CONFIGURATION_Handle *cfg; + /** + * Function to call with updated message queue object. + */ + GCP_MessageQueueNotificationCallback cb; -/** - * Handle to communicate with ATS. - */ -static struct GNUNET_ATS_ConnectivityHandle *ats_ch; + /** + * Closure for @e cb. + */ + void *cb_cls; -/** - * Shutdown falg. - */ -static int in_shutdown; + /** + * The peer this is for. + */ + struct CadetPeer *cp; + /** + * Envelope this manager would like to transmit once it is its turn. + */ + struct GNUNET_MQ_Envelope *env; -/******************************************************************************/ -/***************************** CORE HELPERS *********************************/ -/******************************************************************************/ +}; /** - * Iterator to notify all connections of a broken link. Mark connections - * to destroy after all traffic has been sent. - * - * @param cls Closure (disconnected peer). - * @param key Current key code (peer id). - * @param value Value in the hash map (connection). - * - * @return #GNUNET_YES to continue to iterate. + * Struct containing all information regarding a given peer */ -static int -notify_broken (void *cls, - const struct GNUNET_ShortHashCode *key, - void *value) +struct CadetPeer { - struct CadetPeer *peer = cls; - struct CadetConnection *c = value; + /** + * ID of the peer + */ + struct GNUNET_PeerIdentity pid; + + /** + * Last time we heard from this peer (currently not used!) + */ + struct GNUNET_TIME_Absolute last_contactXXX; + + /** + * Array of DLLs of paths traversing the peer, organized by the + * offset of the peer on the larger path. + */ + struct CadetPeerPathEntry **path_heads; + + /** + * Array of DLL of paths traversing the peer, organized by the + * offset of the peer on the larger path. + */ + struct CadetPeerPathEntry **path_tails; + + /** + * Notifications to call when @e core_mq changes. + */ + struct GCP_MessageQueueManager *mqm_head; + + /** + * Notifications to call when @e core_mq changes. + */ + struct GCP_MessageQueueManager *mqm_tail; + + /** + * Pointer to first "ready" entry in @e mqm_head. + */ + struct GCP_MessageQueueManager *mqm_ready_ptr; + + /** + * MIN-heap of paths owned by this peer (they also end at this + * peer). Ordered by desirability. + */ + struct GNUNET_CONTAINER_Heap *path_heap; + + /** + * Handle to stop the DHT search for paths to this peer + */ + struct GCD_search_handle *search_h; + + /** + * Task to clean up @e path_heap asynchronously. + */ + struct GNUNET_SCHEDULER_Task *heap_cleanup_task; + + /** + * Task to destroy this entry. + */ + struct GNUNET_SCHEDULER_Task *destroy_task; + + /** + * Tunnel to this peer, if any. + */ + struct CadetTunnel *t; + + /** + * Connections that go through this peer; indexed by tid. + */ + struct GNUNET_CONTAINER_MultiShortmap *connections; + + /** + * Handle for core transmissions. + */ + struct GNUNET_MQ_Handle *core_mq; + + /** + * Hello message of the peer. + */ + struct GNUNET_HELLO_Message *hello; + + /** + * Handle to us offering the HELLO to the transport. + */ + struct GNUNET_TRANSPORT_OfferHelloHandle *hello_offer; + + /** + * Handle to our ATS request asking ATS to suggest an address + * to TRANSPORT for this peer (to establish a direct link). + */ + struct GNUNET_ATS_ConnectivitySuggestHandle *connectivity_suggestion; + + /** + * How many messages are in the queue to this peer. + */ + unsigned int queue_n; + + /** + * How many paths do we have to this peer (in all @e path_heads DLLs combined). + */ + unsigned int num_paths; + + /** + * Sum over all of the offsets of all of the paths in the @a path_heads DLLs. + * Used to speed-up @GCP_get_desirability_of_path() calculation. + */ + unsigned int off_sum; + + /** + * Number of message queue managers of this peer that have a message in waiting. + * + * Used to quickly see if we need to bother scanning the @e msm_head DLL. + * TODO: could be replaced by another DLL that would then allow us to avoid + * the O(n)-scan of the DLL for ready entries! + */ + unsigned int mqm_ready_counter; + + /** + * Current length of the @e path_heads and @path_tails arrays. + * The arrays should be grown as needed. + */ + unsigned int path_dll_length; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Notifying %s due to %s disconnect\n", - GCC_2s (c), GCP_2s (peer)); - GCC_neighbor_disconnected (c, peer); - return GNUNET_YES; -} +}; /** - * Remove the direct path to the peer. - * - * @param peer Peer to remove the direct path from. - */ -static struct CadetPeerPath * -pop_direct_path (struct CadetPeer *peer) -{ - struct CadetPeerPath *iter; - - for (iter = peer->path_head; NULL != iter; iter = iter->next) - { - if (2 >= iter->length) - { - GNUNET_CONTAINER_DLL_remove (peer->path_head, - peer->path_tail, - iter); - return iter; - } - } - return NULL; -} - -/** - * Call the continuation after a message has been sent or dropped. - * - * This funcion removes the message from the queue. + * Get the static string for a peer ID. * - * @param q Queue handle. - * @param sent #GNUNET_YES if was sent to CORE, #GNUNET_NO if dropped. + * @param cp Peer. + * @return Static string for it's ID. */ -static void -call_peer_cont (struct CadetPeerQueue *q, int sent); +const char * +GCP_2s (const struct CadetPeer *cp) +{ + static char buf[32]; + + GNUNET_snprintf (buf, + sizeof (buf), + "P(%s)", + GNUNET_i2s (&cp->pid)); + return buf; +} + + +/** + * Calculate how desirable a path is for @a cp if @a cp + * is at offset @a off. + * + * The 'desirability_table.c' program can be used to compute a list of + * sample outputs for different scenarios. Basically, we score paths + * lower if there are many alternatives, and higher if they are + * shorter than average, and very high if they are much shorter than + * average and without many alternatives. + * + * @param cp a peer reachable via a path + * @param off offset of @a cp in the path + * @return score how useful a path is to reach @a cp, + * positive scores mean path is more desirable + */ +double +GCP_get_desirability_of_path (struct CadetPeer *cp, + unsigned int off) +{ + unsigned int num_alts = cp->num_paths; + unsigned int off_sum; + double avg_sum; + double path_delta; + double weight_alts; + + GNUNET_assert (num_alts >= 1); /* 'path' should be in there! */ + GNUNET_assert (0 != cp->path_dll_length); + + /* We maintain 'off_sum' in 'peer' and thereby + avoid the SLOW recalculation each time. Kept here + just to document what is going on. */ +#if SLOW + off_sum = 0; + for (unsigned int j=0;j<cp->path_dll_length;j++) + for (struct CadetPeerPathEntry *pe = cp->path_heads[j]; + NULL != pe; + pe = pe->next) + off_sum += j; + GNUNET_assert (off_sum == cp->off_sum); +#else + off_sum = cp->off_sum; +#endif + avg_sum = off_sum * 1.0 / cp->path_dll_length; + path_delta = off - avg_sum; + /* path_delta positiv: path off of peer above average (bad path for peer), + path_delta negativ: path off of peer below average (good path for peer) */ + if (path_delta <= - 1.0) + weight_alts = - num_alts / path_delta; /* discount alternative paths */ + else if (path_delta >= 1.0) + weight_alts = num_alts * path_delta; /* overcount alternative paths */ + else + weight_alts = num_alts; /* count alternative paths normally */ -/******************************************************************************/ -/***************************** CORE CALLBACKS *********************************/ -/******************************************************************************/ + /* off+1: long paths are generally harder to find and thus count + a bit more as they get longer. However, above-average paths + still need to count less, hence the squaring of that factor. */ + return (off + 1.0) / (weight_alts * weight_alts); +} /** - * Method called whenever a given peer connects. + * This peer is no longer be needed, clean it up now. * - * @param cls Core closure (unused). - * @param peer Peer identity this notification is about - * @param mq Message Queue to this peer. - * - * @return Internal closure for handlers (CadetPeer struct). + * @param cls peer to clean up */ -static void * -core_connect_handler (void *cls, - const struct GNUNET_PeerIdentity *peer, - struct GNUNET_MQ_Handle *mq) -{ - struct CadetPeer *neighbor; - struct CadetPeerPath *path; - char own_id[16]; - - GCC_check_connections (); - GNUNET_snprintf (own_id, - sizeof (own_id), - "%s", - GNUNET_i2s (&my_full_id)); - - /* Save a path to the neighbor */ - neighbor = GCP_get (peer, GNUNET_YES); - if (myid == neighbor->id) - { - LOG (GNUNET_ERROR_TYPE_INFO, - "CONNECTED %s (self)\n", - own_id); - path = path_new (1); - } - else - { - LOG (GNUNET_ERROR_TYPE_INFO, - "CONNECTED %s <= %s\n", - own_id, - GNUNET_i2s (peer)); - path = path_new (2); - path->peers[1] = neighbor->id; - GNUNET_PEER_change_rc (neighbor->id, 1); - GNUNET_assert (NULL == neighbor->core_mq); - neighbor->core_mq = mq; - } - path->peers[0] = myid; - GNUNET_PEER_change_rc (myid, 1); - GCP_add_path (neighbor, path, GNUNET_YES); - - /* Create the connections hashmap */ - GNUNET_assert (NULL == neighbor->connections); - neighbor->connections = GNUNET_CONTAINER_multishortmap_create (16, - GNUNET_YES); - GNUNET_STATISTICS_update (stats, - "# peers", - 1, - GNUNET_NO); - - if ( (NULL != GCP_get_tunnel (neighbor)) && - (0 > GNUNET_CRYPTO_cmp_peer_identity (&my_full_id, peer)) ) - { - GCP_connect (neighbor); - } - GCC_check_connections (); +static void +destroy_peer (void *cls) +{ + struct CadetPeer *cp = cls; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Destroying state about peer %s\n", + GCP_2s (cp)); + cp->destroy_task = NULL; + GNUNET_assert (NULL == cp->t); + GNUNET_assert (NULL == cp->core_mq); + GNUNET_assert (0 == cp->num_paths); + for (unsigned int i=0;i<cp->path_dll_length;i++) + GNUNET_assert (NULL == cp->path_heads[i]); + GNUNET_assert (0 == GNUNET_CONTAINER_multishortmap_size (cp->connections)); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (peers, + &cp->pid, + cp)); + GNUNET_free_non_null (cp->path_heads); + GNUNET_free_non_null (cp->path_tails); + cp->path_dll_length = 0; + if (NULL != cp->search_h) + { + GCD_search_stop (cp->search_h); + cp->search_h = NULL; + } + /* FIXME: clean up search_delayedXXX! */ - return neighbor; + if (NULL != cp->hello_offer) + { + GNUNET_TRANSPORT_offer_hello_cancel (cp->hello_offer); + cp->hello_offer = NULL; + } + if (NULL != cp->connectivity_suggestion) + { + GNUNET_ATS_connectivity_suggest_cancel (cp->connectivity_suggestion); + cp->connectivity_suggestion = NULL; + } + GNUNET_CONTAINER_multishortmap_destroy (cp->connections); + if (NULL != cp->path_heap) + { + GNUNET_CONTAINER_heap_destroy (cp->path_heap); + cp->path_heap = NULL; + } + if (NULL != cp->heap_cleanup_task) + { + GNUNET_SCHEDULER_cancel (cp->heap_cleanup_task); + cp->heap_cleanup_task = NULL; + } + GNUNET_free_non_null (cp->hello); + /* Peer should not be freed if paths exist; if there are no paths, + there ought to be no connections, and without connections, no + notifications. Thus we can assert that mqm_head is empty at this + point. */ + GNUNET_assert (NULL == cp->mqm_head); + GNUNET_assert (NULL == cp->mqm_ready_ptr); + GNUNET_free (cp); } /** - * Method called whenever a peer disconnects. + * This peer is now on more "active" duty, activate processes related to it. * - * @param cls Core closure (unused). - * @param peer Peer identity this notification is about. - * @param internal_cls Internal closure (CadetPeer struct). + * @param cp the more-active peer */ static void -core_disconnect_handler (void *cls, - const struct GNUNET_PeerIdentity *peer, - void *internal_cls) +consider_peer_activate (struct CadetPeer *cp) { - struct CadetPeer *p = internal_cls; - struct CadetPeerPath *direct_path; - char own_id[16]; - - GCC_check_connections (); - strncpy (own_id, GNUNET_i2s (&my_full_id), 16); - own_id[15] = '\0'; - if (myid == p->id) - { - LOG (GNUNET_ERROR_TYPE_INFO, - "DISCONNECTED %s (self)\n", - own_id); - } - else + uint32_t strength; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Updating peer %s activation state (%u connections)%s%s\n", + GCP_2s (cp), + GNUNET_CONTAINER_multishortmap_size (cp->connections), + (NULL == cp->t) ? "" : " with tunnel", + (NULL == cp->core_mq) ? "" : " with CORE link"); + if (NULL != cp->destroy_task) + { + /* It's active, do not destory! */ + GNUNET_SCHEDULER_cancel (cp->destroy_task); + cp->destroy_task = NULL; + } + if ( (0 == GNUNET_CONTAINER_multishortmap_size (cp->connections)) && + (NULL == cp->t) ) + { + /* We're just on a path or directly connected; don't bother too much */ + if (NULL != cp->connectivity_suggestion) { - LOG (GNUNET_ERROR_TYPE_INFO, - "DISCONNECTED %s <= %s\n", - own_id, GNUNET_i2s (peer)); - p->core_mq = NULL; + GNUNET_ATS_connectivity_suggest_cancel (cp->connectivity_suggestion); + cp->connectivity_suggestion = NULL; } - direct_path = pop_direct_path (p); - if (NULL != p->connections) + if (NULL != cp->search_h) { - GNUNET_CONTAINER_multishortmap_iterate (p->connections, - ¬ify_broken, - p); - GNUNET_CONTAINER_multishortmap_destroy (p->connections); - p->connections = NULL; + GCD_search_stop (cp->search_h); + cp->search_h = NULL; } - GNUNET_STATISTICS_update (stats, - "# peers", - -1, - GNUNET_NO); - path_destroy (direct_path); - GCC_check_connections (); -} - - -/******************************************************************************/ -/******************************************************************************/ -/******************************************************************************/ -/******************************************************************************/ -/******************************************************************************/ - -/** - * Check if the create_connection message has the appropriate size. - * - * @param cls Closure (unused). - * @param msg Message to check. - * - * @return #GNUNET_YES if size is correct, #GNUNET_NO otherwise. - */ -static int -check_create (void *cls, const struct GNUNET_CADET_ConnectionCreateMessage *msg) -{ - uint16_t size; - - size = ntohs (msg->header.size); - if (size < sizeof (*msg)) + return; + } + if (NULL == cp->core_mq) + { + /* Lacks direct connection, try to create one by querying the DHT */ + if ( (NULL == cp->search_h) && + (DESIRED_CONNECTIONS_PER_TUNNEL > cp->num_paths) ) + cp->search_h + = GCD_search (&cp->pid); + } + else + { + /* Have direct connection, stop DHT search if active */ + if (NULL != cp->search_h) { - GNUNET_break_op (0); - return GNUNET_NO; + GCD_search_stop (cp->search_h); + cp->search_h = NULL; } - return GNUNET_YES; -} - -/** - * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE - * - * @param cls Closure (CadetPeer for neighbor that sent the message). - * @param msg Message itself. - */ -static void -handle_create (void *cls, const struct GNUNET_CADET_ConnectionCreateMessage *msg) -{ - struct CadetPeer *peer = cls; - GCC_handle_create (peer, msg); -} - - -/** - * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK - * - * @param cls Closure (CadetPeer for neighbor that sent the message). - * @param msg Message itself. - */ -static void -handle_confirm (void *cls, const struct GNUNET_CADET_ConnectionCreateAckMessage *msg) -{ - struct CadetPeer *peer = cls; - GCC_handle_confirm (peer, msg); -} - + } -/** - * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN - * - * @param cls Closure (CadetPeer for neighbor that sent the message). - * @param msg Message itself. - */ -static void -handle_broken (void *cls, const struct GNUNET_CADET_ConnectionBrokenMessage *msg) -{ - struct CadetPeer *peer = cls; - GCC_handle_broken (peer, msg); + /* If we have a tunnel, our urge for connections is much bigger */ + strength = (NULL != cp->t) ? 32 : 1; + if (NULL != cp->connectivity_suggestion) + GNUNET_ATS_connectivity_suggest_cancel (cp->connectivity_suggestion); + cp->connectivity_suggestion + = GNUNET_ATS_connectivity_suggest (ats_ch, + &cp->pid, + strength); } /** - * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY + * This peer may no longer be needed, consider cleaning it up. * - * @param cls Closure (CadetPeer for neighbor that sent the message). - * @param msg Message itself. + * @param cp peer to clean up */ static void -handle_destroy (void *cls, const struct GNUNET_CADET_ConnectionDestroyMessage *msg) -{ - struct CadetPeer *peer = cls; - GCC_handle_destroy (peer, msg); -} +consider_peer_destroy (struct CadetPeer *cp); /** - * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_HOP_BY_HOP_ENCRYPTED_ACK + * We really no longere care about a peer, stop hogging memory with paths to it. + * Afterwards, see if there is more to be cleaned up about this peer. * - * @param cls Closure (CadetPeer for neighbor that sent the message). - * @param msg Message itself. + * @param cls a `struct CadetPeer`. */ static void -handle_ack (void *cls, const struct GNUNET_CADET_ConnectionEncryptedAckMessage *msg) +drop_paths (void *cls) { - struct CadetPeer *peer = cls; - GCC_handle_ack (peer, msg); -} + struct CadetPeer *cp = cls; + struct CadetPeerPath *path; - -/** - * Handle for #GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED_POLL - * - * @param cls Closure (CadetPeer for neighbor that sent the message). - * @param msg Message itself. - */ -static void -handle_poll (void *cls, const struct GNUNET_CADET_ConnectionHopByHopPollMessage *msg) -{ - struct CadetPeer *peer = cls; - GCC_handle_poll (peer, msg); + cp->destroy_task = NULL; + while (NULL != (path = GNUNET_CONTAINER_heap_remove_root (cp->path_heap))) + GCPP_release (path); + consider_peer_destroy (cp); } /** - * Handle for #GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX + * This peer may no longer be needed, consider cleaning it up. * - * @param cls Closure (CadetPeer for neighbor that sent the message). - * @param msg Message itself. + * @param cp peer to clean up */ static void -handle_kx (void *cls, const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg) -{ - struct CadetPeer *peer = cls; - GCC_handle_kx (peer, msg); -} - - -/** - * Check if the encrypted message has the appropriate size. - * - * @param cls Closure (unused). - * @param msg Message to check. - * - * @return #GNUNET_YES if size is correct, #GNUNET_NO otherwise. - */ -static int -check_encrypted (void *cls, const struct GNUNET_CADET_TunnelEncryptedMessage *msg) +consider_peer_destroy (struct CadetPeer *cp) { - uint16_t size; - uint16_t minimum_size; - - size = ntohs (msg->header.size); - minimum_size = sizeof (struct GNUNET_CADET_TunnelEncryptedMessage) - + sizeof (struct GNUNET_MessageHeader); - - if (size < minimum_size) - { - GNUNET_break_op (0); - return GNUNET_NO; - } - return GNUNET_YES; -} + struct GNUNET_TIME_Relative exp; -/** - * Handle for #GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED. - * - * @param cls Closure (CadetPeer for neighbor that sent the message). - * @param msg Message itself. - */ -static void -handle_encrypted (void *cls, const struct GNUNET_CADET_TunnelEncryptedMessage *msg) -{ - struct CadetPeer *peer = cls; - GCC_handle_encrypted (peer, msg); -} - - -/** - * To be called on core init/fail. - * - * @param cls Closure (config) - * @param identity The public identity of this peer. - */ -static void -core_init_notify (void *cls, - const struct GNUNET_PeerIdentity *identity); - - -static void -connect_to_core (const struct GNUNET_CONFIGURATION_Handle *c) -{ - struct GNUNET_MQ_MessageHandler core_handlers[] = { - GNUNET_MQ_hd_var_size (create, - GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE, - struct GNUNET_CADET_ConnectionCreateMessage, - NULL), - GNUNET_MQ_hd_fixed_size (confirm, - GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK, - struct GNUNET_CADET_ConnectionCreateAckMessage, - NULL), - GNUNET_MQ_hd_fixed_size (broken, - GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN, - struct GNUNET_CADET_ConnectionBrokenMessage, - NULL), - GNUNET_MQ_hd_fixed_size (destroy, - GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY, - struct GNUNET_CADET_ConnectionDestroyMessage, - NULL), - GNUNET_MQ_hd_fixed_size (ack, - GNUNET_MESSAGE_TYPE_CADET_CONNECTION_HOP_BY_HOP_ENCRYPTED_ACK, - struct GNUNET_CADET_ConnectionEncryptedAckMessage, - NULL), - GNUNET_MQ_hd_fixed_size (poll, - GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED_POLL, - struct GNUNET_CADET_ConnectionHopByHopPollMessage, - NULL), - GNUNET_MQ_hd_fixed_size (kx, - GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX, - struct GNUNET_CADET_TunnelKeyExchangeMessage, - NULL), - GNUNET_MQ_hd_var_size (encrypted, - GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED, - struct GNUNET_CADET_TunnelEncryptedMessage, - NULL), - GNUNET_MQ_handler_end () - }; - core_handle = GNUNET_CORE_connect (c, NULL, - &core_init_notify, - &core_connect_handler, - &core_disconnect_handler, - core_handlers); -} - -/******************************************************************************/ -/******************************************************************************/ -/******************************************************************************/ -/******************************************************************************/ -/******************************************************************************/ - -/** - * To be called on core init/fail. - * - * @param cls Closure (config) - * @param identity The public identity of this peer. - */ -static void -core_init_notify (void *cls, - const struct GNUNET_PeerIdentity *core_identity) -{ - const struct GNUNET_CONFIGURATION_Handle *c = cls; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Core init\n"); - if (0 != memcmp (core_identity, &my_full_id, sizeof (my_full_id))) - { - LOG (GNUNET_ERROR_TYPE_ERROR, _("Wrong CORE service\n")); - LOG (GNUNET_ERROR_TYPE_ERROR, " core id %s\n", GNUNET_i2s (core_identity)); - LOG (GNUNET_ERROR_TYPE_ERROR, " my id %s\n", GNUNET_i2s (&my_full_id)); - GNUNET_CORE_disconnect (core_handle); - connect_to_core (c); - return; - } - GML_start (); + if (NULL != cp->destroy_task) + { + GNUNET_SCHEDULER_cancel (cp->destroy_task); + cp->destroy_task = NULL; + } + if (NULL != cp->t) + return; /* still relevant! */ + if (NULL != cp->core_mq) + return; /* still relevant! */ + if (0 != GNUNET_CONTAINER_multishortmap_size (cp->connections)) + return; /* still relevant! */ + if ( (NULL != cp->path_heap) && + (0 < GNUNET_CONTAINER_heap_get_size (cp->path_heap)) ) + { + cp->destroy_task = GNUNET_SCHEDULER_add_delayed (IDLE_PATH_TIMEOUT, + &drop_paths, + cp); + return; + } + if (0 != cp->num_paths) + return; /* still relevant! */ + if (NULL != cp->hello) + { + /* relevant only until HELLO expires */ + exp = GNUNET_TIME_absolute_get_remaining (GNUNET_HELLO_get_last_expiration (cp->hello)); + cp->destroy_task = GNUNET_SCHEDULER_add_delayed (exp, + &destroy_peer, + cp); + return; + } + cp->destroy_task = GNUNET_SCHEDULER_add_delayed (IDLE_PEER_TIMEOUT, + &destroy_peer, + cp); } -/******************************************************************************/ -/******************************** STATIC ***********************************/ -/******************************************************************************/ - - /** - * Get priority for a queued message. - * - * @param q Queued message - * - * @return CORE priority to use. + * Set the message queue to @a mq for peer @a cp and notify watchers. * - * FIXME make static - * FIXME use when sending + * @param cp peer to modify + * @param mq message queue to set (can be NULL) */ -enum GNUNET_CORE_Priority -get_priority (struct CadetPeerQueue *q) -{ - enum GNUNET_CORE_Priority low; - enum GNUNET_CORE_Priority high; - - if (NULL == q) - { - GNUNET_break (0); - return GNUNET_CORE_PRIO_BACKGROUND; - } - - /* Relayed traffic has lower priority, our own traffic has higher */ - if (NULL == q->c || GNUNET_NO == GCC_is_origin (q->c, q->c_fwd)) +void +GCP_set_mq (struct CadetPeer *cp, + struct GNUNET_MQ_Handle *mq) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Message queue for peer %s is now %p\n", + GCP_2s (cp), + mq); + cp->core_mq = mq; + for (struct GCP_MessageQueueManager *mqm = cp->mqm_head, *next; + NULL != mqm; + mqm = next) + { + /* Save next pointer in case mqm gets freed by the callback */ + next = mqm->next; + if (NULL == mq) { - low = GNUNET_CORE_PRIO_BEST_EFFORT; - high = GNUNET_CORE_PRIO_URGENT; + if (NULL != mqm->env) + { + GNUNET_MQ_discard (mqm->env); + mqm->env = NULL; + mqm->cb (mqm->cb_cls, + GNUNET_SYSERR); + } + else + { + mqm->cb (mqm->cb_cls, + GNUNET_NO); + } } else { - low = GNUNET_CORE_PRIO_URGENT; - high = GNUNET_CORE_PRIO_CRITICAL_CONTROL; + GNUNET_assert (NULL == mqm->env); + mqm->cb (mqm->cb_cls, + GNUNET_YES); } + } + if ( (NULL != mq) || + (NULL != cp->t) ) + consider_peer_activate (cp); + else + consider_peer_destroy (cp); - /* Bulky payload has lower priority, control traffic has higher. */ - if (GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED == q->type) - return low; - return high; -} - - -/** - * Cancel all messages queued to CORE MQ towards this peer. - * - * @param peer Peer towards which to cancel all messages. - */ -static void -cancel_queued_messages (struct CadetPeer *peer) -{ - while (NULL != peer->q_head) - { - struct CadetPeerQueue *q; - - q = peer->q_head; - call_peer_cont (q, GNUNET_NO); - GNUNET_free (q); - } -} - - -/** - * Destroy the peer_info and free any allocated resources linked to it - * - * @param peer The peer_info to destroy. - * @return #GNUNET_OK on success - */ -static int -peer_destroy (struct CadetPeer *peer) -{ - struct GNUNET_PeerIdentity id; - struct CadetPeerPath *p; - struct CadetPeerPath *nextp; - - GNUNET_PEER_resolve (peer->id, &id); - GNUNET_PEER_change_rc (peer->id, -1); - - LOG (GNUNET_ERROR_TYPE_INFO, - "destroying peer %s\n", - GNUNET_i2s (&id)); - - if (GNUNET_YES != - GNUNET_CONTAINER_multipeermap_remove (peers, &id, peer)) - { - GNUNET_break (0); - LOG (GNUNET_ERROR_TYPE_WARNING, " peer not in peermap!!\n"); - } - GCP_stop_search (peer); - p = peer->path_head; - while (NULL != p) - { - nextp = p->next; - GNUNET_CONTAINER_DLL_remove (peer->path_head, - peer->path_tail, - p); - path_destroy (p); - p = nextp; - } - if (NULL != peer->tunnel) - GCT_destroy_empty (peer->tunnel); - if (NULL != peer->connections) - { - GNUNET_assert (0 == GNUNET_CONTAINER_multishortmap_size (peer->connections)); - GNUNET_CONTAINER_multishortmap_destroy (peer->connections); - peer->connections = NULL; - } - if (NULL != peer->hello_offer) - { - GNUNET_TRANSPORT_offer_hello_cancel (peer->hello_offer); - peer->hello_offer = NULL; - } - if (NULL != peer->connectivity_suggestion) - { - GNUNET_ATS_connectivity_suggest_cancel (peer->connectivity_suggestion); - peer->connectivity_suggestion = NULL; - } - cancel_queued_messages (peer); + if ( (NULL != mq) && + (NULL != cp->t) ) + { + /* have a new, direct path to the target, notify tunnel */ + struct CadetPeerPath *path; - GNUNET_free_non_null (peer->hello); - GNUNET_free (peer); - return GNUNET_OK; + path = GCPP_get_path_from_route (1, + &cp->pid); + GCT_consider_path (cp->t, + path, + 0); + } } /** - * Iterator over peer hash map entries to destroy the peer during in_shutdown. + * Debug function should NEVER return true in production code, useful to + * simulate losses for testcases. * - * @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. + * @return #GNUNET_YES or #GNUNET_NO with the decision to drop. */ static int -shutdown_peer (void *cls, - const struct GNUNET_PeerIdentity *key, - void *value) +should_I_drop (void) { - struct CadetPeer *p = value; - struct CadetTunnel *t = p->tunnel; - - LOG (GNUNET_ERROR_TYPE_DEBUG, " shutting down %s\n", GCP_2s (p)); - if (NULL != t) - GCT_destroy (t); - p->tunnel = NULL; - peer_destroy (p); + if (0 == drop_percent) + return GNUNET_NO; + if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, + 101) < drop_percent) return GNUNET_YES; + return GNUNET_NO; } /** - * Check if peer is searching for a path (either active or delayed search). - * - * @param peer Peer to check - * @return #GNUNET_YES if there is a search active. - * #GNUNET_NO otherwise. - */ -static int -is_searching (const struct CadetPeer *peer) -{ - return ( (NULL == peer->search_h) && - (NULL == peer->search_delayed) ) ? - GNUNET_NO : GNUNET_YES; -} - - -/** - * @brief Start a search for a peer. + * Function called when CORE took one of the messages from + * a message queue manager and transmitted it. * - * @param cls Closure (Peer to search for). + * @param cls the `struct CadetPeeer` where we made progress */ static void -delayed_search (void *cls) -{ - struct CadetPeer *peer = cls; - - peer->search_delayed = NULL; - GCC_check_connections (); - GCP_start_search (peer); - GCC_check_connections (); -} +mqm_send_done (void *cls); /** - * Returns if peer is used (has a tunnel or is neighbor). + * Transmit current envelope from this @a mqm. * - * @param peer Peer to check. - * @return #GNUNET_YES if peer is in use. + * @param mqm mqm to transmit message for now */ -static int -peer_is_used (struct CadetPeer *peer) -{ - struct CadetPeerPath *p; - - if (NULL != peer->tunnel) - return GNUNET_YES; - - for (p = peer->path_head; NULL != p; p = p->next) - { - if (p->length < 3) - return GNUNET_YES; - } - return GNUNET_NO; -} - - -/** - * Iterator over all the peers to get the oldest timestamp. - * - * @param cls Closure (unsued). - * @param key ID of the peer. - * @param value Peer_Info of the peer. - */ -static int -peer_get_oldest (void *cls, - const struct GNUNET_PeerIdentity *key, - void *value) +static void +mqm_execute (struct GCP_MessageQueueManager *mqm) { - struct CadetPeer *p = value; - struct GNUNET_TIME_Absolute *abs = cls; - - /* Don't count active peers */ - if (GNUNET_YES == peer_is_used (p)) - return GNUNET_YES; + struct CadetPeer *cp = mqm->cp; - if (abs->abs_value_us < p->last_contact.abs_value_us) - abs->abs_value_us = p->last_contact.abs_value_us; - - return GNUNET_YES; + /* Move ready pointer to the next entry that might be ready. */ + if ( (mqm == cp->mqm_ready_ptr) && + (NULL != mqm->next) ) + cp->mqm_ready_ptr = mqm->next; + /* Move entry to the end of the DLL, to be fair. */ + if (mqm != cp->mqm_tail) + { + GNUNET_CONTAINER_DLL_remove (cp->mqm_head, + cp->mqm_tail, + mqm); + GNUNET_CONTAINER_DLL_insert_tail (cp->mqm_head, + cp->mqm_tail, + mqm); + } + cp->mqm_ready_counter--; + if (GNUNET_YES == should_I_drop ()) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "DROPPING message to peer %s from MQM %p\n", + GCP_2s (cp), + mqm); + GNUNET_MQ_discard (mqm->env); + mqm->env = NULL; + mqm_send_done (cp); + } + else + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending to peer %s from MQM %p\n", + GCP_2s (cp), + mqm); + GNUNET_MQ_send (cp->core_mq, + mqm->env); + mqm->env = NULL; + } + mqm->cb (mqm->cb_cls, + GNUNET_YES); } /** - * Iterator over all the peers to remove the oldest entry. + * Find the next ready message in the queue (starting + * the search from the `cp->mqm_ready_ptr`) and if possible + * execute the transmission. * - * @param cls Closure (unsued). - * @param key ID of the peer. - * @param value Peer_Info of the peer. - */ -static int -peer_timeout (void *cls, - const struct GNUNET_PeerIdentity *key, - void *value) -{ - struct CadetPeer *p = value; - struct GNUNET_TIME_Absolute *abs = cls; - - LOG (GNUNET_ERROR_TYPE_WARNING, - "peer %s timeout\n", GNUNET_i2s (key)); - - if (p->last_contact.abs_value_us == abs->abs_value_us && - GNUNET_NO == peer_is_used (p)) - { - peer_destroy (p); - return GNUNET_NO; - } - return GNUNET_YES; -} - - -/** - * Delete oldest unused peer. + * @param cp peer to try to send the next ready message to */ static void -peer_delete_oldest (void) +send_next_ready (struct CadetPeer *cp) { - struct GNUNET_TIME_Absolute abs; - - abs = GNUNET_TIME_UNIT_FOREVER_ABS; - - GNUNET_CONTAINER_multipeermap_iterate (peers, - &peer_get_oldest, - &abs); - GNUNET_CONTAINER_multipeermap_iterate (peers, - &peer_timeout, - &abs); -} - + struct GCP_MessageQueueManager *mqm; -/** - * Choose the best (yet unused) path towards a peer, - * considering the tunnel properties. - * - * @param peer The destination peer. - * @return Best current known path towards the peer, if any. - */ -static struct CadetPeerPath * -peer_get_best_path (const struct CadetPeer *peer) -{ - struct CadetPeerPath *best_p; - struct CadetPeerPath *p; - unsigned int best_cost; - unsigned int cost; - - best_cost = UINT_MAX; - best_p = NULL; - for (p = peer->path_head; NULL != p; p = p->next) - { - if (GNUNET_NO == path_is_valid (p)) - continue; /* Don't use invalid paths. */ - if (GNUNET_YES == GCT_is_path_used (peer->tunnel, p)) - continue; /* If path is already in use, skip it. */ - - if ((cost = GCT_get_path_cost (peer->tunnel, p)) < best_cost) - { - best_cost = cost; - best_p = p; - } - } - return best_p; + if (0 == cp->mqm_ready_counter) + return; + while ( (NULL != (mqm = cp->mqm_ready_ptr)) && + (NULL == mqm->env) ) + cp->mqm_ready_ptr = mqm->next; + if (NULL == mqm) + return; /* nothing to do */ + mqm_execute (mqm); } /** - * 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. + * Function called when CORE took one of the messages from + * a message queue manager and transmitted it. * - * @param cls Closure (peer towards a path has been found). - * @param path Path created from the DHT query. Will be freed afterwards. + * @param cls the `struct CadetPeeer` where we made progress */ static void -search_handler (void *cls, const struct CadetPeerPath *path) +mqm_send_done (void *cls) { - struct CadetPeer *peer = cls; - unsigned int connection_count; - - GCC_check_connections (); - GCP_add_path_to_all (path, GNUNET_NO); - - /* Count connections */ - connection_count = GCT_count_connections (peer->tunnel); + struct CadetPeer *cp = cls; - /* If we already have our minimum (or more) connections, it's enough */ - if (CONNECTIONS_PER_TUNNEL <= connection_count) - { - GCC_check_connections (); - return; - } - - if (CADET_TUNNEL_SEARCHING == GCT_get_cstate (peer->tunnel)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " ... connect!\n"); - GCP_connect (peer); - } - GCC_check_connections (); -} - - -/** - * Test if a message type is connection management traffic - * or regular payload traffic. - * - * @param type Message type. - * - * @return #GNUNET_YES if connection management, #GNUNET_NO otherwise. - */ -static int -is_connection_management (uint16_t type) -{ - return type == GNUNET_MESSAGE_TYPE_CADET_CONNECTION_HOP_BY_HOP_ENCRYPTED_ACK || - type == GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED_POLL; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending to peer %s completed\n", + GCP_2s (cp)); + send_next_ready (cp); } /** - * Debug function should NEVER return true in production code, useful to - * simulate losses for testcases. + * Send the message in @a env to @a cp. * - * @return #GNUNET_YES or #GNUNET_NO with the decision to drop. + * @param mqm the message queue manager to use for transmission + * @param env envelope with the message to send; must NOT + * yet have a #GNUNET_MQ_notify_sent() callback attached to it */ -static int -should_I_drop (void) +void +GCP_send (struct GCP_MessageQueueManager *mqm, + struct GNUNET_MQ_Envelope *env) { - if (0 == drop_percent) - return GNUNET_NO; + struct CadetPeer *cp = mqm->cp; - if (GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 101) < drop_percent) - return GNUNET_YES; - - return GNUNET_NO; + GNUNET_assert (NULL != env); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Queueing message to peer %s in MQM %p\n", + GCP_2s (cp), + mqm); + GNUNET_assert (NULL != cp->core_mq); + GNUNET_assert (NULL == mqm->env); + GNUNET_MQ_notify_sent (env, + &mqm_send_done, + cp); + mqm->env = env; + cp->mqm_ready_counter++; + if (mqm != cp->mqm_ready_ptr) + cp->mqm_ready_ptr = cp->mqm_head; + if (1 == cp->mqm_ready_counter) + cp->mqm_ready_ptr = mqm; + if (0 != GNUNET_MQ_get_length (cp->core_mq)) + return; + send_next_ready (cp); } -/******************************************************************************/ -/******************************** API ***********************************/ -/******************************************************************************/ - /** - * Call the continuation after a message has been sent or dropped. + * Function called to destroy a peer now. * - * This funcion removes the message from the queue. - * - * @param q Queue handle. - * @param sent #GNUNET_YES if was sent to CORE, #GNUNET_NO if dropped. + * @param cls NULL + * @param pid identity of the peer (unused) + * @param value the `struct CadetPeer` to clean up + * @return #GNUNET_OK (continue to iterate) */ -static void -call_peer_cont (struct CadetPeerQueue *q, int sent) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, " core mq just sent %s\n", GC_m2s (q->type)); - if (NULL != q->cont) - { - struct GNUNET_TIME_Relative wait_time; - - wait_time = GNUNET_TIME_absolute_get_duration (q->queue_timestamp); - LOG (GNUNET_ERROR_TYPE_DEBUG, - " calling callback on %s after %s\n", - GCC_2s (q->c), - GNUNET_STRINGS_relative_time_to_string (wait_time, GNUNET_NO)); - q->cont (q->cont_cls, - q->c, q->c_fwd, sent, - q->type, - q->payload_type, - q->payload_id, - q->size, wait_time); - q->cont = NULL; - } - GNUNET_CONTAINER_DLL_remove (q->peer->q_head, q->peer->q_tail, q); -} - - -/** - * Function called by MQ when a message is sent to CORE. - * - * @param cls Closure (queue handle). - */ -static void -mq_sent (void *cls) +static int +destroy_iterator_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) { - struct CadetPeerQueue *q = cls; + struct CadetPeer *cp = value; - if (GNUNET_NO == q->management_traffic) - { - q->peer->queue_n--; - } - call_peer_cont (q, GNUNET_YES); - GNUNET_free (q); + if (NULL != cp->destroy_task) + { + GNUNET_SCHEDULER_cancel (cp->destroy_task); + cp->destroy_task = NULL; + } + destroy_peer (cp); + return GNUNET_OK; } /** - * Finish the drop operation. - * - * @param cls queue entry to finish drop for + * Clean up all entries about all peers. + * Must only be called after all tunnels, CORE-connections and + * connections are down. */ -static void -drop_cb (void *cls) +void +GCP_destroy_all_peers () { - struct CadetPeerQueue *q = cls; - - GNUNET_MQ_discard (q->env); - call_peer_cont (q, GNUNET_YES); - GNUNET_free (q); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Destroying all peers now\n"); + GNUNET_CONTAINER_multipeermap_iterate (peers, + &destroy_iterator_cb, + NULL); } /** - * @brief Send a message to another peer (using CORE). + * Drop all paths owned by this peer, and do not + * allow new ones to be added: We are shutting down. * - * @param peer Peer towards which to queue the message. - * @param message Message to send. - * @param payload_type Type of the message's payload, for debug messages. - * 0 if the message is a retransmission (unknown payload). - * UINT16_MAX if the message does not have payload. - * @param payload_id ID of the payload (MID, ACK #, etc) - * @param c Connection this message belongs to (can be NULL). - * @param fwd Is this a message going root->dest? (FWD ACK are NOT FWD!) - * @param cont Continuation to be called once CORE has sent the message. - * @param cont_cls Closure for @c cont. - * - * @return A handle to the message in the queue or NULL (if dropped). + * @param cp peer to drop paths to */ -struct CadetPeerQueue * -GCP_send (struct CadetPeer *peer, - const struct GNUNET_MessageHeader *message, - uint16_t payload_type, - struct CadetEncryptedMessageIdentifier payload_id, - struct CadetConnection *c, - int fwd, - GCP_sent cont, - void *cont_cls) +void +GCP_drop_owned_paths (struct CadetPeer *cp) { - struct CadetPeerQueue *q; - uint16_t type; - uint16_t size; - - GCC_check_connections (); - type = ntohs (message->type); - size = ntohs (message->size); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "que %s (%s %4u) on conn %s (%p) %s towards %s (size %u)\n", - GC_m2s (type), GC_m2s (payload_type), - ntohl (payload_id.pid), - GCC_2s (c), c, GC_f2s (fwd), GCP_2s (peer), size); - - if (NULL == peer->connections) - { - /* We are not connected to this peer, ignore request. */ - GNUNET_break (0); - LOG (GNUNET_ERROR_TYPE_INFO, "%s not a neighbor\n", GCP_2s (peer)); - GNUNET_STATISTICS_update (stats, "# messages dropped due to wrong hop", 1, - GNUNET_NO); - return NULL; - } + struct CadetPeerPath *path; - q = GNUNET_new (struct CadetPeerQueue); - q->env = GNUNET_MQ_msg_copy (message); - q->peer = peer; - q->cont = cont; - q->cont_cls = cont_cls; - q->queue_timestamp = GNUNET_TIME_absolute_get (); - q->management_traffic = is_connection_management (type); - q->type = type; - q->size = size; - q->payload_type = payload_type; - q->payload_id = payload_id; - q->c = c; - q->c_fwd = fwd; - GNUNET_MQ_notify_sent (q->env, &mq_sent, q); - GNUNET_CONTAINER_DLL_insert (peer->q_head, peer->q_tail, q); - - if (GNUNET_YES == q->management_traffic) - { - GNUNET_MQ_send (peer->core_mq, q->env); // FIXME implement "_urgent", use - } - else - { - if (GNUNET_YES == should_I_drop ()) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - "DD %s (%s %u) on conn %s %s (random drop for testing)\n", - GC_m2s (q->type), - GC_m2s (q->payload_type), - ntohl (q->payload_id.pid), - GCC_2s (c), - GC_f2s (q->c_fwd)); - q->drop_task = GNUNET_SCHEDULER_add_now (&drop_cb, - q); - return q; - } - GNUNET_MQ_send (peer->core_mq, q->env); - peer->queue_n++; - } - - GCC_check_connections (); - return q; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Destroying all paths to %s\n", + GCP_2s (cp)); + while (NULL != (path = + GNUNET_CONTAINER_heap_remove_root (cp->path_heap))) + GCPP_release (path); + GNUNET_CONTAINER_heap_destroy (cp->path_heap); + cp->path_heap = NULL; } /** - * Cancel sending a message. Message must have been sent with - * #GCP_send before. May not be called after the notify sent - * callback has been called. - * - * It DOES call the continuation given to #GCP_send. + * Add an entry to the DLL of all of the paths that this peer is on. * - * @param q Queue handle to cancel + * @param cp peer to modify + * @param entry an entry on a path + * @param off offset of this peer on the path */ void -GCP_send_cancel (struct CadetPeerQueue *q) -{ - if (NULL != q->drop_task) +GCP_path_entry_add (struct CadetPeer *cp, + struct CadetPeerPathEntry *entry, + unsigned int off) +{ + GNUNET_assert (cp == GCPP_get_peer_at_offset (entry->path, + off)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Discovered that peer %s is on path %s at offset %u\n", + GCP_2s (cp), + GCPP_2s (entry->path), + off); + if (off >= cp->path_dll_length) { - GNUNET_SCHEDULER_cancel (q->drop_task); - q->drop_task = NULL; - GNUNET_MQ_discard (q->env); + unsigned int len = cp->path_dll_length; + + GNUNET_array_grow (cp->path_heads, + len, + off + 4); + GNUNET_array_grow (cp->path_tails, + cp->path_dll_length, + off + 4); } - else + GNUNET_CONTAINER_DLL_insert (cp->path_heads[off], + cp->path_tails[off], + entry); + cp->off_sum += off; + cp->num_paths++; + + /* If we have a tunnel to this peer, tell the tunnel that there is a + new path available. */ + if (NULL != cp->t) + GCT_consider_path (cp->t, + entry->path, + off); + + if ( (NULL != cp->search_h) && + (DESIRED_CONNECTIONS_PER_TUNNEL <= cp->num_paths) ) { - GNUNET_MQ_send_cancel (q->env); + /* Now I have enough paths, stop search */ + GCD_search_stop (cp->search_h); + cp->search_h = NULL; + } + if (NULL != cp->destroy_task) + { + /* paths changed, this resets the destroy timeout counter + and aborts a destroy task that may no longer be valid + to have (as we now have more paths via this peer). */ + consider_peer_destroy (cp); } - call_peer_cont (q, GNUNET_NO); - GNUNET_free (q); } /** - * Initialize the peer subsystem. + * Remove an entry from the DLL of all of the paths that this peer is on. * - * @param c Configuration. - */ -void -GCP_init (const struct GNUNET_CONFIGURATION_Handle *c) -{ - cfg = c; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "GCP_init\n"); - in_shutdown = GNUNET_NO; - peers = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (c, "CADET", "MAX_PEERS", - &max_peers)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, - "CADET", "MAX_PEERS", "USING DEFAULT"); - max_peers = 1000; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (c, "CADET", "DROP_PERCENT", - &drop_percent)) - { - drop_percent = 0; - } - else - { - LOG (GNUNET_ERROR_TYPE_WARNING, "**************************************\n"); - LOG (GNUNET_ERROR_TYPE_WARNING, "Cadet is running with DROP enabled.\n"); - LOG (GNUNET_ERROR_TYPE_WARNING, "This is NOT a good idea!\n"); - LOG (GNUNET_ERROR_TYPE_WARNING, "Remove DROP_PERCENT from config file.\n"); - LOG (GNUNET_ERROR_TYPE_WARNING, "**************************************\n"); - } - ats_ch = GNUNET_ATS_connectivity_init (c); - connect_to_core (c); - if (NULL == core_handle) - { - GNUNET_break (0); - GNUNET_SCHEDULER_shutdown (); - } -} - - -/** - * Shut down the peer subsystem. + * @param cp peer to modify + * @param entry an entry on a path + * @param off offset of this peer on the path */ void -GCP_shutdown (void) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Shutting down peer subsystem\n"); - in_shutdown = GNUNET_YES; - if (NULL != core_handle) - { - GNUNET_CORE_disconnect (core_handle); - core_handle = NULL; - } - GNUNET_PEER_change_rc (myid, -1); - /* With MQ API, CORE calls the disconnect handler for every peer - * after calling GNUNET_CORE_disconnect, shutdown must occur *after* that. - */ - GNUNET_CONTAINER_multipeermap_iterate (peers, - &shutdown_peer, - NULL); - if (NULL != ats_ch) - { - GNUNET_ATS_connectivity_done (ats_ch); - ats_ch = NULL; - } - GNUNET_CONTAINER_multipeermap_destroy (peers); - peers = NULL; -} - - -/** - * Retrieve the CadetPeer stucture associated with the peer. Optionally create - * one and insert it in the appropriate structures if the peer is not known yet. - * - * @param peer_id Full identity of the peer. - * @param create #GNUNET_YES if a new peer should be created if unknown. - * #GNUNET_NO otherwise. - * - * @return Existing or newly created peer structure. - * NULL if unknown and not requested @a create - */ -struct CadetPeer * -GCP_get (const struct GNUNET_PeerIdentity *peer_id, int create) -{ - struct CadetPeer *peer; - - peer = GNUNET_CONTAINER_multipeermap_get (peers, peer_id); - if (NULL == peer) - { - peer = GNUNET_new (struct CadetPeer); - if (GNUNET_CONTAINER_multipeermap_size (peers) > max_peers) - { - peer_delete_oldest (); - } - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multipeermap_put (peers, - peer_id, - peer, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - peer->id = GNUNET_PEER_intern (peer_id); - } - peer->last_contact = GNUNET_TIME_absolute_get (); - - return peer; -} - - -/** - * Retrieve the CadetPeer stucture associated with the - * peer. Optionally create one and insert it in the appropriate - * structures if the peer is not known yet. - * - * @param peer Short identity of the peer. - * @param create #GNUNET_YES if a new peer should be created if unknown. - * #GNUNET_NO otherwise. - * - * @return Existing or newly created peer structure. - * NULL if unknown and not requested @a create - */ -struct CadetPeer * -GCP_get_short (const GNUNET_PEER_Id peer, int create) -{ - return GCP_get (GNUNET_PEER_resolve2 (peer), create); +GCP_path_entry_remove (struct CadetPeer *cp, + struct CadetPeerPathEntry *entry, + unsigned int off) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Removing knowledge about peer %s beging on path %s at offset %u\n", + GCP_2s (cp), + GCPP_2s (entry->path), + off); + GNUNET_CONTAINER_DLL_remove (cp->path_heads[off], + cp->path_tails[off], + entry); + GNUNET_assert (0 < cp->num_paths); + cp->off_sum -= off; + cp->num_paths--; + if ( (NULL == cp->core_mq) && + (NULL != cp->t) && + (NULL == cp->search_h) && + (DESIRED_CONNECTIONS_PER_TUNNEL > cp->num_paths) ) + cp->search_h + = GCD_search (&cp->pid); + if (NULL == cp->destroy_task) + { + /* paths changed, we might now be ready for destruction, check again */ + consider_peer_destroy (cp); + } } /** - * Function called once #GNUNET_TRANSPORT_offer_hello() is done. - * Marks the operation as finished. + * Prune down the number of paths to this peer, we seem to + * have way too many. * - * @param cls Closure (our `struct CadetPeer`). + * @param cls the `struct CadetPeer` to maintain the path heap for */ static void -hello_offer_done (void *cls) +path_heap_cleanup (void *cls) { - struct CadetPeer *peer = cls; + struct CadetPeer *cp = cls; + struct CadetPeerPath *root; - peer->hello_offer = NULL; + cp->heap_cleanup_task = NULL; + while (GNUNET_CONTAINER_heap_get_size (cp->path_heap) >= + 2 * DESIRED_CONNECTIONS_PER_TUNNEL) + { + /* Now we have way too many, drop least desirable UNLESS it is in use! + (Note that this intentionally keeps highly desireable, but currently + unused paths around in the hope that we might be able to switch, even + if the number of paths exceeds the threshold.) */ + root = GNUNET_CONTAINER_heap_peek (cp->path_heap); + GNUNET_assert (NULL != root); + if (NULL != + GCPP_get_connection (root, + cp, + GCPP_get_length (root) - 1)) + break; /* can't fix */ + /* Got plenty of paths to this destination, and this is a low-quality + one that we don't care about. Allow it to die. */ + GNUNET_assert (root == + GNUNET_CONTAINER_heap_remove_root (cp->path_heap)); + GCPP_release (root); + } } /** - * Try to establish a new connection to this peer (in its 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 CONNECTION towards it. + * Try adding a @a path to this @a peer. If the peer already + * has plenty of paths, return NULL. * - * @param peer Peer to connect to. + * @param cp peer to which the @a path leads to + * @param path a path looking for an owner; may not be fully initialized yet! + * @param off offset of @a cp in @a path + * @param force force attaching the path + * @return NULL if this peer does not care to become a new owner, + * otherwise the node in the peer's path heap for the @a path. */ -void -GCP_connect (struct CadetPeer *peer) +struct GNUNET_CONTAINER_HeapNode * +GCP_attach_path (struct CadetPeer *cp, + struct CadetPeerPath *path, + unsigned int off, + int force) { - struct CadetTunnel *t; - struct CadetPeerPath *path; - struct CadetConnection *c; - int rerun_search; - - GCC_check_connections (); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "peer_connect towards %s\n", - GCP_2s (peer)); - /* If we have a current hello, try to connect using it. */ - GCP_try_connect (peer); + GNUNET_CONTAINER_HeapCostType desirability; + struct CadetPeerPath *root; + GNUNET_CONTAINER_HeapCostType root_desirability; + struct GNUNET_CONTAINER_HeapNode *hn; - t = peer->tunnel; - c = NULL; - rerun_search = GNUNET_NO; - - if (NULL != peer->path_head) + GNUNET_assert (off == GCPP_get_length (path) - 1); + GNUNET_assert (cp == GCPP_get_peer_at_offset (path, + off)); + if (NULL == cp->path_heap) + { + /* #GCP_drop_owned_paths() was already called, we cannot take new ones! */ + GNUNET_assert (GNUNET_NO == force); + return NULL; + } + desirability = GCPP_get_desirability (path); + if (GNUNET_NO == force) + { + /* FIXME: desirability is not yet initialized; tricky! */ + if (GNUNET_NO == + GNUNET_CONTAINER_heap_peek2 (cp->path_heap, + (void **) &root, + &root_desirability)) { - LOG (GNUNET_ERROR_TYPE_DEBUG, " some path exists\n"); - path = peer_get_best_path (peer); - if (NULL != path) - { - char *s; - - s = path_2s (path); - LOG (GNUNET_ERROR_TYPE_DEBUG, " path to use: %s\n", s); - GNUNET_free (s); - - c = GCT_use_path (t, path); - if (NULL == c) - { - /* This case can happen when the path includes a first hop that is - * not yet known to be connected. - * - * This happens quite often during testing when running cadet - * under valgrind: core connect notifications come very late - * and the DHT result has already come and created a valid - * path. In this case, the peer->connections - * hashmaps will be NULL and tunnel_use_path will not be able - * to create a connection from that path. - * - * Re-running the DHT GET should give core time to callback. - * - * GCT_use_path -> GCC_new -> register_neighbors takes care of - * updating statistics about this issue. - */ - rerun_search = GNUNET_YES; - } - else - { - GCC_send_create (c); - return; - } - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " but is NULL, all paths are in use\n"); - } + root = NULL; + root_desirability = 0; } - if (GNUNET_YES == rerun_search) + if ( (DESIRED_CONNECTIONS_PER_TUNNEL > cp->num_paths) && + (desirability < root_desirability) ) { - struct GNUNET_TIME_Relative delay; - - GCP_stop_search (peer); - delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100); - peer->search_delayed = GNUNET_SCHEDULER_add_delayed (delay, - &delayed_search, - peer); - GCC_check_connections (); - return; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Decided to not attach path %p to peer %s due to undesirability\n", + GCPP_2s (path), + GCP_2s (cp)); + return NULL; } + } - if (GNUNET_NO == is_searching (peer)) - GCP_start_search (peer); - GCC_check_connections (); -} - - -/** - * Chech whether there is a direct (core level) connection to peer. - * - * @param peer Peer to check. - * - * @return #GNUNET_YES if there is a direct connection. - */ -int -GCP_is_neighbor (const struct CadetPeer *peer) -{ - struct CadetPeerPath *path; - - if (NULL == peer->connections) - return GNUNET_NO; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Attaching path %s to peer %s (%s)\n", + GCPP_2s (path), + GCP_2s (cp), + (GNUNET_NO == force) ? "desirable" : "forced"); - for (path = peer->path_head; NULL != path; path = path->next) - { - if (3 > path->length) - return GNUNET_YES; - } + /* Yes, we'd like to add this path, add to our heap */ + hn = GNUNET_CONTAINER_heap_insert (cp->path_heap, + path, + desirability); - /* Is not a neighbor but connections is not NULL, probably disconnecting */ - return GNUNET_NO; + /* Consider maybe dropping other paths because of the new one */ + if ( (GNUNET_CONTAINER_heap_get_size (cp->path_heap) >= + 2 * DESIRED_CONNECTIONS_PER_TUNNEL) && + (NULL != cp->heap_cleanup_task) ) + cp->heap_cleanup_task = GNUNET_SCHEDULER_add_now (&path_heap_cleanup, + cp); + return hn; } /** - * Create and initialize a new tunnel towards a peer, in case it has none. - * In case the peer already has a tunnel, nothing is done. + * This peer can no longer own @a path as the path + * has been extended and a peer further down the line + * is now the new owner. * - * Does not generate any traffic, just creates the local data structures. - * - * @param peer Peer towards which to create the tunnel. + * @param cp old owner of the @a path + * @param path path where the ownership is lost + * @param hn note in @a cp's path heap that must be deleted */ void -GCP_add_tunnel (struct CadetPeer *peer) +GCP_detach_path (struct CadetPeer *cp, + struct CadetPeerPath *path, + struct GNUNET_CONTAINER_HeapNode *hn) { - GCC_check_connections (); - if (NULL != peer->tunnel) - return; - peer->tunnel = GCT_new (peer); - GCC_check_connections (); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Detatching path %s from peer %s\n", + GCPP_2s (path), + GCP_2s (cp)); + GNUNET_assert (path == + GNUNET_CONTAINER_heap_remove_node (hn)); } /** - * Add a connection to a neighboring peer. - * - * Store that the peer is the first hop of the connection in one - * direction and that on peer disconnect the connection must be - * notified and destroyed, for it will no longer be valid. + * Add a @a connection to this @a cp. * - * @param peer Peer to add connection to. - * @param c Connection to add. - * @param pred #GNUNET_YES if we are predecessor, #GNUNET_NO if we are successor + * @param cp peer via which the @a connection goes + * @param cc the connection to add */ void -GCP_add_connection (struct CadetPeer *peer, - struct CadetConnection *c, - int pred) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "adding connection %s\n", - GCC_2s (c)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "to peer %s\n", - GCP_2s (peer)); - GNUNET_assert (NULL != peer->connections); - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multishortmap_put (peer->connections, - &GCC_get_id (c)->connection_of_tunnel, - c, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Peer %s has now %u connections.\n", - GCP_2s (peer), - GNUNET_CONTAINER_multishortmap_size (peer->connections)); -} - - -/** - * Add the path to the peer and update the path used to reach it in case this - * is the shortest. - * - * @param peer Destination peer to add the path to. - * @param path New path to add. Last peer must be @c peer. - * Path will be either used of freed if already known. - * @param trusted Do we trust that this path is real? - * - * @return path if path was taken, pointer to existing duplicate if exists - * NULL on error. - */ -struct CadetPeerPath * -GCP_add_path (struct CadetPeer *peer, - struct CadetPeerPath *path, - int trusted) -{ - struct CadetPeerPath *aux; - unsigned int l; - unsigned int l2; - - GCC_check_connections (); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "adding path [%u] to peer %s\n", - path->length, GCP_2s (peer)); - - if (NULL == peer || NULL == path - || path->peers[path->length - 1] != peer->id) - { - GNUNET_break (0); - path_destroy (path); - return NULL; - } - - for (l = 1; l < path->length; l++) - { - if (path->peers[l] == myid) - { - 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)); - } - } - - LOG (GNUNET_ERROR_TYPE_DEBUG, " final length: %u\n", path->length); - - if (2 >= path->length && GNUNET_NO == trusted) - { - /* Only allow CORE to tell us about direct paths */ - path_destroy (path); - return NULL; - } - - l = path_get_length (path); - if (0 == l) - { - path_destroy (path); - return NULL; - } - - GNUNET_assert (peer->id == path->peers[path->length - 1]); - for (aux = peer->path_head; aux != NULL; aux = aux->next) - { - l2 = path_get_length (aux); - if (l2 > l) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " added\n"); - GNUNET_CONTAINER_DLL_insert_before (peer->path_head, - peer->path_tail, aux, path); - goto finish; - } - else - { - if (l2 == l && memcmp (path->peers, aux->peers, l) == 0) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " already known\n"); - path_destroy (path); - return aux; - } - } - } - GNUNET_CONTAINER_DLL_insert_tail (peer->path_head, - peer->path_tail, - path); - LOG (GNUNET_ERROR_TYPE_DEBUG, " added last\n"); - -finish: - if (NULL != peer->tunnel - && CONNECTIONS_PER_TUNNEL > GCT_count_connections (peer->tunnel) - && 2 < path->length) /* Direct paths are handled by core_connect */ - { - GCP_connect (peer); - } - GCC_check_connections (); - return path; +GCP_add_connection (struct CadetPeer *cp, + struct CadetConnection *cc) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Adding connection %s to peer %s\n", + GCC_2s (cc), + GCP_2s (cp)); + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multishortmap_put (cp->connections, + &GCC_get_id (cc)->connection_of_tunnel, + cc, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + if (NULL != cp->destroy_task) + { + GNUNET_SCHEDULER_cancel (cp->destroy_task); + cp->destroy_task = NULL; + } } /** - * 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 Peer to add the path to, being the origin of the path. - * @param path New path to add after being inversed. - * Path will be either used or freed. - * @param trusted Do we trust that this path is real? + * Remove a @a connection that went via this @a cp. * - * @return path if path was taken, pointer to existing duplicate if exists - * NULL on error. + * @param cp peer via which the @a connection went + * @param cc the connection to remove */ -struct CadetPeerPath * -GCP_add_path_to_origin (struct CadetPeer *peer, - struct CadetPeerPath *path, - int trusted) +void +GCP_remove_connection (struct CadetPeer *cp, + struct CadetConnection *cc) { - if (NULL == path) - return NULL; - path_invert (path); - return GCP_add_path (peer, path, trusted); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Removing connection %s from peer %s\n", + GCC_2s (cc), + GCP_2s (cp)); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multishortmap_remove (cp->connections, + &GCC_get_id (cc)->connection_of_tunnel, + cc)); + consider_peer_destroy (cp); } /** - * Adds a path to the info of all the peers in the path + * Retrieve the CadetPeer stucture associated with the + * peer. Optionally create one and insert it in the appropriate + * structures if the peer is not known yet. * - * @param p Path to process. - * @param confirmed Whether we know if the path works or not. + * @param peer_id Full identity of the peer. + * @param create #GNUNET_YES if a new peer should be created if unknown. + * #GNUNET_NO to return NULL if peer is unknown. + * @return Existing or newly created peer structure. + * NULL if unknown and not requested @a create */ -void -GCP_add_path_to_all (const struct CadetPeerPath *p, int confirmed) +struct CadetPeer * +GCP_get (const struct GNUNET_PeerIdentity *peer_id, + int create) { - unsigned int i; + struct CadetPeer *cp; - /* TODO: invert and add to origin */ - /* TODO: replace all "GCP_add_path" with this, make the other one static */ - GCC_check_connections (); - for (i = 0; i < p->length && p->peers[i] != myid; i++) /* skip'em */ ; - for (i++; i < p->length; i++) - { - struct CadetPeer *peer; - struct CadetPeerPath *copy; - - peer = GCP_get_short (p->peers[i], GNUNET_YES); - copy = path_duplicate (p); - copy->length = i + 1; - GCP_add_path (peer, copy, 3 > p->length ? GNUNET_NO : confirmed); - } - GCC_check_connections (); + cp = GNUNET_CONTAINER_multipeermap_get (peers, + peer_id); + if (NULL != cp) + return cp; + if (GNUNET_NO == create) + return NULL; + cp = GNUNET_new (struct CadetPeer); + cp->pid = *peer_id; + cp->connections = GNUNET_CONTAINER_multishortmap_create (32, + GNUNET_YES); + cp->path_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_put (peers, + &cp->pid, + cp, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Creating peer %s\n", + GCP_2s (cp)); + return cp; } /** - * Remove any path to the peer that has the exact same peers as the one given. + * Obtain the peer identity for a `struct CadetPeer`. * - * @param peer Peer to remove the path from. - * @param path Path to remove. Is always destroyed . + * @param cp our peer handle + * @return the peer identity */ -void -GCP_remove_path (struct CadetPeer *peer, - struct CadetPeerPath *path) +const struct GNUNET_PeerIdentity * +GCP_get_id (struct CadetPeer *cp) { - struct CadetPeerPath *iter; - struct CadetPeerPath *next; - - GCC_check_connections (); - GNUNET_assert (myid == path->peers[0]); - GNUNET_assert (peer->id == path->peers[path->length - 1]); - - LOG (GNUNET_ERROR_TYPE_INFO, - "Removing path %p (%u) from %s\n", - path, path->length, GCP_2s (peer)); - - for (iter = peer->path_head; NULL != iter; iter = next) - { - next = iter->next; - if (0 == path_cmp (path, iter)) - { - GNUNET_CONTAINER_DLL_remove (peer->path_head, - peer->path_tail, - iter); - if (iter != path) - path_destroy (iter); - } - } - path_destroy (path); - GCC_check_connections (); + return &cp->pid; } /** - * Check that we are aware of a connection from a neighboring peer. + * Iterate over all known peers. * - * @param peer Peer to the connection is with - * @param c Connection that should be in the map with this peer. + * @param iter Iterator. + * @param cls Closure for @c iter. */ void -GCP_check_connection (const struct CadetPeer *peer, - const struct CadetConnection *c) +GCP_iterate_all (GNUNET_CONTAINER_PeerMapIterator iter, + void *cls) { - GNUNET_assert (NULL != peer); - GNUNET_assert (NULL != peer->connections); - return; // ???? - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multishortmap_contains_value (peer->connections, - &GCC_get_id (c)->connection_of_tunnel, - c)); + GNUNET_CONTAINER_multipeermap_iterate (peers, + iter, + cls); } /** - * Remove a connection from a neighboring peer. + * Count the number of known paths toward the peer. * - * @param peer Peer to remove connection from. - * @param c Connection to remove. + * @param cp Peer to get path info. + * @return Number of known paths. */ -void -GCP_remove_connection (struct CadetPeer *peer, - const struct CadetConnection *c) +unsigned int +GCP_count_paths (const struct CadetPeer *cp) { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Removing connection %s\n", - GCC_2s (c)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "from peer %s\n", - GCP_2s (peer)); - if ( (NULL == peer) || - (NULL == peer->connections) ) - return; - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multishortmap_remove (peer->connections, - &GCC_get_id (c)->connection_of_tunnel, - c)); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Peer %s remains with %u connections.\n", - GCP_2s (peer), - GNUNET_CONTAINER_multishortmap_size (peer->connections)); + return cp->num_paths; } /** - * Start the DHT search for new paths towards the peer: we don't have - * enough good connections. + * Iterate over the paths to a peer. * - * @param peer Destination peer. + * @param cp Peer to get path info. + * @param callback Function to call for every path. + * @param callback_cls Closure for @a callback. + * @return Number of iterated paths. */ -void -GCP_start_search (struct CadetPeer *peer) +unsigned int +GCP_iterate_paths (struct CadetPeer *cp, + GCP_PathIterator callback, + void *callback_cls) { - const struct GNUNET_PeerIdentity *id; - struct CadetTunnel *t = peer->tunnel; - - GCC_check_connections (); - if (NULL != peer->search_h) - { - GNUNET_break (0); - return; - } - - if (NULL != peer->search_delayed) - GCP_stop_search (peer); - - id = GNUNET_PEER_resolve2 (peer->id); - peer->search_h = GCD_search (id, &search_handler, peer); - - if (NULL == t) - { - /* Why would we search for a peer with no tunnel towards it? */ - GNUNET_break (0); - return; - } - - if (CADET_TUNNEL_NEW == GCT_get_cstate (t) - || 0 == GCT_count_any_connections (t)) - { - GCT_change_cstate (t, CADET_TUNNEL_SEARCHING); - } - GCC_check_connections (); -} + unsigned int ret = 0; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Iterating over paths to peer %s%s\n", + GCP_2s (cp), + (NULL == cp->core_mq) ? "" : " including direct link"); + if (NULL != cp->core_mq) + { + struct CadetPeerPath *path; -/** - * Stop the DHT search for new paths towards the peer: we already have - * enough good connections. - * - * @param peer Destination peer. - */ -void -GCP_stop_search (struct CadetPeer *peer) -{ - GCC_check_connections (); - if (NULL != peer->search_h) - { - GCD_search_stop (peer->search_h); - peer->search_h = NULL; - } - if (NULL != peer->search_delayed) + path = GCPP_get_path_from_route (1, + &cp->pid); + ret++; + if (GNUNET_NO == + callback (callback_cls, + path, + 0)) + return ret; + } + for (unsigned int i=0;i<cp->path_dll_length;i++) + { + for (struct CadetPeerPathEntry *pe = cp->path_heads[i]; + NULL != pe; + pe = pe->next) { - GNUNET_SCHEDULER_cancel (peer->search_delayed); - peer->search_delayed = NULL; + ret++; + if (GNUNET_NO == + callback (callback_cls, + pe->path, + i)) + return ret; } - GCC_check_connections (); -} - - -/** - * Get the Full ID of a peer. - * - * @param peer Peer to get from. - * - * @return Full ID of peer. - */ -const struct GNUNET_PeerIdentity * -GCP_get_id (const struct CadetPeer *peer) -{ - return GNUNET_PEER_resolve2 (peer->id); + } + return ret; } /** - * Get the Short ID of a peer. - * - * @param peer Peer to get from. + * Iterate over the paths to @a cp where + * @a cp is at distance @a dist from us. * - * @return Short ID of peer. + * @param cp Peer to get path info. + * @param dist desired distance of @a cp to us on the path + * @param callback Function to call for every path. + * @param callback_cls Closure for @a callback. + * @return Number of iterated paths. */ -GNUNET_PEER_Id -GCP_get_short_id (const struct CadetPeer *peer) +unsigned int +GCP_iterate_paths_at (struct CadetPeer *cp, + unsigned int dist, + GCP_PathIterator callback, + void *callback_cls) { - return peer->id; -} - + unsigned int ret = 0; -/** - * Set tunnel. - * - * If tunnel is NULL and there was a search active, stop it, as it's useless. - * - * @param peer Peer. - * @param t Tunnel. - */ -void -GCP_set_tunnel (struct CadetPeer *peer, struct CadetTunnel *t) -{ - peer->tunnel = t; - if (NULL == t && GNUNET_YES == is_searching (peer)) - { - GCP_stop_search (peer); - } + if (dist >= cp->path_dll_length) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Asked to look for paths at distance %u, but maximum for me is < %u\n", + dist, + cp->path_dll_length); + return 0; + } + for (struct CadetPeerPathEntry *pe = cp->path_heads[dist]; + NULL != pe; + pe = pe->next) + { + if (GNUNET_NO == + callback (callback_cls, + pe->path, + dist)) + return ret; + ret++; + } + return ret; } /** * Get the tunnel towards a peer. * - * @param peer Peer to get from. - * + * @param cp Peer to get from. + * @param create #GNUNET_YES to create a tunnel if we do not have one * @return Tunnel towards peer. */ struct CadetTunnel * -GCP_get_tunnel (const struct CadetPeer *peer) +GCP_get_tunnel (struct CadetPeer *cp, + int create) { - if (NULL == peer) - return NULL; - return peer->tunnel; + if (NULL == cp) + return NULL; + if ( (NULL != cp->t) || + (GNUNET_NO == create) ) + return cp->t; + cp->t = GCT_create_tunnel (cp); + consider_peer_activate (cp); + return cp->t; } /** - * Set the hello message. + * Hello offer was passed to the transport service. Mark it + * as done. * - * @param peer Peer whose message to set. - * @param hello Hello message. + * @param cls the `struct CadetPeer` where the offer completed */ -void -GCP_set_hello (struct CadetPeer *peer, - const struct GNUNET_HELLO_Message *hello) +static void +hello_offer_done (void *cls) { - struct GNUNET_HELLO_Message *old; - size_t size; + struct CadetPeer *cp = cls; - GCC_check_connections (); - LOG (GNUNET_ERROR_TYPE_DEBUG, "set hello for %s\n", GCP_2s (peer)); - if (NULL == hello) - return; - - old = GCP_get_hello (peer); - if (NULL == old) - { - size = GNUNET_HELLO_size (hello); - peer->hello = GNUNET_malloc (size); - GNUNET_memcpy (peer->hello, hello, size); - } - else - { - peer->hello = GNUNET_HELLO_merge (old, hello); - GNUNET_free (old); - } - GCC_check_connections (); + cp->hello_offer = NULL; } /** - * Get the hello message. - * - * @param peer Peer whose message to get. + * We got a HELLO for a @a peer, remember it, and possibly + * trigger adequate actions (like trying to connect). * - * @return Hello message. + * @param cp the peer we got a HELLO for + * @param hello the HELLO to remember */ -struct GNUNET_HELLO_Message * -GCP_get_hello (struct CadetPeer *peer) +void +GCP_set_hello (struct CadetPeer *cp, + const struct GNUNET_HELLO_Message *hello) { - struct GNUNET_TIME_Absolute expiration; - struct GNUNET_TIME_Relative remaining; - - if (NULL == peer->hello) - return NULL; + struct GNUNET_HELLO_Message *mrg; - expiration = GNUNET_HELLO_get_last_expiration (peer->hello); - remaining = GNUNET_TIME_absolute_get_remaining (expiration); - if (0 == remaining.rel_value_us) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " get - hello expired on %s\n", - GNUNET_STRINGS_absolute_time_to_string (expiration)); - GNUNET_free (peer->hello); - peer->hello = NULL; - } - return peer->hello; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got %u byte HELLO for peer %s\n", + (unsigned int) GNUNET_HELLO_size (hello), + GCP_2s (cp)); + if (NULL != cp->hello_offer) + { + GNUNET_TRANSPORT_offer_hello_cancel (cp->hello_offer); + cp->hello_offer = NULL; + } + if (NULL != cp->hello) + { + mrg = GNUNET_HELLO_merge (hello, + cp->hello); + GNUNET_free (cp->hello); + cp->hello = mrg; + } + else + { + cp->hello = GNUNET_memdup (hello, + GNUNET_HELLO_size (hello)); + } + cp->hello_offer + = GNUNET_TRANSPORT_offer_hello (cfg, + GNUNET_HELLO_get_header (cp->hello) , + &hello_offer_done, + cp); + /* New HELLO means cp's destruction time may change... */ + consider_peer_destroy (cp); } /** - * Try to connect to a peer on TRANSPORT level. + * The tunnel to the given peer no longer exists, remove it from our + * data structures, and possibly clean up the peer itself. * - * @param peer Peer to whom to connect. + * @param cp the peer affected + * @param t the dead tunnel */ void -GCP_try_connect (struct CadetPeer *peer) +GCP_drop_tunnel (struct CadetPeer *cp, + struct CadetTunnel *t) { - struct GNUNET_HELLO_Message *hello; - struct GNUNET_MessageHeader *mh; - - if (GNUNET_YES != - GNUNET_CONFIGURATION_get_value_yesno (cfg, - "CADET", - "DISABLE_TRY_CONNECT")) - return; - GCC_check_connections (); - if (GNUNET_YES == GCP_is_neighbor (peer)) - return; - hello = GCP_get_hello (peer); - if (NULL == hello) - return; - - mh = GNUNET_HELLO_get_header (hello); - if (NULL != peer->hello_offer) - { - GNUNET_TRANSPORT_offer_hello_cancel (peer->hello_offer); - peer->hello_offer = NULL; - } - peer->hello_offer = GNUNET_TRANSPORT_offer_hello (cfg, - mh, - &hello_offer_done, - peer); - if (NULL == peer->connectivity_suggestion) - peer->connectivity_suggestion - = GNUNET_ATS_connectivity_suggest (ats_ch, - GCP_get_id (peer), - 1); /* strength */ - GCC_check_connections (); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Dropping tunnel %s to peer %s\n", + GCT_2s (t), + GCP_2s (cp)); + GNUNET_assert (cp->t == t); + cp->t = NULL; + consider_peer_destroy (cp); } /** - * Notify a peer that a link between two other peers is broken. If any path - * used that link, eliminate it. + * Test if @a cp has a core-level connection * - * @param peer Peer affected by the change. - * @param peer1 Peer whose link is broken. - * @param peer2 Peer whose link is broken. + * @param cp peer to test + * @return #GNUNET_YES if @a cp has a core-level connection */ -void -GCP_notify_broken_link (struct CadetPeer *peer, - const struct GNUNET_PeerIdentity *peer1, - const struct GNUNET_PeerIdentity *peer2) +int +GCP_has_core_connection (struct CadetPeer *cp) { - struct CadetPeerPath *iter; - struct CadetPeerPath *next; - unsigned int i; - GNUNET_PEER_Id p1; - GNUNET_PEER_Id p2; - - GCC_check_connections (); - p1 = GNUNET_PEER_search (peer1); - p2 = GNUNET_PEER_search (peer2); - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Link %u-%u broken\n", p1, p2); - if (0 == p1 || 0 == p2) - { - /* We don't even know them */ - return; - } - - for (iter = peer->path_head; NULL != iter; iter = next) - { - next = iter->next; - for (i = 0; i < iter->length - 1; i++) - { - if ((iter->peers[i] == p1 && iter->peers[i + 1] == p2) - || (iter->peers[i] == p2 && iter->peers[i + 1] == p1)) - { - char *s; - - s = path_2s (iter); - LOG (GNUNET_ERROR_TYPE_DEBUG, " - invalidating %s\n", s); - GNUNET_free (s); - - path_invalidate (iter); - } - } - } - GCC_check_connections (); + return (NULL != cp->core_mq) ? GNUNET_YES : GNUNET_NO; } /** - * Count the number of known paths toward the peer. - * - * @param peer Peer to get path info. + * Start message queue change notifications. * - * @return Number of known paths. + * @param cp peer to notify for + * @param cb function to call if mq becomes available or unavailable + * @param cb_cls closure for @a cb + * @return handle to cancel request */ -unsigned int -GCP_count_paths (const struct CadetPeer *peer) +struct GCP_MessageQueueManager * +GCP_request_mq (struct CadetPeer *cp, + GCP_MessageQueueNotificationCallback cb, + void *cb_cls) { - struct CadetPeerPath *iter; - unsigned int i; + struct GCP_MessageQueueManager *mqm; - for (iter = peer->path_head, i = 0; NULL != iter; iter = iter->next) - i++; - - return i; + mqm = GNUNET_new (struct GCP_MessageQueueManager); + mqm->cb = cb; + mqm->cb_cls = cb_cls; + mqm->cp = cp; + GNUNET_CONTAINER_DLL_insert (cp->mqm_head, + cp->mqm_tail, + mqm); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Creating MQM %p for peer %s\n", + mqm, + GCP_2s (cp)); + if (NULL != cp->core_mq) + cb (cb_cls, + GNUNET_YES); + return mqm; } /** - * Iterate over the paths to a peer. + * Stops message queue change notifications. * - * @param peer Peer to get path info. - * @param callback Function to call for every path. - * @param cls Closure for @a callback. - * - * @return Number of iterated paths. + * @param mqm handle matching request to cancel + * @param last_env final message to transmit, or NULL */ -unsigned int -GCP_iterate_paths (struct CadetPeer *peer, - GCP_path_iterator callback, - void *cls) -{ - struct CadetPeerPath *iter; - unsigned int i; - - for (iter = peer->path_head, i = 0; NULL != iter; iter = iter->next) +void +GCP_request_mq_cancel (struct GCP_MessageQueueManager *mqm, + struct GNUNET_MQ_Envelope *last_env) +{ + struct CadetPeer *cp = mqm->cp; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Destroying MQM %p for peer %s%s\n", + mqm, + GCP_2s (cp), + (NULL == last_env) ? "" : " with last ditch transmission"); + if (NULL != mqm->env) + GNUNET_MQ_discard (mqm->env); + if (NULL != last_env) + { + if (NULL != cp->core_mq) { - i++; - if (GNUNET_YES != callback (cls, peer, iter)) - break; + GNUNET_MQ_notify_sent (last_env, + &mqm_send_done, + cp); + GNUNET_MQ_send (cp->core_mq, + last_env); } - - return i; + else + { + GNUNET_MQ_discard (last_env); + } + } + if (cp->mqm_ready_ptr == mqm) + cp->mqm_ready_ptr = mqm->next; + GNUNET_CONTAINER_DLL_remove (cp->mqm_head, + cp->mqm_tail, + mqm); + GNUNET_free (mqm); } /** - * Iterate all known peers. + * Send the message in @a env to @a cp, overriding queueing logic. + * This function should only be used to send error messages outside + * of flow and congestion control, similar to ICMP. Note that + * the envelope may be silently discarded as well. * - * @param iter Iterator. - * @param cls Closure for @c iter. + * @param cp peer to send the message to + * @param env envelope with the message to send */ void -GCP_iterate_all (GNUNET_CONTAINER_PeerMapIterator iter, - void *cls) +GCP_send_ooo (struct CadetPeer *cp, + struct GNUNET_MQ_Envelope *env) { - GCC_check_connections (); - GNUNET_CONTAINER_multipeermap_iterate (peers, - iter, - cls); - GCC_check_connections (); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending message to %s out of management\n", + GCP_2s (cp)); + if (NULL == cp->core_mq) + { + GNUNET_MQ_discard (env); + return; + } + GNUNET_MQ_notify_sent (env, + &mqm_send_done, + cp); + GNUNET_MQ_send (cp->core_mq, + env); } -/** - * Get the static string for a peer ID. - * - * @param peer Peer. - * - * @return Static string for it's ID. - */ -const char * -GCP_2s (const struct CadetPeer *peer) -{ - if (NULL == peer) - return "(NULL)"; - return GNUNET_i2s (GNUNET_PEER_resolve2 (peer->id)); -} -/* end of gnunet-service-cadet_peer.c */ +/* end of gnunet-service-cadet-new_peer.c */ diff --git a/src/cadet/gnunet-service-cadet_peer.h b/src/cadet/gnunet-service-cadet_peer.h index 1e206e10f1..a2a6c6a92a 100644 --- a/src/cadet/gnunet-service-cadet_peer.h +++ b/src/cadet/gnunet-service-cadet_peer.h @@ -1,6 +1,7 @@ + /* This file is part of GNUnet. - Copyright (C) 2013 GNUnet e.V. + Copyright (C) 2001-2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -19,465 +20,375 @@ */ /** - * @file cadet/gnunet-service-cadet_peer.h - * @brief cadet service; dealing with remote peers + * @file cadet/gnunet-service-cadet-new_peer.h + * @brief Information we track per peer. * @author Bartlomiej Polot - * - * All functions in this file should use the prefix GMP (Gnunet Cadet Peer) + * @author Christian Grothoff */ - #ifndef GNUNET_SERVICE_CADET_PEER_H #define GNUNET_SERVICE_CADET_PEER_H -#ifdef __cplusplus -extern "C" -{ -#if 0 /* keep Emacsens' auto-indent happy */ -} -#endif -#endif - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "cadet_path.h" - -/** - * Struct containing all information regarding a given peer - */ -struct CadetPeer; +#include "gnunet-service-cadet.h" +#include "gnunet_hello_lib.h" -/** - * Handle to queued messages on a peer level. - */ -struct CadetPeerQueue; - -#include "gnunet-service-cadet_connection.h" - - -/** - * Callback called when a queued message is sent. - * - * @param cls Closure. - * @param c Connection this message was on. - * @param fwd Was this a FWD going message? - * @param sent Was it really sent? (Could have been canceled) - * @param type Type of message sent. - * @param payload_type Type of payload, if applicable. - * @param pid Message ID, or 0 if not applicable (create, destroy, etc). - * @param size Size of the message. - * @param wait Time spent waiting for core (only the time for THIS message) - */ -typedef void -(*GCP_sent) (void *cls, - struct CadetConnection *c, - int fwd, - int sent, - uint16_t type, - uint16_t payload_type, - struct CadetEncryptedMessageIdentifier pid, - size_t size, - struct GNUNET_TIME_Relative wait); /** - * Peer path iterator. + * Get the static string for a peer ID. * - * @param cls Closure. - * @param peer Peer this path is towards. - * @param path Path itself - * @return #GNUNET_YES if should keep iterating. - * #GNUNET_NO otherwise. - */ -typedef int -(*GCP_path_iterator) (void *cls, - struct CadetPeer *peer, - struct CadetPeerPath *path); - - -/******************************************************************************/ -/******************************** API ***********************************/ -/******************************************************************************/ - -/** - * Initialize peer subsystem. + * @param peer Peer. * - * @param c Configuration. - */ -void -GCP_init (const struct GNUNET_CONFIGURATION_Handle *c); - -/** - * Shut down the peer subsystem. + * @return Static string for it's ID. */ -void -GCP_shutdown (void); +const char * +GCP_2s (const struct CadetPeer *peer); /** - * Retrieve the CadetPeer stucture associated with the peer. Optionally create - * one and insert it in the appropriate structures if the peer is not known yet. + * Retrieve the CadetPeer stucture associated with the + * peer. Optionally create one and insert it in the appropriate + * structures if the peer is not known yet. * * @param peer_id Full identity of the peer. * @param create #GNUNET_YES if a new peer should be created if unknown. - * #GNUNET_NO otherwise. - * + * #GNUNET_NO to return NULL if peer is unknown. * @return Existing or newly created peer structure. * NULL if unknown and not requested @a create */ struct CadetPeer * -GCP_get (const struct GNUNET_PeerIdentity *peer_id, int create); +GCP_get (const struct GNUNET_PeerIdentity *peer_id, + int create); /** - * Retrieve the CadetPeer stucture associated with the peer. Optionally create - * one and insert it in the appropriate structures if the peer is not known yet. - * - * @param peer Short identity of the peer. - * @param create #GNUNET_YES if a new peer should be created if unknown. - * #GNUNET_NO otherwise. + * Calculate how desirable a path is for @a cp if + * @a cp is at offset @a off in the path. * - * @return Existing or newly created peer structure. - * NULL if unknown and not requested @a create + * @param cp a peer reachable via a path + * @param off offset of @a cp in a path + * @return score how useful a path is to reach @a cp, + * positive scores mean path is more desirable */ -struct CadetPeer * -GCP_get_short (const GNUNET_PEER_Id peer, int create); - +double +GCP_get_desirability_of_path (struct CadetPeer *cp, + unsigned int off); -/** - * Try to establish a new connection to this peer (in its 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 CONNECTION towards it. - * - * @param peer Peer to connect to. - */ -void -GCP_connect (struct CadetPeer *peer); /** - * @brief Send a message to another peer (using CORE). + * Obtain the peer identity for a `struct CadetPeer`. * - * @param peer Peer towards which to queue the message. - * @param message Message to send. - * @param payload_type Type of the message's payload, for debug messages. - * 0 if the message is a retransmission (unknown payload). - * UINT16_MAX if the message does not have payload. - * @param payload_id ID of the payload (MID, ACK #, etc) - * @param c Connection this message belongs to (can be NULL). - * @param fwd Is this a message going root->dest? (FWD ACK are NOT FWD!) - * @param cont Continuation to be called once CORE has sent the message. - * @param cont_cls Closure for @c cont. + * @param cp our peer handle + * @return the peer identity */ -struct CadetPeerQueue * -GCP_send (struct CadetPeer *peer, - const struct GNUNET_MessageHeader *message, - uint16_t payload_type, - struct CadetEncryptedMessageIdentifier payload_id, - struct CadetConnection *c, - int fwd, - GCP_sent cont, - void *cont_cls); +const struct GNUNET_PeerIdentity * +GCP_get_id (struct CadetPeer *cp); -/** - * Cancel sending a message. Message must have been sent with - * #GCP_send before. May not be called after the notify sent - * callback has been called. - * - * It does NOT call the continuation given to #GCP_send. - * - * @param q Queue handle to cancel - */ -void -GCP_send_cancel (struct CadetPeerQueue *q); /** - * Set tunnel. + * Iterate over all known peers. * - * @param peer Peer. - * @param t Tunnel. + * @param iter Iterator. + * @param cls Closure for @c iter. */ void -GCP_set_tunnel (struct CadetPeer *peer, struct CadetTunnel *t); +GCP_iterate_all (GNUNET_CONTAINER_PeerMapIterator iter, + void *cls); /** - * Check whether there is a direct (core level) connection to peer. - * - * @param peer Peer to check. + * Count the number of known paths toward the peer. * - * @return #GNUNET_YES if there is a direct connection. + * @param cp Peer to get path info. + * @return Number of known paths. */ -int -GCP_is_neighbor (const struct CadetPeer *peer); +unsigned int +GCP_count_paths (const struct CadetPeer *cp); /** - * Create and initialize a new tunnel towards a peer, in case it has none. - * - * Does not generate any traffic, just creates the local data structures. + * Drop all paths owned by this peer, and do not + * allow new ones to be added: We are shutting down. * - * @param peer Peer towards which to create the tunnel. + * @param cp peer to drop paths to */ void -GCP_add_tunnel (struct CadetPeer *peer); +GCP_drop_owned_paths (struct CadetPeer *cp); /** - * Add a connection to a neighboring peer. - * - * Store that the peer is the first hop of the connection in one - * direction and that on peer disconnect the connection must be - * notified and destroyed, for it will no longer be valid. + * Peer path iterator. * - * @param peer Peer to add connection to. - * @param c Connection to add. - * @param pred #GNUNET_YES if we are predecessor, #GNUNET_NO if we are successor + * @param cls Closure. + * @param path Path itself + * @param off offset of the target peer in @a path + * @return #GNUNET_YES if should keep iterating. + * #GNUNET_NO otherwise. */ -void -GCP_add_connection (struct CadetPeer *peer, - struct CadetConnection *c, - int pred); +typedef int +(*GCP_PathIterator) (void *cls, + struct CadetPeerPath *path, + unsigned int off); /** - * Add the path to the peer and update the path used to reach it in case this - * is the shortest. - * - * @param peer 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? + * Iterate over the paths to a peer. * - * @return path if path was taken, pointer to existing duplicate if exists - * NULL on error. + * @param cp Peer to get path info. + * @param callback Function to call for every path. + * @param callback_cls Closure for @a callback. + * @return Number of iterated paths. */ -struct CadetPeerPath * -GCP_add_path (struct CadetPeer *peer, - struct CadetPeerPath *p, - int trusted); +unsigned int +GCP_iterate_paths (struct CadetPeer *cp, + GCP_PathIterator callback, + void *callback_cls); /** - * 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. + * Iterate over the paths to @a peer where + * @a peer is at distance @a dist from us. * - * @param peer Peer to add the path to, being the origin of the path. - * @param path New path to add after being inversed. - * Path will be either used or freed. - * @param trusted Do we trust that this path is real? - * - * @return path if path was taken, pointer to existing duplicate if exists - * NULL on error. + * @param cp Peer to get path info. + * @param dist desired distance of @a peer to us on the path + * @param callback Function to call for every path. + * @param callback_cls Closure for @a callback. + * @return Number of iterated paths. */ -struct CadetPeerPath * -GCP_add_path_to_origin (struct CadetPeer *peer, - struct CadetPeerPath *path, - int trusted); +unsigned int +GCP_iterate_paths_at (struct CadetPeer *cp, + unsigned int dist, + GCP_PathIterator callback, + void *callback_cls); + /** - * Adds a path to the info of all the peers in the path + * Remove an entry from the DLL of all of the paths that this peer is on. * - * @param p Path to process. - * @param confirmed Whether we know if the path works or not. + * @param cp peer to modify + * @param entry an entry on a path + * @param off offset of this peer on the path */ void -GCP_add_path_to_all (const struct CadetPeerPath *p, int confirmed); +GCP_path_entry_remove (struct CadetPeer *cp, + struct CadetPeerPathEntry *entry, + unsigned int off); /** - * Remove any path to the peer that has the extact same peers as the one given. + * Add an entry to the DLL of all of the paths that this peer is on. * - * @param peer Peer to remove the path from. - * @param path Path to remove. Is always destroyed . + * @param cp peer to modify + * @param entry an entry on a path + * @param off offset of this peer on the path */ void -GCP_remove_path (struct CadetPeer *peer, - struct CadetPeerPath *path); +GCP_path_entry_add (struct CadetPeer *cp, + struct CadetPeerPathEntry *entry, + unsigned int off); /** - * Check that we are aware of a connection from a neighboring peer. + * Get the tunnel towards a peer. * - * @param peer Peer to the connection is with - * @param c Connection that should be in the map with this peer. + * @param cp Peer to get from. + * @param create #GNUNET_YES to create a tunnel if we do not have one + * @return Tunnel towards peer. */ -void -GCP_check_connection (const struct CadetPeer *peer, - const struct CadetConnection *c); +struct CadetTunnel * +GCP_get_tunnel (struct CadetPeer *cp, + int create); /** - * Remove a connection from a neighboring peer. + * The tunnel to the given peer no longer exists, remove it from our + * data structures, and possibly clean up the peer itself. * - * @param peer Peer to remove connection from. - * @param c Connection to remove. + * @param cp the peer affected + * @param t the dead tunnel */ void -GCP_remove_connection (struct CadetPeer *peer, - const struct CadetConnection *c); +GCP_drop_tunnel (struct CadetPeer *cp, + struct CadetTunnel *t); /** - * Start the DHT search for new paths towards the peer: we don't have - * enough good connections. + * Try adding a @a path to this @a cp. If the peer already + * has plenty of paths, return NULL. * - * @param peer Destination peer. + * @param cp peer to which the @a path leads to + * @param path a path looking for an owner; may not be fully initialized yet! + * @param off offset of @a cp in @a path + * @param force for attaching the path + * @return NULL if this peer does not care to become a new owner, + * otherwise the node in the peer's path heap for the @a path. */ -void -GCP_start_search (struct CadetPeer *peer); +struct GNUNET_CONTAINER_HeapNode * +GCP_attach_path (struct CadetPeer *cp, + struct CadetPeerPath *path, + unsigned int off, + int force); /** - * Stop the DHT search for new paths towards the peer: we already have - * enough good connections. + * This peer can no longer own @a path as the path + * has been extended and a peer further down the line + * is now the new owner. * - * @param peer Destination peer. + * @param cp old owner of the @a path + * @param path path where the ownership is lost + * @param hn note in @a cp's path heap that must be deleted */ void -GCP_stop_search (struct CadetPeer *peer); +GCP_detach_path (struct CadetPeer *cp, + struct CadetPeerPath *path, + struct GNUNET_CONTAINER_HeapNode *hn); /** - * Get the Full ID of a peer. - * - * @param peer Peer to get from. + * Add a @a connection to this @a cp. * - * @return Full ID of peer. + * @param cp peer via which the @a connection goes + * @param cc the connection to add */ -const struct GNUNET_PeerIdentity * -GCP_get_id (const struct CadetPeer *peer); +void +GCP_add_connection (struct CadetPeer *cp, + struct CadetConnection *cc); /** - * Get the Short ID of a peer. + * Remove a @a connection that went via this @a cp. * - * @param peer Peer to get from. - * - * @return Short ID of peer. + * @param cp peer via which the @a connection went + * @param cc the connection to remove */ -GNUNET_PEER_Id -GCP_get_short_id (const struct CadetPeer *peer); +void +GCP_remove_connection (struct CadetPeer *cp, + struct CadetConnection *cc); /** - * Get the tunnel towards a peer. - * - * @param peer Peer to get from. + * We got a HELLO for a @a cp, remember it, and possibly + * trigger adequate actions (like trying to connect). * - * @return Tunnel towards peer. + * @param cp the peer we got a HELLO for + * @param hello the HELLO to remember */ -struct CadetTunnel * -GCP_get_tunnel (const struct CadetPeer *peer); +void +GCP_set_hello (struct CadetPeer *cp, + const struct GNUNET_HELLO_Message *hello); /** - * Set the hello message. - * - * @param peer Peer whose message to set. - * @param hello Hello message. + * Clean up all entries about all peers. + * Must only be called after all tunnels, CORE-connections and + * connections are down. */ void -GCP_set_hello (struct CadetPeer *peer, - const struct GNUNET_HELLO_Message *hello); +GCP_destroy_all_peers (void); /** - * Get the hello message. - * - * @param peer Peer whose message to get. + * Data structure used to track whom we have to notify about changes + * in our ability to transmit to a given peer. * - * @return Hello message. + * All queue managers will be given equal chance for sending messages + * to @a cp. This construct this guarantees fairness for access to @a + * cp among the different message queues. Each connection or route + * will have its respective message queue managers for each direction. */ -struct GNUNET_HELLO_Message * -GCP_get_hello (struct CadetPeer *peer); +struct GCP_MessageQueueManager; /** - * Try to connect to a peer on TRANSPORT level. + * Function to call with updated message queue object. * - * @param peer Peer to whom to connect. + * @param cls closure + * @param available #GNUNET_YES if sending is now possible, + * #GNUNET_NO if sending is no longer possible + * #GNUNET_SYSERR if sending is no longer possible + * and the last envelope was discarded */ -void -GCP_try_connect (struct CadetPeer *peer); +typedef void +(*GCP_MessageQueueNotificationCallback)(void *cls, + int available); + /** - * Notify a peer that a link between two other peers is broken. If any path - * used that link, eliminate it. + * Start message queue change notifications. Will create a new slot + * to manage the message queue to the given @a cp. * - * @param peer Peer affected by the change. - * @param peer1 Peer whose link is broken. - * @param peer2 Peer whose link is broken. + * @param cp peer to notify for + * @param cb function to call if mq becomes available or unavailable + * @param cb_cls closure for @a cb + * @return handle to cancel request */ -void -GCP_notify_broken_link (struct CadetPeer *peer, - const struct GNUNET_PeerIdentity *peer1, - const struct GNUNET_PeerIdentity *peer2); +struct GCP_MessageQueueManager * +GCP_request_mq (struct CadetPeer *cp, + GCP_MessageQueueNotificationCallback cb, + void *cb_cls); /** - * Count the number of known paths toward the peer. - * - * @param peer Peer to get path info. + * Test if @a cp has a core-level connection * - * @return Number of known paths. + * @param cp peer to test + * @return #GNUNET_YES if @a cp has a core-level connection */ -unsigned int -GCP_count_paths (const struct CadetPeer *peer); +int +GCP_has_core_connection (struct CadetPeer *cp); + /** - * Iterate over the paths to a peer. - * - * @param peer Peer to get path info. - * @param callback Function to call for every path. - * @param cls Closure for @a callback. + * Send the message in @a env via a @a mqm. Must only be called at + * most once after the respective + * #GCP_MessageQueueNotificationCallback was called with `available` + * set to #GNUNET_YES, and not after the callback was called with + * `available` set to #GNUNET_NO or #GNUNET_SYSERR. * - * @return Number of iterated paths. + * @param mqm message queue manager for the transmission + * @param env envelope with the message to send; must NOT + * yet have a #GNUNET_MQ_notify_sent() callback attached to it */ -unsigned int -GCP_iterate_paths (struct CadetPeer *peer, - GCP_path_iterator callback, - void *cls); +void +GCP_send (struct GCP_MessageQueueManager *mqm, + struct GNUNET_MQ_Envelope *env); /** - * Iterate all known peers. + * Send the message in @a env to @a cp, overriding queueing logic. + * This function should only be used to send error messages outside + * of flow and congestion control, similar to ICMP. Note that + * the envelope may be silently discarded as well. * - * @param iter Iterator. - * @param cls Closure for @c iter. + * @param cp peer to send the message to + * @param env envelope with the message to send */ void -GCP_iterate_all (GNUNET_CONTAINER_PeerMapIterator iter, - void *cls); +GCP_send_ooo (struct CadetPeer *cp, + struct GNUNET_MQ_Envelope *env); /** - * Get the static string for a peer ID. - * - * @param peer Peer. + * Stops message queue change notifications and sends a last message. + * In practice, this is implemented by sending that @a last_env + * message immediately (if any), ignoring queue order. * - * @return Static string for it's ID. + * @param mqm handle matching request to cancel + * @param last_env final message to transmit, or NULL */ -const char * -GCP_2s (const struct CadetPeer *peer); +void +GCP_request_mq_cancel (struct GCP_MessageQueueManager *mqm, + struct GNUNET_MQ_Envelope *last_env); /** - * Log all kinds of info about a peer. + * Set the message queue to @a mq for peer @a cp and notify watchers. * - * @param peer Peer. + * @param cp peer to modify + * @param mq message queue to set (can be NULL) */ void -GCP_debug (const struct CadetPeer *p, - enum GNUNET_ErrorType level); - +GCP_set_mq (struct CadetPeer *cp, + struct GNUNET_MQ_Handle *mq); -#if 0 /* keep Emacsens' auto-indent happy */ -{ -#endif -#ifdef __cplusplus -} -#endif -/* ifndef GNUNET_CADET_SERVICE_PEER_H */ #endif -/* end of gnunet-cadet-service_peer.h */ diff --git a/src/cadet/gnunet-service-cadet_tunnel.c b/src/cadet/gnunet-service-cadet_tunnel.c deleted file mode 100644 index a94e8f4ffe..0000000000 --- a/src/cadet/gnunet-service-cadet_tunnel.c +++ /dev/null @@ -1,3501 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013, 2017 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file cadet/gnunet-service-cadet_tunnel.c - * @brief logical links between CADET clients - * @author Bartlomiej Polot - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_signatures.h" -#include "gnunet_statistics_service.h" -#include "cadet_protocol.h" -#include "cadet_path.h" -#include "gnunet-service-cadet_tunnel.h" -#include "gnunet-service-cadet_connection.h" -#include "gnunet-service-cadet_channel.h" -#include "gnunet-service-cadet_peer.h" - -#define LOG(level, ...) GNUNET_log_from(level,"cadet-tun",__VA_ARGS__) -#define LOG2(level, ...) GNUNET_log_from_nocheck(level,"cadet-tun",__VA_ARGS__) - -#define REKEY_WAIT GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 5) - -#if !defined(GNUNET_CULL_LOGGING) - #define DUMP_KEYS_TO_STDERR GNUNET_YES -#else - #define DUMP_KEYS_TO_STDERR GNUNET_NO -#endif - -#define MIN_TUNNEL_BUFFER 8 -#define MAX_TUNNEL_BUFFER 64 -#define MAX_SKIPPED_KEYS 64 -#define MAX_KEY_GAP 256 -#define AX_HEADER_SIZE (sizeof (uint32_t) * 2\ - + sizeof (struct GNUNET_CRYPTO_EcdhePublicKey)) - -/******************************************************************************/ -/******************************** STRUCTS **********************************/ -/******************************************************************************/ - -struct CadetTChannel -{ - struct CadetTChannel *next; - struct CadetTChannel *prev; - struct CadetChannel *ch; -}; - - -/** - * Entry in list of connections used by tunnel, with metadata. - */ -struct CadetTConnection -{ - /** - * Next in DLL. - */ - struct CadetTConnection *next; - - /** - * Prev in DLL. - */ - struct CadetTConnection *prev; - - /** - * Connection handle. - */ - struct CadetConnection *c; - - /** - * Creation time, to keep oldest connection alive. - */ - struct GNUNET_TIME_Absolute created; - - /** - * Connection throughput, to keep fastest connection alive. - */ - uint32_t throughput; -}; - - -/** - * Struct to old keys for skipped messages while advancing the Axolotl ratchet. - */ -struct CadetTunnelSkippedKey -{ - /** - * DLL next. - */ - struct CadetTunnelSkippedKey *next; - - /** - * DLL prev. - */ - struct CadetTunnelSkippedKey *prev; - - /** - * When was this key stored (for timeout). - */ - struct GNUNET_TIME_Absolute timestamp; - - /** - * Header key. - */ - struct GNUNET_CRYPTO_SymmetricSessionKey HK; - - /** - * Message key. - */ - struct GNUNET_CRYPTO_SymmetricSessionKey MK; - - /** - * Key number for a given HK. - */ - unsigned int Kn; -}; - - -/** - * Axolotl data, according to https://github.com/trevp/axolotl/wiki . - */ -struct CadetTunnelAxolotl -{ - /** - * A (double linked) list of stored message keys and associated header keys - * for "skipped" messages, i.e. messages that have not been - * received despite the reception of more recent messages, (head). - */ - struct CadetTunnelSkippedKey *skipped_head; - - /** - * Skipped messages' keys DLL, tail. - */ - struct CadetTunnelSkippedKey *skipped_tail; - - /** - * Elements in @a skipped_head <-> @a skipped_tail. - */ - unsigned int skipped; - - /** - * 32-byte root key which gets updated by DH ratchet. - */ - struct GNUNET_CRYPTO_SymmetricSessionKey RK; - - /** - * 32-byte header key (send). - */ - struct GNUNET_CRYPTO_SymmetricSessionKey HKs; - - /** - * 32-byte header key (recv) - */ - struct GNUNET_CRYPTO_SymmetricSessionKey HKr; - - /** - * 32-byte next header key (send). - */ - struct GNUNET_CRYPTO_SymmetricSessionKey NHKs; - - /** - * 32-byte next header key (recv). - */ - struct GNUNET_CRYPTO_SymmetricSessionKey NHKr; - - /** - * 32-byte chain keys (used for forward-secrecy updating, send). - */ - struct GNUNET_CRYPTO_SymmetricSessionKey CKs; - - /** - * 32-byte chain keys (used for forward-secrecy updating, recv). - */ - struct GNUNET_CRYPTO_SymmetricSessionKey CKr; - - /** - * ECDH for key exchange (A0 / B0). - */ - struct GNUNET_CRYPTO_EcdhePrivateKey *kx_0; - - /** - * ECDH Ratchet key (send). - */ - struct GNUNET_CRYPTO_EcdhePrivateKey *DHRs; - - /** - * ECDH Ratchet key (recv). - */ - struct GNUNET_CRYPTO_EcdhePublicKey DHRr; - - /** - * Message number (reset to 0 with each new ratchet, next message to send). - */ - uint32_t Ns; - - /** - * Message number (reset to 0 with each new ratchet, next message to recv). - */ - uint32_t Nr; - - /** - * Previous message numbers (# of msgs sent under prev ratchet) - */ - uint32_t PNs; - - /** - * True (#GNUNET_YES) if we have to send a new ratchet key in next msg. - */ - int ratchet_flag; - - /** - * Number of messages recieved since our last ratchet advance. - * - If this counter = 0, we cannot send a new ratchet key in next msg. - * - If this counter > 0, we can (but don't yet have to) send a new key. - */ - unsigned int ratchet_allowed; - - /** - * Number of messages recieved since our last ratchet advance. - * - If this counter = 0, we cannot send a new ratchet key in next msg. - * - If this counter > 0, we can (but don't yet have to) send a new key. - */ - unsigned int ratchet_counter; - - /** - * When does this ratchet expire and a new one is triggered. - */ - struct GNUNET_TIME_Absolute ratchet_expiration; -}; - - -/** - * Struct containing all information regarding a tunnel to a peer. - */ -struct CadetTunnel -{ - /** - * Endpoint of the tunnel. - */ - struct CadetPeer *peer; - - /** - * Axolotl info. - */ - struct CadetTunnelAxolotl *ax; - - /** - * State of the tunnel connectivity. - */ - enum CadetTunnelCState cstate; - - /** - * State of the tunnel encryption. - */ - enum CadetTunnelEState estate; - - /** - * Peer's ephemeral key, to recreate @c e_key and @c d_key when own ephemeral - * key changes. - */ - struct GNUNET_CRYPTO_EcdhePublicKey peers_ephemeral_key; - - /** - * Encryption ("our") key. It is only "confirmed" if kx_ctx is NULL. - */ - struct GNUNET_CRYPTO_SymmetricSessionKey e_key; - - /** - * Decryption ("their") key. It is only "confirmed" if kx_ctx is NULL. - */ - struct GNUNET_CRYPTO_SymmetricSessionKey d_key; - - /** - * Task to start the rekey process. - */ - struct GNUNET_SCHEDULER_Task *rekey_task; - - /** - * Paths that are actively used to reach the destination peer. - */ - struct CadetTConnection *connection_head; - struct CadetTConnection *connection_tail; - - /** - * Next connection number. - */ - uint32_t next_cid; - - /** - * Channels inside this tunnel. - */ - struct CadetTChannel *channel_head; - struct CadetTChannel *channel_tail; - - /** - * Channel ID for the next created channel. - */ - struct GNUNET_CADET_ChannelTunnelNumber next_ctn; - - /** - * Destroy flag: if true, destroy on last message. - */ - struct GNUNET_SCHEDULER_Task * destroy_task; - - /** - * Queued messages, to transmit once tunnel gets connected. - */ - struct CadetTunnelDelayed *tq_head; - struct CadetTunnelDelayed *tq_tail; - - /** - * Task to trim connections if too many are present. - */ - struct GNUNET_SCHEDULER_Task * trim_connections_task; - - /** - * Ephemeral message in the queue (to avoid queueing more than one). - */ - struct CadetConnectionQueue *ephm_h; - - /** - * Pong message in the queue. - */ - struct CadetConnectionQueue *pong_h; -}; - - -/** - * Struct used to save messages in a non-ready tunnel to send once connected. - */ -struct CadetTunnelDelayed -{ - /** - * DLL - */ - struct CadetTunnelDelayed *next; - struct CadetTunnelDelayed *prev; - - /** - * Tunnel. - */ - struct CadetTunnel *t; - - /** - * Tunnel queue given to the channel to cancel request. Update on send_queued. - */ - struct CadetTunnelQueue *tq; - - /** - * Message to send. - */ - /* struct GNUNET_MessageHeader *msg; */ -}; - - -/** - * Handle for messages queued but not yet sent. - */ -struct CadetTunnelQueue -{ - /** - * Connection queue handle, to cancel if necessary. - */ - struct CadetConnectionQueue *cq; - - /** - * Handle in case message hasn't been given to a connection yet. - */ - struct CadetTunnelDelayed *tqd; - - /** - * Continuation to call once sent. - */ - GCT_sent cont; - - /** - * Closure for @c cont. - */ - void *cont_cls; -}; - - -/******************************************************************************/ -/******************************* GLOBALS ***********************************/ -/******************************************************************************/ - -/** - * Global handle to the statistics service. - */ -extern struct GNUNET_STATISTICS_Handle *stats; - -/** - * Local peer own ID (memory efficient handle). - */ -extern GNUNET_PEER_Id myid; - -/** - * Local peer own ID (full value). - */ -extern struct GNUNET_PeerIdentity my_full_id; - - -/** - * Don't try to recover tunnels if shutting down. - */ -extern int shutting_down; - - -/** - * Set of all tunnels, in order to trigger a new exchange on rekey. - * Indexed by peer's ID. - */ -static struct GNUNET_CONTAINER_MultiPeerMap *tunnels; - -/** - * Own Peer ID private key. - */ -const static struct GNUNET_CRYPTO_EddsaPrivateKey *id_key; - - -/******************************** AXOLOTL ************************************/ - -/** - * How many messages are needed to trigger a ratchet advance. - */ -static unsigned long long ratchet_messages; - -/** - * How long until we trigger a ratched advance. - */ -static struct GNUNET_TIME_Relative ratchet_time; - - -/******************************************************************************/ -/******************************** STATIC ***********************************/ -/******************************************************************************/ - -/** - * Get string description for tunnel connectivity state. - * - * @param cs Tunnel state. - * - * @return String representation. - */ -static const char * -cstate2s (enum CadetTunnelCState cs) -{ - static char buf[32]; - - switch (cs) - { - case CADET_TUNNEL_NEW: - return "CADET_TUNNEL_NEW"; - case CADET_TUNNEL_SEARCHING: - return "CADET_TUNNEL_SEARCHING"; - case CADET_TUNNEL_WAITING: - return "CADET_TUNNEL_WAITING"; - case CADET_TUNNEL_READY: - return "CADET_TUNNEL_READY"; - case CADET_TUNNEL_SHUTDOWN: - return "CADET_TUNNEL_SHUTDOWN"; - default: - SPRINTF (buf, "%u (UNKNOWN STATE)", cs); - return buf; - } - return ""; -} - - -/** - * Get string description for tunnel encryption state. - * - * @param es Tunnel state. - * - * @return String representation. - */ -static const char * -estate2s (enum CadetTunnelEState es) -{ - static char buf[32]; - - switch (es) - { - case CADET_TUNNEL_KEY_UNINITIALIZED: - return "CADET_TUNNEL_KEY_UNINITIALIZED"; - case CADET_TUNNEL_KEY_AX_SENT: - return "CADET_TUNNEL_KEY_AX_SENT"; - case CADET_TUNNEL_KEY_AX_AUTH_SENT: - return "CADET_TUNNEL_KEY_AX_AUTH_SENT"; - case CADET_TUNNEL_KEY_OK: - return "CADET_TUNNEL_KEY_OK"; - case CADET_TUNNEL_KEY_REKEY: - return "CADET_TUNNEL_KEY_REKEY"; - default: - SPRINTF (buf, "%u (UNKNOWN STATE)", es); - return buf; - } - return ""; -} - - -/** - * @brief Check if tunnel is ready to send traffic. - * - * Tunnel must be connected and with encryption correctly set up. - * - * @param t Tunnel to check. - * - * @return #GNUNET_YES if ready, #GNUNET_NO otherwise - */ -static int -is_ready (struct CadetTunnel *t) -{ - int ready; - int conn_ok; - int enc_ok; - - conn_ok = CADET_TUNNEL_READY == t->cstate; - enc_ok = CADET_TUNNEL_KEY_OK == t->estate - || CADET_TUNNEL_KEY_REKEY == t->estate - || CADET_TUNNEL_KEY_AX_AUTH_SENT == t->estate; - ready = conn_ok && enc_ok; - ready = ready || GCT_is_loopback (t); - return ready; -} - - -/** - * Get the channel's buffer. ONLY FOR NON-LOOPBACK CHANNELS!! - * - * @param tch Tunnel's channel handle. - * - * @return Amount of messages the channel can still buffer towards the client. - */ -static unsigned int -get_channel_buffer (const struct CadetTChannel *tch) -{ - int fwd; - - /* If channel is incoming, is terminal in the FWD direction and fwd is YES */ - fwd = GCCH_is_terminal (tch->ch, GNUNET_YES); - - return GCCH_get_buffer (tch->ch, fwd); -} - - -/** - * Get the channel's allowance status. - * - * @param tch Tunnel's channel handle. - * - * @return #GNUNET_YES if we allowed the client to send data to us. - */ -static int -get_channel_allowed (const struct CadetTChannel *tch) -{ - int fwd; - - /* If channel is outgoing, is origin in the FWD direction and fwd is YES */ - fwd = GCCH_is_origin (tch->ch, GNUNET_YES); - - return GCCH_get_allowed (tch->ch, fwd); -} - - -/** - * Get the connection's buffer. - * - * @param tc Tunnel's connection handle. - * - * @return Amount of messages the connection can still buffer. - */ -static unsigned int -get_connection_buffer (const struct CadetTConnection *tc) -{ - int fwd; - - /* If connection is outgoing, is origin in the FWD direction and fwd is YES */ - fwd = GCC_is_origin (tc->c, GNUNET_YES); - - return GCC_get_buffer (tc->c, fwd); -} - - -/** - * Get the connection's allowance. - * - * @param tc Tunnel's connection handle. - * - * @return Amount of messages we have allowed the next peer to send us. - */ -static unsigned int -get_connection_allowed (const struct CadetTConnection *tc) -{ - int fwd; - - /* If connection is outgoing, is origin in the FWD direction and fwd is YES */ - fwd = GCC_is_origin (tc->c, GNUNET_YES); - - return GCC_get_allowed (tc->c, fwd); -} - - -/** - * Create a new Axolotl ephemeral (ratchet) key. - * - * @param t Tunnel. - */ -static void -new_ephemeral (struct CadetTunnel *t) -{ - GNUNET_free_non_null (t->ax->DHRs); - t->ax->DHRs = GNUNET_CRYPTO_ecdhe_key_create(); - #if DUMP_KEYS_TO_STDERR - { - struct GNUNET_CRYPTO_EcdhePublicKey pub; - GNUNET_CRYPTO_ecdhe_key_get_public (t->ax->DHRs, &pub); - LOG (GNUNET_ERROR_TYPE_DEBUG, " new DHRs generated: pub %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &pub)); - } - #endif -} - - -/** - * Calculate HMAC. - * - * @param plaintext Content to HMAC. - * @param size Size of @c plaintext. - * @param iv Initialization vector for the message. - * @param key Key to use. - * @param hmac[out] Destination to store the HMAC. - */ -static void -t_hmac (const void *plaintext, size_t size, - uint32_t iv, const struct GNUNET_CRYPTO_SymmetricSessionKey *key, - struct GNUNET_ShortHashCode *hmac) -{ - static const char ctx[] = "cadet authentication key"; - struct GNUNET_CRYPTO_AuthKey auth_key; - struct GNUNET_HashCode hash; - -#if DUMP_KEYS_TO_STDERR - LOG (GNUNET_ERROR_TYPE_INFO, " HMAC %u bytes with key %s\n", size, - GNUNET_i2s ((struct GNUNET_PeerIdentity *) key)); -#endif - GNUNET_CRYPTO_hmac_derive_key (&auth_key, key, - &iv, sizeof (iv), - key, sizeof (*key), - ctx, sizeof (ctx), - NULL); - /* Two step: CADET_Hash is only 256 bits, HashCode is 512. */ - GNUNET_CRYPTO_hmac (&auth_key, plaintext, size, &hash); - GNUNET_memcpy (hmac, &hash, sizeof (*hmac)); -} - - -/** - * Perform a HMAC. - * - * @param key Key to use. - * @param hash[out] Resulting HMAC. - * @param source Source key material (data to HMAC). - * @param len Length of @a source. - */ -static void -t_ax_hmac_hash (struct GNUNET_CRYPTO_SymmetricSessionKey *key, - struct GNUNET_HashCode *hash, - void *source, unsigned int len) -{ - static const char ctx[] = "axolotl HMAC-HASH"; - struct GNUNET_CRYPTO_AuthKey auth_key; - - GNUNET_CRYPTO_hmac_derive_key (&auth_key, key, - ctx, sizeof (ctx), - NULL); - GNUNET_CRYPTO_hmac (&auth_key, source, len, hash); -} - - -/** - * Derive a key from a HMAC-HASH. - * - * @param key Key to use for the HMAC. - * @param out Key to generate. - * @param source Source key material (data to HMAC). - * @param len Length of @a source. - */ -static void -t_hmac_derive_key (struct GNUNET_CRYPTO_SymmetricSessionKey *key, - struct GNUNET_CRYPTO_SymmetricSessionKey *out, - void *source, unsigned int len) -{ - static const char ctx[] = "axolotl derive key"; - struct GNUNET_HashCode h; - - t_ax_hmac_hash (key, &h, source, len); - GNUNET_CRYPTO_kdf (out, sizeof (*out), ctx, sizeof (ctx), - &h, sizeof (h), NULL); -} - - -/** - * Encrypt data with the axolotl tunnel key. - * - * @param t Tunnel whose key to use. - * @param dst Destination for the encrypted data. - * @param src Source of the plaintext. Can overlap with @c dst. - * @param size Size of the plaintext. - * - * @return Size of the encrypted data. - */ -static int -t_ax_encrypt (struct CadetTunnel *t, void *dst, const void *src, size_t size) -{ - struct GNUNET_CRYPTO_SymmetricSessionKey MK; - struct GNUNET_CRYPTO_SymmetricInitializationVector iv; - struct CadetTunnelAxolotl *ax; - size_t out_size; - - CADET_TIMING_START; - - ax = t->ax; - ax->ratchet_counter++; - if (GNUNET_YES == ax->ratchet_allowed - && (ratchet_messages <= ax->ratchet_counter - || 0 == GNUNET_TIME_absolute_get_remaining (ax->ratchet_expiration).rel_value_us)) - { - ax->ratchet_flag = GNUNET_YES; - } - - if (GNUNET_YES == ax->ratchet_flag) - { - /* Advance ratchet */ - struct GNUNET_CRYPTO_SymmetricSessionKey keys[3]; - struct GNUNET_HashCode dh; - struct GNUNET_HashCode hmac; - static const char ctx[] = "axolotl ratchet"; - - new_ephemeral (t); - ax->HKs = ax->NHKs; - - /* RK, NHKs, CKs = KDF( HMAC-HASH(RK, DH(DHRs, DHRr)) ) */ - GNUNET_CRYPTO_ecc_ecdh (ax->DHRs, &ax->DHRr, &dh); - t_ax_hmac_hash (&ax->RK, &hmac, &dh, sizeof (dh)); - GNUNET_CRYPTO_kdf (keys, sizeof (keys), ctx, sizeof (ctx), - &hmac, sizeof (hmac), NULL); - ax->RK = keys[0]; - ax->NHKs = keys[1]; - ax->CKs = keys[2]; - - ax->PNs = ax->Ns; - ax->Ns = 0; - ax->ratchet_flag = GNUNET_NO; - ax->ratchet_allowed = GNUNET_NO; - ax->ratchet_counter = 0; - ax->ratchet_expiration = - GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(), ratchet_time); - } - - t_hmac_derive_key (&ax->CKs, &MK, "0", 1); - GNUNET_CRYPTO_symmetric_derive_iv (&iv, &MK, NULL, 0, NULL); - - #if DUMP_KEYS_TO_STDERR - LOG (GNUNET_ERROR_TYPE_DEBUG, " CKs: %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &ax->CKs)); - LOG (GNUNET_ERROR_TYPE_INFO, " AX_ENC with key %u: %s\n", ax->Ns, - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &MK)); - #endif - - out_size = GNUNET_CRYPTO_symmetric_encrypt (src, size, &MK, &iv, dst); - t_hmac_derive_key (&ax->CKs, &ax->CKs, "1", 1); - - CADET_TIMING_END; - - return out_size; -} - - -/** - * Decrypt data with the axolotl tunnel key. - * - * @param t Tunnel whose key to use. - * @param dst Destination for the decrypted data. - * @param src Source of the ciphertext. Can overlap with @c dst. - * @param size Size of the ciphertext. - * - * @return Size of the decrypted data. - */ -static int -t_ax_decrypt (struct CadetTunnel *t, void *dst, const void *src, size_t size) -{ - struct GNUNET_CRYPTO_SymmetricSessionKey MK; - struct GNUNET_CRYPTO_SymmetricInitializationVector iv; - struct CadetTunnelAxolotl *ax; - size_t out_size; - - CADET_TIMING_START; - - ax = t->ax; - - t_hmac_derive_key (&ax->CKr, &MK, "0", 1); - GNUNET_CRYPTO_symmetric_derive_iv (&iv, &MK, NULL, 0, NULL); - - #if DUMP_KEYS_TO_STDERR - LOG (GNUNET_ERROR_TYPE_DEBUG, " CKr: %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &ax->CKr)); - LOG (GNUNET_ERROR_TYPE_INFO, " AX_DEC with key %u: %s\n", ax->Nr, - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &MK)); - #endif - - GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader)); - out_size = GNUNET_CRYPTO_symmetric_decrypt (src, size, &MK, &iv, dst); - GNUNET_assert (out_size == size); - - t_hmac_derive_key (&ax->CKr, &ax->CKr, "1", 1); - - CADET_TIMING_END; - - return out_size; -} - - -/** - * Encrypt header with the axolotl header key. - * - * @param t Tunnel whose key to use. - * @param msg Message whose header to encrypt. - */ -static void -t_h_encrypt (struct CadetTunnel *t, struct GNUNET_CADET_TunnelEncryptedMessage *msg) -{ - struct GNUNET_CRYPTO_SymmetricInitializationVector iv; - struct CadetTunnelAxolotl *ax; - size_t out_size; - - CADET_TIMING_START; - ax = t->ax; - GNUNET_CRYPTO_symmetric_derive_iv (&iv, &ax->HKs, NULL, 0, NULL); - - #if DUMP_KEYS_TO_STDERR - LOG (GNUNET_ERROR_TYPE_INFO, " AX_ENC_H with key %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &ax->HKs)); - #endif - - out_size = GNUNET_CRYPTO_symmetric_encrypt (&msg->Ns, AX_HEADER_SIZE, - &ax->HKs, &iv, &msg->Ns); - - GNUNET_assert (AX_HEADER_SIZE == out_size); - CADET_TIMING_END; -} - - -/** - * Decrypt header with the current axolotl header key. - * - * @param t Tunnel whose current ax HK to use. - * @param src Message whose header to decrypt. - * @param dst Where to decrypt header to. - */ -static void -t_h_decrypt (struct CadetTunnel *t, const struct GNUNET_CADET_TunnelEncryptedMessage *src, - struct GNUNET_CADET_TunnelEncryptedMessage *dst) -{ - struct GNUNET_CRYPTO_SymmetricInitializationVector iv; - struct CadetTunnelAxolotl *ax; - size_t out_size; - - CADET_TIMING_START; - - ax = t->ax; - GNUNET_CRYPTO_symmetric_derive_iv (&iv, &ax->HKr, NULL, 0, NULL); - - #if DUMP_KEYS_TO_STDERR - LOG (GNUNET_ERROR_TYPE_INFO, " AX_DEC_H with key %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &ax->HKr)); - #endif - - out_size = GNUNET_CRYPTO_symmetric_decrypt (&src->Ns, AX_HEADER_SIZE, - &ax->HKr, &iv, &dst->Ns); - - GNUNET_assert (AX_HEADER_SIZE == out_size); - - CADET_TIMING_END; -} - - -/** - * Decrypt and verify data with the appropriate tunnel key and verify that the - * data has not been altered since it was sent by the remote peer. - * - * @param t Tunnel whose key to use. - * @param dst Destination for the plaintext. - * @param src Source of the message. Can overlap with @c dst. - * @param size Size of the message. - * - * @return Size of the decrypted data, -1 if an error was encountered. - */ -static int -try_old_ax_keys (struct CadetTunnel *t, void *dst, - const struct GNUNET_CADET_TunnelEncryptedMessage *src, size_t size) -{ - struct CadetTunnelSkippedKey *key; - struct GNUNET_ShortHashCode *hmac; - struct GNUNET_CRYPTO_SymmetricInitializationVector iv; - struct GNUNET_CADET_TunnelEncryptedMessage plaintext_header; - struct GNUNET_CRYPTO_SymmetricSessionKey *valid_HK; - size_t esize; - size_t res; - size_t len; - unsigned int N; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying old keys\n"); - hmac = &plaintext_header.hmac; - esize = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage); - - /* Find a correct Header Key */ - for (key = t->ax->skipped_head; NULL != key; key = key->next) - { - #if DUMP_KEYS_TO_STDERR - LOG (GNUNET_ERROR_TYPE_DEBUG, " Trying hmac with key %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &key->HK)); - #endif - t_hmac (&src->Ns, AX_HEADER_SIZE + esize, 0, &key->HK, hmac); - if (0 == memcmp (hmac, &src->hmac, sizeof (*hmac))) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " hmac correct\n"); - valid_HK = &key->HK; - break; - } - } - if (NULL == key) - return -1; - - /* Should've been checked in -cadet_connection.c handle_cadet_encrypted. */ - GNUNET_assert (size > sizeof (struct GNUNET_CADET_TunnelEncryptedMessage)); - len = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage); - GNUNET_assert (len >= sizeof (struct GNUNET_MessageHeader)); - - /* Decrypt header */ - GNUNET_CRYPTO_symmetric_derive_iv (&iv, &key->HK, NULL, 0, NULL); - res = GNUNET_CRYPTO_symmetric_decrypt (&src->Ns, AX_HEADER_SIZE, - &key->HK, &iv, &plaintext_header.Ns); - GNUNET_assert (AX_HEADER_SIZE == res); - LOG (GNUNET_ERROR_TYPE_DEBUG, " Message %u, previous: %u\n", - ntohl (plaintext_header.Ns), ntohl (plaintext_header.PNs)); - - /* Find the correct Message Key */ - N = ntohl (plaintext_header.Ns); - while (NULL != key && N != key->Kn) - key = key->next; - if (NULL == key || 0 != memcmp (&key->HK, valid_HK, sizeof (*valid_HK))) - return -1; - - #if DUMP_KEYS_TO_STDERR - LOG (GNUNET_ERROR_TYPE_INFO, " AX_DEC_H with skipped key %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &key->HK)); - LOG (GNUNET_ERROR_TYPE_INFO, " AX_DEC with skipped key %u: %s\n", - key->Kn, GNUNET_i2s ((struct GNUNET_PeerIdentity *) &key->MK)); - #endif - - /* Decrypt payload */ - GNUNET_CRYPTO_symmetric_derive_iv (&iv, &key->MK, NULL, 0, NULL); - res = GNUNET_CRYPTO_symmetric_decrypt (&src[1], len, &key->MK, &iv, dst); - - /* Remove key */ - GNUNET_CONTAINER_DLL_remove (t->ax->skipped_head, t->ax->skipped_tail, key); - t->ax->skipped--; - GNUNET_free (key); /* GNUNET_free overwrites memory with 0xbaadf00d */ - - return res; -} - - -/** - * Delete a key from the list of skipped keys. - * - * @param t Tunnel to delete from. - * @param HKr Header Key to use. - */ -static void -store_skipped_key (struct CadetTunnel *t, - const struct GNUNET_CRYPTO_SymmetricSessionKey *HKr) -{ - struct CadetTunnelSkippedKey *key; - - key = GNUNET_new (struct CadetTunnelSkippedKey); - key->timestamp = GNUNET_TIME_absolute_get (); - key->Kn = t->ax->Nr; - key->HK = t->ax->HKr; - t_hmac_derive_key (&t->ax->CKr, &key->MK, "0", 1); - #if DUMP_KEYS_TO_STDERR - LOG (GNUNET_ERROR_TYPE_DEBUG, " storing MK for Nr %u: %s\n", - key->Kn, GNUNET_i2s ((struct GNUNET_PeerIdentity *) &key->MK)); - LOG (GNUNET_ERROR_TYPE_DEBUG, " for CKr: %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &t->ax->CKr)); - #endif - t_hmac_derive_key (&t->ax->CKr, &t->ax->CKr, "1", 1); - GNUNET_CONTAINER_DLL_insert (t->ax->skipped_head, t->ax->skipped_tail, key); - t->ax->Nr++; - t->ax->skipped++; -} - - -/** - * Delete a key from the list of skipped keys. - * - * @param t Tunnel to delete from. - * @param key Key to delete. - */ -static void -delete_skipped_key (struct CadetTunnel *t, struct CadetTunnelSkippedKey *key) -{ - GNUNET_CONTAINER_DLL_remove (t->ax->skipped_head, t->ax->skipped_tail, key); - GNUNET_free (key); - t->ax->skipped--; -} - - -/** - * Stage skipped AX keys and calculate the message key. - * - * Stores each HK and MK for skipped messages. - * - * @param t Tunnel where to stage the keys. - * @param HKr Header key. - * @param Np Received meesage number. - * - * @return GNUNET_OK if keys were stored. - * GNUNET_SYSERR if an error ocurred (Np not expected). - */ -static int -store_ax_keys (struct CadetTunnel *t, - const struct GNUNET_CRYPTO_SymmetricSessionKey *HKr, - uint32_t Np) -{ - int gap; - - - gap = Np - t->ax->Nr; - LOG (GNUNET_ERROR_TYPE_INFO, "Storing keys [%u, %u)\n", t->ax->Nr, Np); - if (MAX_KEY_GAP < gap) - { - /* Avoid DoS (forcing peer to do 2*33 chain HMAC operations) */ - /* TODO: start new key exchange on return */ - GNUNET_break_op (0); - LOG (GNUNET_ERROR_TYPE_WARNING, "Got message %u, expected %u+\n", - Np, t->ax->Nr); - return GNUNET_SYSERR; - } - if (0 > gap) - { - /* Delayed message: don't store keys, flag to try old keys. */ - return GNUNET_SYSERR; - } - - while (t->ax->Nr < Np) - store_skipped_key (t, HKr); - - while (t->ax->skipped > MAX_SKIPPED_KEYS) - delete_skipped_key (t, t->ax->skipped_tail); - - return GNUNET_OK; -} - - -/** - * Decrypt and verify data with the appropriate tunnel key and verify that the - * data has not been altered since it was sent by the remote peer. - * - * @param t Tunnel whose key to use. - * @param dst Destination for the plaintext. - * @param src Source of the message. Can overlap with @c dst. - * @param size Size of the message. - * - * @return Size of the decrypted data, -1 if an error was encountered. - */ -static int -t_ax_decrypt_and_validate (struct CadetTunnel *t, void *dst, - const struct GNUNET_CADET_TunnelEncryptedMessage *src, - size_t size) -{ - struct CadetTunnelAxolotl *ax; - struct GNUNET_ShortHashCode msg_hmac; - struct GNUNET_HashCode hmac; - struct GNUNET_CADET_TunnelEncryptedMessage plaintext_header; - uint32_t Np; - uint32_t PNp; - size_t esize; /* Size of encryped payload */ - size_t osize; /* Size of output (decrypted payload) */ - - esize = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage); - ax = t->ax; - if (NULL == ax) - return -1; - - /* Try current HK */ - t_hmac (&src->Ns, AX_HEADER_SIZE + esize, 0, &ax->HKr, &msg_hmac); - if (0 != memcmp (&msg_hmac, &src->hmac, sizeof (msg_hmac))) - { - static const char ctx[] = "axolotl ratchet"; - struct GNUNET_CRYPTO_SymmetricSessionKey keys[3]; /* RKp, NHKp, CKp */ - struct GNUNET_CRYPTO_SymmetricSessionKey HK; - struct GNUNET_HashCode dh; - struct GNUNET_CRYPTO_EcdhePublicKey *DHRp; - - /* Try Next HK */ - LOG (GNUNET_ERROR_TYPE_DEBUG, " trying next HK\n"); - t_hmac (&src->Ns, AX_HEADER_SIZE + esize, 0, &ax->NHKr, &msg_hmac); - if (0 != memcmp (&msg_hmac, &src->hmac, sizeof (msg_hmac))) - { - /* Try the skipped keys, if that fails, we're out of luck. */ - return try_old_ax_keys (t, dst, src, size); - } - LOG (GNUNET_ERROR_TYPE_INFO, "next HK worked\n"); - - HK = ax->HKr; - ax->HKr = ax->NHKr; - t_h_decrypt (t, src, &plaintext_header); - Np = ntohl (plaintext_header.Ns); - PNp = ntohl (plaintext_header.PNs); - DHRp = &plaintext_header.DHRs; - store_ax_keys (t, &HK, PNp); - - /* RKp, NHKp, CKp = KDF (HMAC-HASH (RK, DH (DHRp, DHRs))) */ - GNUNET_CRYPTO_ecc_ecdh (ax->DHRs, DHRp, &dh); - t_ax_hmac_hash (&ax->RK, &hmac, &dh, sizeof (dh)); - GNUNET_CRYPTO_kdf (keys, sizeof (keys), ctx, sizeof (ctx), - &hmac, sizeof (hmac), NULL); - - /* Commit "purported" keys */ - ax->RK = keys[0]; - ax->NHKr = keys[1]; - ax->CKr = keys[2]; - ax->DHRr = *DHRp; - ax->Nr = 0; - ax->ratchet_allowed = GNUNET_YES; - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "current HK\n"); - t_h_decrypt (t, src, &plaintext_header); - Np = ntohl (plaintext_header.Ns); - PNp = ntohl (plaintext_header.PNs); - } - LOG (GNUNET_ERROR_TYPE_INFO, " got AX Nr %u\n", Np); - if (Np != ax->Nr) - if (GNUNET_OK != store_ax_keys (t, &ax->HKr, Np)) - /* Try the skipped keys, if that fails, we're out of luck. */ - return try_old_ax_keys (t, dst, src, size); - - osize = t_ax_decrypt (t, dst, &src[1], esize); - ax->Nr = Np + 1; - - if (osize != esize) - { - GNUNET_break_op (0); - return -1; - } - - return osize; -} - - -/** - * Pick a connection on which send the next data message. - * - * @param t Tunnel on which to send the message. - * - * @return The connection on which to send the next message. - */ -static struct CadetConnection * -tunnel_get_connection (struct CadetTunnel *t) -{ - struct CadetTConnection *iter; - struct CadetConnection *best; - unsigned int qn; - unsigned int lowest_q; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "tunnel_get_connection %s\n", GCT_2s (t)); - best = NULL; - lowest_q = UINT_MAX; - for (iter = t->connection_head; NULL != iter; iter = iter->next) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " connection %s: %u\n", - GCC_2s (iter->c), GCC_get_state (iter->c)); - if (CADET_CONNECTION_READY == GCC_get_state (iter->c)) - { - qn = GCC_get_qn (iter->c, GCC_is_origin (iter->c, GNUNET_YES)); - LOG (GNUNET_ERROR_TYPE_DEBUG, " q_n %u, \n", qn); - if (qn < lowest_q) - { - best = iter->c; - lowest_q = qn; - } - } - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " selected: connection %s\n", GCC_2s (best)); - return best; -} - - -/** - * Callback called when a queued message is sent. - * - * Calculates the average time and connection packet tracking. - * - * @param cls Closure (TunnelQueue handle). - * @param c Connection this message was on. - * @param q Connection queue handle (unused). - * @param type Type of message sent. - * @param fwd Was this a FWD going message? - * @param size Size of the message. - */ -static void -tun_message_sent (void *cls, - struct CadetConnection *c, - struct CadetConnectionQueue *q, - uint16_t type, int fwd, size_t size) -{ - struct CadetTunnelQueue *qt = cls; - struct CadetTunnel *t; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "tun_message_sent\n"); - - GNUNET_assert (NULL != qt->cont); - t = NULL == c ? NULL : GCC_get_tunnel (c); - qt->cont (qt->cont_cls, t, qt, type, size); - GNUNET_free (qt); -} - - -static unsigned int -count_queued_data (const struct CadetTunnel *t) -{ - struct CadetTunnelDelayed *iter; - unsigned int count; - - for (count = 0, iter = t->tq_head; iter != NULL; iter = iter->next) - count++; - - return count; -} - -/** - * Delete a queued message: either was sent or the channel was destroyed - * before the tunnel's key exchange had a chance to finish. - * - * @param tqd Delayed queue handle. - */ -static void -unqueue_data (struct CadetTunnelDelayed *tqd) -{ - GNUNET_CONTAINER_DLL_remove (tqd->t->tq_head, tqd->t->tq_tail, tqd); - GNUNET_free (tqd); -} - - -/** - * Cache a message to be sent once tunnel is online. - * - * @param t Tunnel to hold the message. - * @param msg Message itself (copy will be made). - */ -static struct CadetTunnelDelayed * -queue_data (struct CadetTunnel *t, const struct GNUNET_MessageHeader *msg) -{ - struct CadetTunnelDelayed *tqd; - uint16_t size = ntohs (msg->size); - - LOG (GNUNET_ERROR_TYPE_DEBUG, "queue data on Tunnel %s\n", GCT_2s (t)); - - GNUNET_assert (GNUNET_NO == is_ready (t)); - - tqd = GNUNET_malloc (sizeof (struct CadetTunnelDelayed) + size); - - tqd->t = t; - GNUNET_memcpy (&tqd[1], msg, size); - GNUNET_CONTAINER_DLL_insert_tail (t->tq_head, t->tq_tail, tqd); - return tqd; -} - - -/** - * Sends an already built message on a tunnel, encrypting it and - * choosing the best connection. - * - * @param message Message to send. Function modifies it. - * @param t Tunnel on which this message is transmitted. - * @param c Connection to use (autoselect if NULL). - * @param force Force the tunnel to take the message (buffer overfill). - * @param cont Continuation to call once message is really sent. - * @param cont_cls Closure for @c cont. - * @param existing_q In case this a transmission of previously queued data, - * this should be TunnelQueue given to the client. - * Otherwise, NULL. - * @return Handle to cancel message. - * NULL if @c cont is NULL or an error happens and message is dropped. - */ -static struct CadetTunnelQueue * -send_prebuilt_message (const struct GNUNET_MessageHeader *message, - struct CadetTunnel *t, - struct CadetConnection *c, - int force, - GCT_sent cont, - void *cont_cls, - struct CadetTunnelQueue *existing_q) -{ - struct GNUNET_MessageHeader *msg; - struct GNUNET_CADET_TunnelEncryptedMessage *ax_msg; - struct CadetTunnelQueue *tq; - size_t size = ntohs (message->size); - char cbuf[sizeof (struct GNUNET_CADET_TunnelEncryptedMessage) + size] GNUNET_ALIGN; - size_t esize; - uint16_t type; - int fwd; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "GMT Send on Tunnel %s\n", GCT_2s (t)); - - if (GNUNET_NO == is_ready (t)) - { - struct CadetTunnelDelayed *tqd; - /* A non null existing_q indicates sending of queued data. - * Should only happen after tunnel becomes ready. - */ - GNUNET_assert (NULL == existing_q); - tqd = queue_data (t, message); - if (NULL == cont) - return NULL; - tq = GNUNET_new (struct CadetTunnelQueue); - tq->tqd = tqd; - tqd->tq = tq; - tq->cont = cont; - tq->cont_cls = cont_cls; - return tq; - } - - GNUNET_assert (GNUNET_NO == GCT_is_loopback (t)); - - ax_msg = (struct GNUNET_CADET_TunnelEncryptedMessage *) cbuf; - msg = &ax_msg->header; - msg->size = htons (sizeof (struct GNUNET_CADET_TunnelEncryptedMessage) + size); - msg->type = htons (GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED); - esize = t_ax_encrypt (t, &ax_msg[1], message, size); - ax_msg->Ns = htonl (t->ax->Ns++); - ax_msg->PNs = htonl (t->ax->PNs); - GNUNET_CRYPTO_ecdhe_key_get_public (t->ax->DHRs, &ax_msg->DHRs); - t_h_encrypt (t, ax_msg); - t_hmac (&ax_msg->Ns, AX_HEADER_SIZE + esize, 0, &t->ax->HKs, &ax_msg->hmac); - GNUNET_assert (esize == size); - - if (NULL == c) - c = tunnel_get_connection (t); - if (NULL == c) - { - /* Why is tunnel 'ready'? Should have been queued! */ - if (NULL != t->destroy_task) - { - GNUNET_break (0); - GCT_debug (t, GNUNET_ERROR_TYPE_WARNING); - } - return NULL; /* Drop... */ - } - fwd = GCC_is_origin (c, GNUNET_YES); - ax_msg->cid = *GCC_get_id (c); - ax_msg->cemi = GCC_get_pid (c, fwd); - - type = htons (message->type); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending message of type %s with CEMI %u and CID %s\n", - GC_m2s (type), - htonl (ax_msg->cemi.pid), - GNUNET_sh2s (&ax_msg->cid.connection_of_tunnel)); - - if (NULL == cont) - { - (void) GCC_send_prebuilt_message (msg, - type, - ax_msg->cemi, - c, - fwd, - force, NULL, NULL); - return NULL; - } - if (NULL == existing_q) - { - tq = GNUNET_new (struct CadetTunnelQueue); /* FIXME valgrind: leak*/ - } - else - { - tq = existing_q; - tq->tqd = NULL; - } - tq->cont = cont; - tq->cont_cls = cont_cls; - tq->cq = GCC_send_prebuilt_message (msg, - type, - ax_msg->cemi, - c, - fwd, - force, - &tun_message_sent, tq); - GNUNET_assert (NULL != tq->cq); - - return tq; -} - - -/** - * Send all cached messages that we can, tunnel is online. - * - * @param t Tunnel that holds the messages. Cannot be loopback. - */ -static void -send_queued_data (struct CadetTunnel *t) -{ - struct CadetTunnelDelayed *tqd; - struct CadetTunnelDelayed *next; - unsigned int room; - - LOG (GNUNET_ERROR_TYPE_INFO, "Send queued data, tunnel %s\n", GCT_2s (t)); - - if (GCT_is_loopback (t)) - { - GNUNET_break (0); - return; - } - - if (GNUNET_NO == is_ready (t)) - { - LOG (GNUNET_ERROR_TYPE_WARNING, " not ready yet: %s/%s\n", - estate2s (t->estate), cstate2s (t->cstate)); - return; - } - - room = GCT_get_connections_buffer (t); - LOG (GNUNET_ERROR_TYPE_DEBUG, " buffer space: %u\n", room); - LOG (GNUNET_ERROR_TYPE_DEBUG, " tq head: %p\n", t->tq_head); - for (tqd = t->tq_head; NULL != tqd && room > 0; tqd = next) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " sending queued data\n"); - next = tqd->next; - room--; - send_prebuilt_message ((struct GNUNET_MessageHeader *) &tqd[1], - tqd->t, NULL, GNUNET_YES, - NULL != tqd->tq ? tqd->tq->cont : NULL, - NULL != tqd->tq ? tqd->tq->cont_cls : NULL, - tqd->tq); - unqueue_data (tqd); - } - LOG (GNUNET_ERROR_TYPE_DEBUG, "GCT_send_queued_data end\n", GCP_2s (t->peer)); -} - - -/** - * @brief Resend the KX until we complete the handshake. - * - * @param cls Closure (tunnel). - */ -static void -kx_resend (void *cls) -{ - struct CadetTunnel *t = cls; - - t->rekey_task = NULL; - if (CADET_TUNNEL_KEY_OK == t->estate) - { - /* Should have been canceled on estate change */ - GNUNET_break (0); - return; - } - - GCT_send_kx (t, CADET_TUNNEL_KEY_AX_SENT >= t->estate); -} - - -/** - * Callback called when a queued message is sent. - * - * @param cls Closure. - * @param c Connection this message was on. - * @param type Type of message sent. - * @param fwd Was this a FWD going message? - * @param size Size of the message. - */ -static void -ephm_sent (void *cls, - struct CadetConnection *c, - struct CadetConnectionQueue *q, - uint16_t type, int fwd, size_t size) -{ - struct CadetTunnel *t = cls; - LOG (GNUNET_ERROR_TYPE_DEBUG, "ephemeral sent %s\n", GC_m2s (type)); - - t->ephm_h = NULL; - - if (CADET_TUNNEL_KEY_OK == t->estate) - return; - - if (NULL != t->rekey_task) - { - GNUNET_break (0); - GCT_debug (t, GNUNET_ERROR_TYPE_WARNING); - GNUNET_SCHEDULER_cancel (t->rekey_task); - } - t->rekey_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &kx_resend, t); - -} - - -/** - * Called only on shutdown, destroy every tunnel. - * - * @param cls Closure (unused). - * @param key Current public key. - * @param value Value in the hash map (tunnel). - * - * @return #GNUNET_YES, so we should continue to iterate, - */ -static int -destroy_iterator (void *cls, - const struct GNUNET_PeerIdentity *key, - void *value) -{ - struct CadetTunnel *t = value; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "GCT_shutdown destroying tunnel at %p\n", t); - GCT_destroy (t); - return GNUNET_YES; -} - - -/** - * Notify remote peer that we don't know a channel he is talking about, - * probably CHANNEL_DESTROY was missed. - * - * @param t Tunnel on which to notify. - * @param gid ID of the channel. - */ -static void -send_channel_destroy (struct CadetTunnel *t, - struct GNUNET_CADET_ChannelTunnelNumber gid) -{ - struct GNUNET_CADET_ChannelManageMessage msg; - - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY); - msg.header.size = htons (sizeof (msg)); - msg.ctn = gid; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "WARNING destroying unknown channel %u on tunnel %s\n", - ntohl (gid.cn), - GCT_2s (t)); - send_prebuilt_message (&msg.header, t, NULL, GNUNET_YES, NULL, NULL, NULL); -} - - -/** - * Demultiplex data per channel and call appropriate channel handler. - * - * @param t Tunnel on which the data came. - * @param msg Data message. - * @param fwd Is this message fwd? This only is meaningful in loopback channels. - * #GNUNET_YES if message is FWD on the respective channel (loopback) - * #GNUNET_NO if message is BCK on the respective channel (loopback) - * #GNUNET_SYSERR if message on a one-ended channel (remote) - */ -static void -handle_data (struct CadetTunnel *t, - const struct GNUNET_CADET_ChannelAppDataMessage *msg, - int fwd) -{ - struct CadetChannel *ch; - char buf[128]; - size_t size; - uint16_t type; - - /* Check size */ - size = ntohs (msg->header.size); - if (size < - sizeof (struct GNUNET_CADET_ChannelAppDataMessage) + - sizeof (struct GNUNET_MessageHeader)) - { - GNUNET_break (0); - return; - } - type = ntohs (msg[1].header.type); - LOG (GNUNET_ERROR_TYPE_DEBUG, " payload of type %s\n", GC_m2s (type)); - SPRINTF (buf, "# received payload of type %hu", type); - GNUNET_STATISTICS_update (stats, buf, 1, GNUNET_NO); - - - /* Check channel */ - ch = GCT_get_channel (t, msg->ctn); - if (NULL == ch) - { - GNUNET_STATISTICS_update (stats, - "# data on unknown channel", - 1, - GNUNET_NO); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "channel 0x%X unknown\n", - ntohl (msg->ctn.cn)); - send_channel_destroy (t, msg->ctn); - return; - } - - GCCH_handle_data (ch, msg, fwd); -} - - -/** - * Demultiplex data ACKs per channel and update appropriate channel buffer info. - * - * @param t Tunnel on which the DATA ACK came. - * @param msg DATA ACK message. - * @param fwd Is this message fwd? This only is meaningful in loopback channels. - * #GNUNET_YES if message is FWD on the respective channel (loopback) - * #GNUNET_NO if message is BCK on the respective channel (loopback) - * #GNUNET_SYSERR if message on a one-ended channel (remote) - */ -static void -handle_data_ack (struct CadetTunnel *t, - const struct GNUNET_CADET_ChannelDataAckMessage *msg, - int fwd) -{ - struct CadetChannel *ch; - size_t size; - - /* Check size */ - size = ntohs (msg->header.size); - if (size != sizeof (struct GNUNET_CADET_ChannelDataAckMessage)) - { - GNUNET_break (0); - return; - } - - /* Check channel */ - ch = GCT_get_channel (t, msg->ctn); - if (NULL == ch) - { - GNUNET_STATISTICS_update (stats, "# data ack on unknown channel", - 1, GNUNET_NO); - LOG (GNUNET_ERROR_TYPE_DEBUG, "WARNING channel %u unknown\n", - ntohl (msg->ctn.cn)); - return; - } - - GCCH_handle_data_ack (ch, msg, fwd); -} - - -/** - * Handle channel create. - * - * @param t Tunnel on which the message came. - * @param msg ChannelCreate message. - */ -static void -handle_ch_create (struct CadetTunnel *t, - const struct GNUNET_CADET_ChannelOpenMessage *msg) -{ - struct CadetChannel *ch; - size_t size; - - /* Check size */ - size = ntohs (msg->header.size); - if (size != sizeof (struct GNUNET_CADET_ChannelOpenMessage)) - { - GNUNET_break_op (0); - return; - } - - /* Check channel */ - ch = GCT_get_channel (t, msg->ctn); - if (NULL != ch && ! GCT_is_loopback (t)) - { - /* Probably a retransmission, safe to ignore */ - LOG (GNUNET_ERROR_TYPE_DEBUG, " already exists...\n"); - } - ch = GCCH_handle_create (t, msg); - if (NULL != ch) - GCT_add_channel (t, ch); -} - - - -/** - * Handle channel NACK: check correctness and call channel handler for NACKs. - * - * @param t Tunnel on which the NACK came. - * @param msg NACK message. - */ -static void -handle_ch_nack (struct CadetTunnel *t, - const struct GNUNET_CADET_ChannelManageMessage *msg) -{ - struct CadetChannel *ch; - size_t size; - - /* Check size */ - size = ntohs (msg->header.size); - if (size != sizeof (struct GNUNET_CADET_ChannelManageMessage)) - { - GNUNET_break (0); - return; - } - - /* Check channel */ - ch = GCT_get_channel (t, msg->ctn); - if (NULL == ch) - { - GNUNET_STATISTICS_update (stats, "# channel NACK on unknown channel", - 1, GNUNET_NO); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "WARNING channel %u unknown\n", - ntohl (msg->ctn.cn)); - return; - } - - GCCH_handle_nack (ch); -} - - -/** - * Handle a CHANNEL ACK (SYNACK/ACK). - * - * @param t Tunnel on which the CHANNEL ACK came. - * @param msg CHANNEL ACK message. - * @param fwd Is this message fwd? This only is meaningful in loopback channels. - * #GNUNET_YES if message is FWD on the respective channel (loopback) - * #GNUNET_NO if message is BCK on the respective channel (loopback) - * #GNUNET_SYSERR if message on a one-ended channel (remote) - */ -static void -handle_ch_ack (struct CadetTunnel *t, - const struct GNUNET_CADET_ChannelManageMessage *msg, - int fwd) -{ - struct CadetChannel *ch; - size_t size; - - /* Check size */ - size = ntohs (msg->header.size); - if (size != sizeof (struct GNUNET_CADET_ChannelManageMessage)) - { - GNUNET_break (0); - return; - } - - /* Check channel */ - ch = GCT_get_channel (t, msg->ctn); - if (NULL == ch) - { - GNUNET_STATISTICS_update (stats, - "# channel ack on unknown channel", - 1, - GNUNET_NO); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "WARNING channel %u unknown\n", - ntohl (msg->ctn.cn)); - return; - } - - GCCH_handle_ack (ch, msg, fwd); -} - - -/** - * Handle a channel destruction message. - * - * @param t Tunnel on which the message came. - * @param msg Channel destroy message. - * @param fwd Is this message fwd? This only is meaningful in loopback channels. - * #GNUNET_YES if message is FWD on the respective channel (loopback) - * #GNUNET_NO if message is BCK on the respective channel (loopback) - * #GNUNET_SYSERR if message on a one-ended channel (remote) - */ -static void -handle_ch_destroy (struct CadetTunnel *t, - const struct GNUNET_CADET_ChannelManageMessage *msg, - int fwd) -{ - struct CadetChannel *ch; - size_t size; - - /* Check size */ - size = ntohs (msg->header.size); - if (size != sizeof (struct GNUNET_CADET_ChannelManageMessage)) - { - GNUNET_break (0); - return; - } - - /* Check channel */ - ch = GCT_get_channel (t, msg->ctn); - if (NULL == ch) - { - /* Probably a retransmission, safe to ignore */ - return; - } - - GCCH_handle_destroy (ch, msg, fwd); -} - - -/** - * Free Axolotl data. - * - * @param t Tunnel. - */ -static void -destroy_ax (struct CadetTunnel *t) -{ - if (NULL == t->ax) - return; - - GNUNET_free_non_null (t->ax->DHRs); - GNUNET_free_non_null (t->ax->kx_0); - while (NULL != t->ax->skipped_head) - delete_skipped_key (t, t->ax->skipped_head); - GNUNET_assert (0 == t->ax->skipped); - - GNUNET_free (t->ax); - t->ax = NULL; - - if (NULL != t->rekey_task) - { - GNUNET_SCHEDULER_cancel (t->rekey_task); - t->rekey_task = NULL; - } - if (NULL != t->ephm_h) - { - GCC_cancel (t->ephm_h); - t->ephm_h = NULL; - } -} - - -/** - * Demultiplex by message type and call appropriate handler for a message - * towards a channel of a local tunnel. - * - * @param t Tunnel this message came on. - * @param msgh Message header. - * @param fwd Is this message fwd? This only is meaningful in loopback channels. - * #GNUNET_YES if message is FWD on the respective channel (loopback) - * #GNUNET_NO if message is BCK on the respective channel (loopback) - * #GNUNET_SYSERR if message on a one-ended channel (remote) - */ -static void -handle_decrypted (struct CadetTunnel *t, - const struct GNUNET_MessageHeader *msgh, - int fwd) -{ - uint16_t type; - char buf[256]; - - type = ntohs (msgh->type); - LOG (GNUNET_ERROR_TYPE_DEBUG, "<-- %s on %s\n", GC_m2s (type), GCT_2s (t)); - SPRINTF (buf, "# received encrypted of type %hu (%s)", type, GC_m2s (type)); - GNUNET_STATISTICS_update (stats, buf, 1, GNUNET_NO); - - switch (type) - { - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE: - /* Do nothing, connection aleady got updated. */ - GNUNET_STATISTICS_update (stats, "# keepalives received", 1, GNUNET_NO); - break; - - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA: - /* Don't send hop ACK, wait for client to ACK */ - handle_data (t, (struct GNUNET_CADET_ChannelAppDataMessage *) msgh, fwd); - break; - - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK: - handle_data_ack (t, (struct GNUNET_CADET_ChannelDataAckMessage *) msgh, fwd); - break; - - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN: - handle_ch_create (t, (struct GNUNET_CADET_ChannelOpenMessage *) msgh); - break; - - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_NACK_DEPRECATED: - handle_ch_nack (t, (struct GNUNET_CADET_ChannelManageMessage *) msgh); - break; - - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK: - handle_ch_ack (t, (struct GNUNET_CADET_ChannelManageMessage *) msgh, fwd); - break; - - case GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY: - handle_ch_destroy (t, (struct GNUNET_CADET_ChannelManageMessage *) msgh, fwd); - break; - - default: - GNUNET_break_op (0); - LOG (GNUNET_ERROR_TYPE_WARNING, - "end-to-end message not known (%u)\n", - ntohs (msgh->type)); - GCT_debug (t, GNUNET_ERROR_TYPE_WARNING); - } -} - - -/******************************************************************************/ -/******************************** API ***********************************/ -/******************************************************************************/ - -/** - * Decrypt and process an encrypted message. - * - * Calls the appropriate handler for a message in a channel of a local tunnel. - * - * @param t Tunnel this message came on. - * @param msg Message header. - */ -void -GCT_handle_encrypted (struct CadetTunnel *t, - const struct GNUNET_CADET_TunnelEncryptedMessage *msg) -{ - uint16_t size = ntohs (msg->header.size); - char cbuf [size]; - int decrypted_size; - const struct GNUNET_MessageHeader *msgh; - unsigned int off; - - GNUNET_STATISTICS_update (stats, "# received encrypted", 1, GNUNET_NO); - - decrypted_size = t_ax_decrypt_and_validate (t, cbuf, msg, size); - - if (-1 == decrypted_size) - { - GNUNET_STATISTICS_update (stats, "# unable to decrypt", 1, GNUNET_NO); - if (CADET_TUNNEL_KEY_AX_AUTH_SENT <= t->estate) - { - GNUNET_break_op (0); - LOG (GNUNET_ERROR_TYPE_WARNING, "Wrong crypto, tunnel %s\n", GCT_2s (t)); - GCT_debug (t, GNUNET_ERROR_TYPE_WARNING); - } - return; - } - GCT_change_estate (t, CADET_TUNNEL_KEY_OK); - - /* FIXME: this is bad, as the structs returned from - this loop may be unaligned, see util's MST for - how to do this right. */ - off = 0; - while (off + sizeof (struct GNUNET_MessageHeader) <= decrypted_size) - { - uint16_t msize; - - msgh = (const struct GNUNET_MessageHeader *) &cbuf[off]; - msize = ntohs (msgh->size); - if (msize < sizeof (struct GNUNET_MessageHeader)) - { - GNUNET_break_op (0); - return; - } - if (off + msize < decrypted_size) - { - GNUNET_break_op (0); - return; - } - handle_decrypted (t, msgh, GNUNET_SYSERR); - off += msize; - } -} - - -/** - * Handle a Key eXchange message. - * - * @param t Tunnel on which the message came. - * @param msg KX message itself. - */ -void -GCT_handle_kx (struct CadetTunnel *t, - const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg) -{ - struct CadetTunnelAxolotl *ax; - struct GNUNET_HashCode key_material[3]; - struct GNUNET_CRYPTO_SymmetricSessionKey keys[5]; - const char salt[] = "CADET Axolotl salt"; - const struct GNUNET_PeerIdentity *pid; - int am_I_alice; - - CADET_TIMING_START; - - LOG (GNUNET_ERROR_TYPE_INFO, "<== { KX} on %s\n", GCT_2s (t)); - - if (NULL == t->ax) - { - /* Something is wrong if ax is NULL. Whose fault it is? */ - return; - } - ax = t->ax; - - pid = GCT_get_destination (t); - if (0 > GNUNET_CRYPTO_cmp_peer_identity (&my_full_id, pid)) - am_I_alice = GNUNET_YES; - else if (0 < GNUNET_CRYPTO_cmp_peer_identity (&my_full_id, pid)) - am_I_alice = GNUNET_NO; - else - { - GNUNET_break_op (0); - return; - } - - if (0 != (GNUNET_CADET_KX_FLAG_FORCE_REPLY & ntohl (msg->flags))) - { - if (NULL != t->rekey_task) - { - GNUNET_SCHEDULER_cancel (t->rekey_task); - t->rekey_task = NULL; - } - GCT_send_kx (t, GNUNET_NO); - } - - if (0 == memcmp (&ax->DHRr, &msg->ratchet_key, sizeof(msg->ratchet_key))) - { - LOG (GNUNET_ERROR_TYPE_INFO, " known ratchet key, exit\n"); - return; - } - - LOG (GNUNET_ERROR_TYPE_INFO, " is Alice? %s\n", am_I_alice ? "YES" : "NO"); - - ax->DHRr = msg->ratchet_key; - - /* ECDH A B0 */ - if (GNUNET_YES == am_I_alice) - { - GNUNET_CRYPTO_eddsa_ecdh (id_key, /* A */ - &msg->ephemeral_key, /* B0 */ - &key_material[0]); - } - else - { - GNUNET_CRYPTO_ecdh_eddsa (ax->kx_0, /* B0 */ - &pid->public_key, /* A */ - &key_material[0]); - } - - /* ECDH A0 B */ - if (GNUNET_YES == am_I_alice) - { - GNUNET_CRYPTO_ecdh_eddsa (ax->kx_0, /* A0 */ - &pid->public_key, /* B */ - &key_material[1]); - } - else - { - GNUNET_CRYPTO_eddsa_ecdh (id_key, /* A */ - &msg->ephemeral_key, /* B0 */ - &key_material[1]); - - - } - - /* ECDH A0 B0 */ - /* (This is the triple-DH, we could probably safely skip this, - as A0/B0 are already in the key material.) */ - GNUNET_CRYPTO_ecc_ecdh (ax->kx_0, /* A0 or B0 */ - &msg->ephemeral_key, /* B0 or A0 */ - &key_material[2]); - - #if DUMP_KEYS_TO_STDERR - { - unsigned int i; - for (i = 0; i < 3; i++) - LOG (GNUNET_ERROR_TYPE_INFO, "km[%u]: %s\n", - i, GNUNET_h2s (&key_material[i])); - } - #endif - - /* KDF */ - GNUNET_CRYPTO_kdf (keys, sizeof (keys), - salt, sizeof (salt), - &key_material, sizeof (key_material), NULL); - - if (0 == memcmp (&ax->RK, &keys[0], sizeof(ax->RK))) - { - LOG (GNUNET_ERROR_TYPE_INFO, " known handshake key, exit\n"); - return; - } - ax->RK = keys[0]; - if (GNUNET_YES == am_I_alice) - { - ax->HKr = keys[1]; - ax->NHKs = keys[2]; - ax->NHKr = keys[3]; - ax->CKr = keys[4]; - ax->ratchet_flag = GNUNET_YES; - } - else - { - ax->HKs = keys[1]; - ax->NHKr = keys[2]; - ax->NHKs = keys[3]; - ax->CKs = keys[4]; - ax->ratchet_flag = GNUNET_NO; - ax->ratchet_allowed = GNUNET_NO; - ax->ratchet_counter = 0; - ax->ratchet_expiration = - GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(), ratchet_time); - } - ax->PNs = 0; - ax->Nr = 0; - ax->Ns = 0; - - GCT_change_estate (t, CADET_TUNNEL_KEY_AX_AUTH_SENT); - send_queued_data (t); - - CADET_TIMING_END; -} - -/** - * Initialize the tunnel subsystem. - * - * @param c Configuration handle. - * @param key ECC private key, to derive all other keys and do crypto. - */ -void -GCT_init (const struct GNUNET_CONFIGURATION_Handle *c, - const struct GNUNET_CRYPTO_EddsaPrivateKey *key) -{ - unsigned int expected_overhead; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "init\n"); - - expected_overhead = 0; - expected_overhead += sizeof (struct GNUNET_CADET_TunnelEncryptedMessage); - expected_overhead += sizeof (struct GNUNET_CADET_ChannelAppDataMessage); - expected_overhead += sizeof (struct GNUNET_CADET_ConnectionEncryptedAckMessage); - GNUNET_assert (GNUNET_CONSTANTS_CADET_P2P_OVERHEAD == expected_overhead); - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (c, - "CADET", - "RATCHET_MESSAGES", - &ratchet_messages)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, - "CADET", - "RATCHET_MESSAGES", - "USING DEFAULT"); - ratchet_messages = 64; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (c, - "CADET", - "RATCHET_TIME", - &ratchet_time)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, - "CADET", "RATCHET_TIME", "USING DEFAULT"); - ratchet_time = GNUNET_TIME_UNIT_HOURS; - } - - - id_key = key; - tunnels = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_YES); -} - - -/** - * Shut down the tunnel subsystem. - */ -void -GCT_shutdown (void) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down tunnels\n"); - GNUNET_CONTAINER_multipeermap_iterate (tunnels, &destroy_iterator, NULL); - GNUNET_CONTAINER_multipeermap_destroy (tunnels); -} - - -/** - * Create a tunnel. - * - * @param destination Peer this tunnel is towards. - */ -struct CadetTunnel * -GCT_new (struct CadetPeer *destination) -{ - struct CadetTunnel *t; - - t = GNUNET_new (struct CadetTunnel); - t->next_ctn.cn = 0; - t->peer = destination; - - if (GNUNET_OK != - GNUNET_CONTAINER_multipeermap_put (tunnels, GCP_get_id (destination), t, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) - { - GNUNET_break (0); - GNUNET_free (t); - return NULL; - } - t->ax = GNUNET_new (struct CadetTunnelAxolotl); - new_ephemeral (t); - t->ax->kx_0 = GNUNET_CRYPTO_ecdhe_key_create (); - return t; -} - - -/** - * Change the tunnel's connection state. - * - * @param t Tunnel whose connection state to change. - * @param cstate New connection state. - */ -void -GCT_change_cstate (struct CadetTunnel* t, enum CadetTunnelCState cstate) -{ - if (NULL == t) - return; - LOG (GNUNET_ERROR_TYPE_DEBUG, "Tunnel %s cstate %s => %s\n", - GCP_2s (t->peer), cstate2s (t->cstate), cstate2s (cstate)); - if (myid != GCP_get_short_id (t->peer) && - CADET_TUNNEL_READY != t->cstate && - CADET_TUNNEL_READY == cstate) - { - t->cstate = cstate; - if (CADET_TUNNEL_KEY_OK == t->estate) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " cstate triggered send queued data\n"); - send_queued_data (t); - } - else if (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " cstate triggered KX\n"); - GCT_send_kx (t, GNUNET_NO); - } - else - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "estate %s\n", estate2s (t->estate)); - } - } - t->cstate = cstate; - - if (CADET_TUNNEL_READY == cstate - && CONNECTIONS_PER_TUNNEL <= GCT_count_connections (t)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " cstate triggered stop dht\n"); - GCP_stop_search (t->peer); - } -} - - -/** - * Change the tunnel encryption state. - * - * If the encryption state changes to OK, stop the rekey task. - * - * @param t Tunnel whose encryption state to change, or NULL. - * @param state New encryption state. - */ -void -GCT_change_estate (struct CadetTunnel* t, enum CadetTunnelEState state) -{ - enum CadetTunnelEState old; - - if (NULL == t) - return; - - old = t->estate; - t->estate = state; - LOG (GNUNET_ERROR_TYPE_DEBUG, "Tunnel %s estate was %s\n", - GCP_2s (t->peer), estate2s (old)); - LOG (GNUNET_ERROR_TYPE_DEBUG, "Tunnel %s estate is now %s\n", - GCP_2s (t->peer), estate2s (t->estate)); - - if (CADET_TUNNEL_KEY_OK != old && CADET_TUNNEL_KEY_OK == t->estate) - { - if (NULL != t->rekey_task) - { - GNUNET_SCHEDULER_cancel (t->rekey_task); - t->rekey_task = NULL; - } - /* Send queued data if tunnel is not loopback */ - if (myid != GCP_get_short_id (t->peer)) - send_queued_data (t); - } -} - - -/** - * @brief Check if tunnel has too many connections, and remove one if necessary. - * - * Currently this means the newest connection, unless it is a direct one. - * Implemented as a task to avoid freeing a connection that is in the middle - * of being created/processed. - * - * @param cls Closure (Tunnel to check). - */ -static void -trim_connections (void *cls) -{ - struct CadetTunnel *t = cls; - - t->trim_connections_task = NULL; - if (GCT_count_connections (t) > 2 * CONNECTIONS_PER_TUNNEL) - { - struct CadetTConnection *iter; - struct CadetTConnection *c; - - for (c = iter = t->connection_head; NULL != iter; iter = iter->next) - { - if ((iter->created.abs_value_us > c->created.abs_value_us) - && GNUNET_NO == GCC_is_direct (iter->c)) - { - c = iter; - } - } - if (NULL != c) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "Too many connections on tunnel %s\n", - GCT_2s (t)); - LOG (GNUNET_ERROR_TYPE_DEBUG, "Destroying connection %s\n", - GCC_2s (c->c)); - GCC_destroy (c->c); - } - else - { - GNUNET_break (0); - } - } -} - - -/** - * Add a connection to a tunnel. - * - * @param t Tunnel. - * @param c Connection. - */ -void -GCT_add_connection (struct CadetTunnel *t, struct CadetConnection *c) -{ - struct CadetTConnection *aux; - - GNUNET_assert (NULL != c); - - LOG (GNUNET_ERROR_TYPE_DEBUG, "add connection %s\n", GCC_2s (c)); - LOG (GNUNET_ERROR_TYPE_DEBUG, " to tunnel %s\n", GCT_2s (t)); - for (aux = t->connection_head; aux != NULL; aux = aux->next) - if (aux->c == c) - return; - - aux = GNUNET_new (struct CadetTConnection); - aux->c = c; - aux->created = GNUNET_TIME_absolute_get (); - - GNUNET_CONTAINER_DLL_insert (t->connection_head, t->connection_tail, aux); - - if (CADET_TUNNEL_SEARCHING == t->cstate) - GCT_change_cstate (t, CADET_TUNNEL_WAITING); - - if (NULL != t->trim_connections_task) - t->trim_connections_task = GNUNET_SCHEDULER_add_now (&trim_connections, t); -} - - -/** - * Remove a connection from a tunnel. - * - * @param t Tunnel. - * @param c Connection. - */ -void -GCT_remove_connection (struct CadetTunnel *t, - struct CadetConnection *c) -{ - struct CadetTConnection *aux; - struct CadetTConnection *next; - unsigned int conns; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Removing connection %s from tunnel %s\n", - GCC_2s (c), GCT_2s (t)); - for (aux = t->connection_head; aux != NULL; aux = next) - { - next = aux->next; - if (aux->c == c) - { - GNUNET_CONTAINER_DLL_remove (t->connection_head, t->connection_tail, aux); - GNUNET_free (aux); - } - } - - conns = GCT_count_connections (t); - if (0 == conns - && NULL == t->destroy_task - && CADET_TUNNEL_SHUTDOWN != t->cstate - && GNUNET_NO == shutting_down) - { - if (0 == GCT_count_any_connections (t)) - GCT_change_cstate (t, CADET_TUNNEL_SEARCHING); - else - GCT_change_cstate (t, CADET_TUNNEL_WAITING); - } - - /* Start new connections if needed */ - if (CONNECTIONS_PER_TUNNEL > conns - && CADET_TUNNEL_SHUTDOWN != t->cstate - && GNUNET_NO == shutting_down) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " too few connections, getting new ones\n"); - GCP_connect (t->peer); /* Will change cstate to WAITING when possible */ - return; - } - - /* If not marked as ready, no change is needed */ - if (CADET_TUNNEL_READY != t->cstate) - return; - - /* Check if any connection is ready to maintain cstate */ - for (aux = t->connection_head; aux != NULL; aux = aux->next) - if (CADET_CONNECTION_READY == GCC_get_state (aux->c)) - return; -} - - -/** - * Add a channel to a tunnel. - * - * @param t Tunnel. - * @param ch Channel. - */ -void -GCT_add_channel (struct CadetTunnel *t, - struct CadetChannel *ch) -{ - struct CadetTChannel *aux; - - GNUNET_assert (NULL != ch); - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding channel %p to tunnel %p\n", ch, t); - - for (aux = t->channel_head; aux != NULL; aux = aux->next) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " already there %p\n", aux->ch); - if (aux->ch == ch) - return; - } - - aux = GNUNET_new (struct CadetTChannel); - aux->ch = ch; - LOG (GNUNET_ERROR_TYPE_DEBUG, - " adding %p to %p\n", aux, t->channel_head); - GNUNET_CONTAINER_DLL_insert_tail (t->channel_head, - t->channel_tail, - aux); - - if (NULL != t->destroy_task) - { - GNUNET_SCHEDULER_cancel (t->destroy_task); - t->destroy_task = NULL; - LOG (GNUNET_ERROR_TYPE_DEBUG, " undo destroy!\n"); - } -} - - -/** - * Remove a channel from a tunnel. - * - * @param t Tunnel. - * @param ch Channel. - */ -void -GCT_remove_channel (struct CadetTunnel *t, struct CadetChannel *ch) -{ - struct CadetTChannel *aux; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Removing channel %p from tunnel %p\n", ch, t); - for (aux = t->channel_head; aux != NULL; aux = aux->next) - { - if (aux->ch == ch) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, " found! %s\n", GCCH_2s (ch)); - GNUNET_CONTAINER_DLL_remove (t->channel_head, - t->channel_tail, - aux); - GNUNET_free (aux); - return; - } - } -} - - -/** - * Search for a channel by global ID. - * - * @param t Tunnel containing the channel. - * @param ctn Public channel number. - * - * @return channel handler, NULL if doesn't exist - */ -struct CadetChannel * -GCT_get_channel (struct CadetTunnel *t, - struct GNUNET_CADET_ChannelTunnelNumber ctn) -{ - struct CadetTChannel *iter; - - if (NULL == t) - return NULL; - - for (iter = t->channel_head; NULL != iter; iter = iter->next) - { - if (GCCH_get_id (iter->ch).cn == ctn.cn) - break; - } - - return NULL == iter ? NULL : iter->ch; -} - - -/** - * @brief Destroy a tunnel and free all resources. - * - * Should only be called a while after the tunnel has been marked as destroyed, - * in case there is a new channel added to the same peer shortly after marking - * the tunnel. This way we avoid a new public key handshake. - * - * @param cls Closure (tunnel to destroy). - */ -static void -delayed_destroy (void *cls) -{ - struct CadetTunnel *t = cls; - struct CadetTConnection *iter; - - t->destroy_task = NULL; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "delayed destroying tunnel %p\n", - t); - t->cstate = CADET_TUNNEL_SHUTDOWN; - for (iter = t->connection_head; NULL != iter; iter = iter->next) - { - GCC_send_destroy (iter->c); - } - GCT_destroy (t); -} - - -/** - * Tunnel is empty: destroy it. - * - * Notifies all connections about the destruction. - * - * @param t Tunnel to destroy. - */ -void -GCT_destroy_empty (struct CadetTunnel *t) -{ - if (GNUNET_YES == shutting_down) - return; /* Will be destroyed immediately anyway */ - - if (NULL != t->destroy_task) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - "Tunnel %s is already scheduled for destruction. Tunnel debug dump:\n", - GCT_2s (t)); - GCT_debug (t, GNUNET_ERROR_TYPE_WARNING); - GNUNET_break (0); - /* should never happen, tunnel can only become empty once, and the - * task identifier should be NO_TASK (cleaned when the tunnel was created - * or became un-empty) - */ - return; - } - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Tunnel %s empty: scheduling destruction\n", - GCT_2s (t)); - - // FIXME make delay a config option - t->destroy_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, - &delayed_destroy, t); - LOG (GNUNET_ERROR_TYPE_DEBUG, "Scheduled destroy of %p as %p\n", - t, t->destroy_task); -} - - -/** - * Destroy tunnel if empty (no more channels). - * - * @param t Tunnel to destroy if empty. - */ -void -GCT_destroy_if_empty (struct CadetTunnel *t) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, "Tunnel %s destroy if empty\n", GCT_2s (t)); - if (0 < GCT_count_channels (t)) - return; - - GCT_destroy_empty (t); -} - - -/** - * Destroy the tunnel. - * - * This function does not generate any warning traffic to clients or peers. - * - * Tasks: - * Cancel messages belonging to this tunnel queued to neighbors. - * Free any allocated resources linked to the tunnel. - * - * @param t The tunnel to destroy. - */ -void -GCT_destroy (struct CadetTunnel *t) -{ - struct CadetTConnection *iter_c; - struct CadetTConnection *next_c; - struct CadetTChannel *iter_ch; - struct CadetTChannel *next_ch; - unsigned int keepalives_queued; - - if (NULL == t) - return; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "destroying tunnel %s\n", - GCP_2s (t->peer)); - GNUNET_break (GNUNET_YES == - GNUNET_CONTAINER_multipeermap_remove (tunnels, - GCP_get_id (t->peer), t)); - - for (iter_c = t->connection_head; NULL != iter_c; iter_c = next_c) - { - next_c = iter_c->next; - GCC_destroy (iter_c->c); - } - for (iter_ch = t->channel_head; NULL != iter_ch; iter_ch = next_ch) - { - next_ch = iter_ch->next; - GCCH_destroy (iter_ch->ch); - /* Should only happen on shutdown, but it's ok. */ - } - keepalives_queued = 0; - while (NULL != t->tq_head) - { - /* Should have been cleaned by destuction of channel. */ - struct GNUNET_MessageHeader *mh; - uint16_t type; - - mh = (struct GNUNET_MessageHeader *) &t->tq_head[1]; - type = ntohs (mh->type); - if (0 == keepalives_queued && GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE == type) - { - keepalives_queued = 1; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "one keepalive left behind on tunnel shutdown\n"); - } - else if (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY == type) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - "tunnel destroyed before a CHANNEL_DESTROY was sent to peer\n"); - } - else - { - GNUNET_break (0); - LOG (GNUNET_ERROR_TYPE_ERROR, - "message left behind on tunnel shutdown: %s\n", - GC_m2s (type)); - } - unqueue_data (t->tq_head); - } - - - if (NULL != t->destroy_task) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "cancelling dest: %p\n", - t->destroy_task); - GNUNET_SCHEDULER_cancel (t->destroy_task); - t->destroy_task = NULL; - } - - if (NULL != t->trim_connections_task) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "cancelling trim: %p\n", - t->trim_connections_task); - GNUNET_SCHEDULER_cancel (t->trim_connections_task); - t->trim_connections_task = NULL; - } - - GNUNET_STATISTICS_update (stats, "# tunnels", -1, GNUNET_NO); - GCP_set_tunnel (t->peer, NULL); - - if (NULL != t->rekey_task) - { - GNUNET_SCHEDULER_cancel (t->rekey_task); - t->rekey_task = NULL; - } - if (NULL != t->ax) - destroy_ax (t); - - GNUNET_free (t); -} - - -/** - * @brief Use the given path for the tunnel. - * Update the next and prev hops (and RCs). - * (Re)start the path refresh in case the tunnel is locally owned. - * - * @param t Tunnel to update. - * @param p Path to use. - * - * @return Connection created. - */ -struct CadetConnection * -GCT_use_path (struct CadetTunnel *t, struct CadetPeerPath *path) -{ - struct CadetConnection *c; - struct GNUNET_CADET_ConnectionTunnelIdentifier cid; - unsigned int own_pos; - - if (NULL == t || NULL == path) - { - GNUNET_break (0); - return NULL; - } - - if (CADET_TUNNEL_SHUTDOWN == t->cstate) - { - GNUNET_break (0); - return NULL; - } - - for (own_pos = 0; own_pos < path->length; own_pos++) - { - if (path->peers[own_pos] == myid) - break; - } - if (own_pos >= path->length) - { - GNUNET_break_op (0); - return NULL; - } - - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &cid, sizeof (cid)); - c = GCC_new (&cid, t, path, own_pos); - if (NULL == c) - { - /* Path was flawed */ - return NULL; - } - GCT_add_connection (t, c); - return c; -} - - -/** - * Count all created connections of a tunnel. Not necessarily ready connections! - * - * @param t Tunnel on which to count. - * - * @return Number of connections created, either being established or ready. - */ -unsigned int -GCT_count_any_connections (struct CadetTunnel *t) -{ - struct CadetTConnection *iter; - unsigned int count; - - if (NULL == t) - return 0; - - for (count = 0, iter = t->connection_head; NULL != iter; iter = iter->next) - count++; - - return count; -} - - -/** - * Count established (ready) connections of a tunnel. - * - * @param t Tunnel on which to count. - * - * @return Number of connections. - */ -unsigned int -GCT_count_connections (struct CadetTunnel *t) -{ - struct CadetTConnection *iter; - unsigned int count; - - if (NULL == t) - return 0; - - for (count = 0, iter = t->connection_head; NULL != iter; iter = iter->next) - if (CADET_CONNECTION_READY == GCC_get_state (iter->c)) - count++; - - return count; -} - - -/** - * Count channels of a tunnel. - * - * @param t Tunnel on which to count. - * - * @return Number of channels. - */ -unsigned int -GCT_count_channels (struct CadetTunnel *t) -{ - struct CadetTChannel *iter; - unsigned int count; - - for (count = 0, iter = t->channel_head; - NULL != iter; - iter = iter->next, count++) /* skip */; - - return count; -} - - -/** - * Get the connectivity state of a tunnel. - * - * @param t Tunnel. - * - * @return Tunnel's connectivity state. - */ -enum CadetTunnelCState -GCT_get_cstate (struct CadetTunnel *t) -{ - if (NULL == t) - { - GNUNET_assert (0); - return (enum CadetTunnelCState) -1; - } - return t->cstate; -} - - -/** - * Get the encryption state of a tunnel. - * - * @param t Tunnel. - * - * @return Tunnel's encryption state. - */ -enum CadetTunnelEState -GCT_get_estate (struct CadetTunnel *t) -{ - if (NULL == t) - { - GNUNET_break (0); - return (enum CadetTunnelEState) -1; - } - return t->estate; -} - -/** - * Get the maximum buffer space for a tunnel towards a local client. - * - * @param t Tunnel. - * - * @return Biggest buffer space offered by any channel in the tunnel. - */ -unsigned int -GCT_get_channels_buffer (struct CadetTunnel *t) -{ - struct CadetTChannel *iter; - unsigned int buffer; - unsigned int ch_buf; - - if (NULL == t->channel_head) - { - /* Probably getting buffer for a channel create/handshake. */ - LOG (GNUNET_ERROR_TYPE_DEBUG, " no channels, allow max\n"); - return MIN_TUNNEL_BUFFER; - } - - buffer = 0; - for (iter = t->channel_head; NULL != iter; iter = iter->next) - { - ch_buf = get_channel_buffer (iter); - if (ch_buf > buffer) - buffer = ch_buf; - } - if (MIN_TUNNEL_BUFFER > buffer) - return MIN_TUNNEL_BUFFER; - - if (MAX_TUNNEL_BUFFER < buffer) - { - GNUNET_break (0); - return MAX_TUNNEL_BUFFER; - } - return buffer; -} - - -/** - * Get the total buffer space for a tunnel for P2P traffic. - * - * @param t Tunnel. - * - * @return Buffer space offered by all connections in the tunnel. - */ -unsigned int -GCT_get_connections_buffer (struct CadetTunnel *t) -{ - struct CadetTConnection *iter; - unsigned int buffer; - - if (GNUNET_NO == is_ready (t)) - { - if (count_queued_data (t) >= 3) - return 0; - else - return 1; - } - - buffer = 0; - for (iter = t->connection_head; NULL != iter; iter = iter->next) - { - if (GCC_get_state (iter->c) != CADET_CONNECTION_READY) - { - continue; - } - buffer += get_connection_buffer (iter); - } - - return buffer; -} - - -/** - * Get the tunnel's destination. - * - * @param t Tunnel. - * - * @return ID of the destination peer. - */ -const struct GNUNET_PeerIdentity * -GCT_get_destination (struct CadetTunnel *t) -{ - return GCP_get_id (t->peer); -} - - -/** - * Get the tunnel's next free global channel ID. - * - * @param t Tunnel. - * - * @return GID of a channel free to use. - */ -struct GNUNET_CADET_ChannelTunnelNumber -GCT_get_next_ctn (struct CadetTunnel *t) -{ - struct GNUNET_CADET_ChannelTunnelNumber ctn; - struct GNUNET_CADET_ChannelTunnelNumber mask; - int result; - - /* Set bit 30 depending on the ID relationship. Bit 31 is always 0 for GID. - * If our ID is bigger or loopback tunnel, start at 0, bit 30 = 0 - * If peer's ID is bigger, start at 0x4... bit 30 = 1 - */ - result = GNUNET_CRYPTO_cmp_peer_identity (&my_full_id, GCP_get_id (t->peer)); - if (0 > result) - mask.cn = htonl (0x40000000); - else - mask.cn = 0x0; - t->next_ctn.cn |= mask.cn; - - while (NULL != GCT_get_channel (t, t->next_ctn)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Channel %u exists...\n", - t->next_ctn.cn); - t->next_ctn.cn = htonl ((ntohl (t->next_ctn.cn) + 1) & ~GNUNET_CADET_LOCAL_CHANNEL_ID_CLI); - t->next_ctn.cn |= mask.cn; - } - ctn = t->next_ctn; - t->next_ctn.cn = (t->next_ctn.cn + 1) & ~GNUNET_CADET_LOCAL_CHANNEL_ID_CLI; - t->next_ctn.cn |= mask.cn; - - return ctn; -} - - -/** - * Send ACK on one or more channels due to buffer in connections. - * - * @param t Channel which has some free buffer space. - */ -void -GCT_unchoke_channels (struct CadetTunnel *t) -{ - struct CadetTChannel *iter; - unsigned int buffer; - unsigned int channels = GCT_count_channels (t); - unsigned int choked_n; - struct CadetChannel *choked[channels]; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "GCT_unchoke_channels on %s\n", GCT_2s (t)); - LOG (GNUNET_ERROR_TYPE_DEBUG, " head: %p\n", t->channel_head); - if (NULL != t->channel_head) - LOG (GNUNET_ERROR_TYPE_DEBUG, " head ch: %p\n", t->channel_head->ch); - - if (NULL != t->tq_head) - send_queued_data (t); - - /* Get buffer space */ - buffer = GCT_get_connections_buffer (t); - if (0 == buffer) - { - return; - } - - /* Count and remember choked channels */ - choked_n = 0; - for (iter = t->channel_head; NULL != iter; iter = iter->next) - { - if (GNUNET_NO == get_channel_allowed (iter)) - { - choked[choked_n++] = iter->ch; - } - } - - /* Unchoke random channels */ - while (0 < buffer && 0 < choked_n) - { - unsigned int r = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, - choked_n); - GCCH_allow_client (choked[r], GCCH_is_origin (choked[r], GNUNET_YES)); - choked_n--; - buffer--; - choked[r] = choked[choked_n]; - } -} - - -/** - * Send ACK on one or more connections due to buffer space to the client. - * - * Iterates all connections of the tunnel and sends ACKs appropriately. - * - * @param t Tunnel. - */ -void -GCT_send_connection_acks (struct CadetTunnel *t) -{ - struct CadetTConnection *iter; - uint32_t allowed; - uint32_t to_allow; - uint32_t allow_per_connection; - unsigned int cs; - unsigned int buffer; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Tunnel send connection ACKs on %s\n", - GCT_2s (t)); - - if (NULL == t) - { - GNUNET_break (0); - return; - } - - if (CADET_TUNNEL_READY != t->cstate) - return; - - buffer = GCT_get_channels_buffer (t); - LOG (GNUNET_ERROR_TYPE_DEBUG, " buffer %u\n", buffer); - - /* Count connections, how many messages are already allowed */ - cs = GCT_count_connections (t); - for (allowed = 0, iter = t->connection_head; NULL != iter; iter = iter->next) - { - allowed += get_connection_allowed (iter); - } - LOG (GNUNET_ERROR_TYPE_DEBUG, " allowed %u\n", allowed); - - /* Make sure there is no overflow */ - if (allowed > buffer) - return; - - /* Authorize connections to send more data */ - to_allow = buffer - allowed; - - for (iter = t->connection_head; - NULL != iter && to_allow > 0; - iter = iter->next) - { - if (CADET_CONNECTION_READY != GCC_get_state (iter->c) - || get_connection_allowed (iter) > 64 / 3) - { - continue; - } - GNUNET_assert(cs != 0); - allow_per_connection = to_allow/cs; - to_allow -= allow_per_connection; - cs--; - GCC_allow (iter->c, allow_per_connection, - GCC_is_origin (iter->c, GNUNET_NO)); - } - - if (0 != to_allow) - { - /* Since we don't allow if it's allowed to send 64/3, this can happen. */ - LOG (GNUNET_ERROR_TYPE_DEBUG, " reminding to_allow: %u\n", to_allow); - } -} - - -/** - * Cancel a previously sent message while it's in the queue. - * - * ONLY can be called before the continuation given to the send function - * is called. Once the continuation is called, the message is no longer in the - * queue. - * - * @param q Handle to the queue. - */ -void -GCT_cancel (struct CadetTunnelQueue *q) -{ - if (NULL != q->cq) - { - GNUNET_assert (NULL == q->tqd); - GCC_cancel (q->cq); - /* tun_message_sent() will be called and free q */ - } - else if (NULL != q->tqd) - { - unqueue_data (q->tqd); - q->tqd = NULL; - if (NULL != q->cont) - q->cont (q->cont_cls, NULL, q, 0, 0); - GNUNET_free (q); - } - else - { - GNUNET_break (0); - } -} - - -/** - * Check if the tunnel has queued traffic. - * - * @param t Tunnel to check. - * - * @return #GNUNET_YES if there is queued traffic - * #GNUNET_NO otherwise - */ -int -GCT_has_queued_traffic (struct CadetTunnel *t) -{ - return (NULL != t->tq_head) ? GNUNET_YES : GNUNET_NO; -} - - -/** - * Sends an already built message on a tunnel, encrypting it and - * choosing the best connection if not provided. - * - * @param message Message to send. Function modifies it. - * @param t Tunnel on which this message is transmitted. - * @param c Connection to use (autoselect if NULL). - * @param force Force the tunnel to take the message (buffer overfill). - * @param cont Continuation to call once message is really sent. - * @param cont_cls Closure for @c cont. - * - * @return Handle to cancel message. NULL if @c cont is NULL. - */ -struct CadetTunnelQueue * -GCT_send_prebuilt_message (const struct GNUNET_MessageHeader *message, - struct CadetTunnel *t, - struct CadetConnection *c, - int force, GCT_sent cont, void *cont_cls) -{ - return send_prebuilt_message (message, t, c, force, cont, cont_cls, NULL); -} - - -/** - * Send a KX message. - * - * @param t Tunnel on which to send it. - * @param force_reply Force the other peer to reply with a KX message. - */ -void -GCT_send_kx (struct CadetTunnel *t, int force_reply) -{ - static struct CadetEncryptedMessageIdentifier zero; - struct CadetConnection *c; - struct GNUNET_CADET_TunnelKeyExchangeMessage msg; - enum GNUNET_CADET_KX_Flags flags; - - LOG (GNUNET_ERROR_TYPE_INFO, "==> { KX} on %s\n", GCT_2s (t)); - if (NULL != t->ephm_h) - { - LOG (GNUNET_ERROR_TYPE_INFO, " already queued, nop\n"); - return; - } - GNUNET_assert (GNUNET_NO == GCT_is_loopback (t)); - - c = tunnel_get_connection (t); - if (NULL == c) - { - if (NULL == t->destroy_task && CADET_TUNNEL_READY == t->cstate) - { - GNUNET_break (0); - GCT_debug (t, GNUNET_ERROR_TYPE_ERROR); - } - return; - } - - msg.header.size = htons (sizeof (msg)); - msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX); - flags = GNUNET_CADET_KX_FLAG_NONE; - if (GNUNET_YES == force_reply) - flags |= GNUNET_CADET_KX_FLAG_FORCE_REPLY; - msg.flags = htonl (flags); - msg.cid = *GCC_get_id (c); - GNUNET_CRYPTO_ecdhe_key_get_public (t->ax->kx_0, &msg.ephemeral_key); - GNUNET_CRYPTO_ecdhe_key_get_public (t->ax->DHRs, &msg.ratchet_key); - - t->ephm_h = GCC_send_prebuilt_message (&msg.header, - UINT16_MAX, - zero, - c, - GCC_is_origin (c, GNUNET_YES), - GNUNET_YES, &ephm_sent, t); - if (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate) - GCT_change_estate (t, CADET_TUNNEL_KEY_AX_SENT); -} - - -/** - * Is the tunnel directed towards the local peer? - * - * @param t Tunnel. - * - * @return #GNUNET_YES if it is loopback. - */ -int -GCT_is_loopback (const struct CadetTunnel *t) -{ - return (myid == GCP_get_short_id (t->peer)); -} - - -/** - * Is the tunnel this path already? - * - * @param t Tunnel. - * @param p Path. - * - * @return #GNUNET_YES a connection uses this path. - */ -int -GCT_is_path_used (const struct CadetTunnel *t, const struct CadetPeerPath *p) -{ - struct CadetTConnection *iter; - - for (iter = t->connection_head; NULL != iter; iter = iter->next) - if (path_equivalent (GCC_get_path (iter->c), p)) - return GNUNET_YES; - - return GNUNET_NO; -} - - -/** - * Get a cost of a path for a tunnel considering existing connections. - * - * @param t Tunnel. - * @param path Candidate path. - * - * @return Cost of the path (path length + number of overlapping nodes) - */ -unsigned int -GCT_get_path_cost (const struct CadetTunnel *t, - const struct CadetPeerPath *path) -{ - struct CadetTConnection *iter; - const struct CadetPeerPath *aux; - unsigned int overlap; - unsigned int i; - unsigned int j; - - if (NULL == path) - return 0; - - overlap = 0; - GNUNET_assert (NULL != t); - - for (i = 0; i < path->length; i++) - { - for (iter = t->connection_head; NULL != iter; iter = iter->next) - { - aux = GCC_get_path (iter->c); - if (NULL == aux) - continue; - - for (j = 0; j < aux->length; j++) - { - if (path->peers[i] == aux->peers[j]) - { - overlap++; - break; - } - } - } - } - return path->length + overlap; -} - - -/** - * Get the static string for the peer this tunnel is directed. - * - * @param t Tunnel. - * - * @return Static string the destination peer's ID. - */ -const char * -GCT_2s (const struct CadetTunnel *t) -{ - if (NULL == t) - return "(NULL)"; - - return GCP_2s (t->peer); -} - - -/******************************************************************************/ -/***************************** INFO/DEBUG *******************************/ -/******************************************************************************/ - -static void -ax_debug (const struct CadetTunnelAxolotl *ax, enum GNUNET_ErrorType level) -{ - struct GNUNET_CRYPTO_EcdhePublicKey pub; - struct CadetTunnelSkippedKey *iter; - - LOG2 (level, "TTT RK \t %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &ax->RK)); - - LOG2 (level, "TTT HKs \t %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &ax->HKs)); - LOG2 (level, "TTT HKr \t %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &ax->HKr)); - LOG2 (level, "TTT NHKs\t %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &ax->NHKs)); - LOG2 (level, "TTT NHKr\t %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &ax->NHKr)); - - LOG2 (level, "TTT CKs \t %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &ax->CKs)); - LOG2 (level, "TTT CKr \t %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &ax->CKr)); - - GNUNET_CRYPTO_ecdhe_key_get_public (ax->DHRs, &pub); - LOG2 (level, "TTT DHRs\t %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &pub)); - LOG2 (level, "TTT DHRr\t %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &ax->DHRr)); - - LOG2 (level, "TTT Nr\t %u\tNs\t%u\n", ax->Nr, ax->Ns); - LOG2 (level, "TTT PNs\t %u\tSkipped\t%u\n", ax->PNs, ax->skipped); - LOG2 (level, "TTT Ratchet\t%u\n", ax->ratchet_flag); - - for (iter = ax->skipped_head; NULL != iter; iter = iter->next) - { - LOG2 (level, "TTT HK\t %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &iter->HK)); - LOG2 (level, "TTT MK\t %s\n", - GNUNET_i2s ((struct GNUNET_PeerIdentity *) &iter->MK)); - } -} - -/** - * Log all possible info about the tunnel state. - * - * @param t Tunnel to debug. - * @param level Debug level to use. - */ -void -GCT_debug (const struct CadetTunnel *t, enum GNUNET_ErrorType level) -{ - struct CadetTChannel *iter_ch; - struct CadetTConnection *iter_c; - int do_log; - - do_log = GNUNET_get_log_call_status (level & (~GNUNET_ERROR_TYPE_BULK), - "cadet-tun", - __FILE__, __FUNCTION__, __LINE__); - if (0 == do_log) - return; - - LOG2 (level, "TTT DEBUG TUNNEL TOWARDS %s\n", GCT_2s (t)); - LOG2 (level, "TTT cstate %s, estate %s\n", - cstate2s (t->cstate), estate2s (t->estate)); -#if DUMP_KEYS_TO_STDERR - ax_debug (t->ax, level); -#endif - LOG2 (level, "TTT tq_head %p, tq_tail %p\n", t->tq_head, t->tq_tail); - LOG2 (level, "TTT destroy %p\n", t->destroy_task); - LOG2 (level, "TTT channels:\n"); - for (iter_ch = t->channel_head; NULL != iter_ch; iter_ch = iter_ch->next) - { - GCCH_debug (iter_ch->ch, level); - } - - LOG2 (level, "TTT connections:\n"); - for (iter_c = t->connection_head; NULL != iter_c; iter_c = iter_c->next) - { - GCC_debug (iter_c->c, level); - } - - LOG2 (level, "TTT DEBUG TUNNEL END\n"); -} - - -/** - * Iterate all tunnels. - * - * @param iter Iterator. - * @param cls Closure for @c iter. - */ -void -GCT_iterate_all (GNUNET_CONTAINER_PeerMapIterator iter, void *cls) -{ - GNUNET_CONTAINER_multipeermap_iterate (tunnels, iter, cls); -} - - -/** - * Count all tunnels. - * - * @return Number of tunnels to remote peers kept by this peer. - */ -unsigned int -GCT_count_all (void) -{ - return GNUNET_CONTAINER_multipeermap_size (tunnels); -} - - -/** - * Iterate all connections of a tunnel. - * - * @param t Tunnel whose connections to iterate. - * @param iter Iterator. - * @param cls Closure for @c iter. - */ -void -GCT_iterate_connections (struct CadetTunnel *t, GCT_conn_iter iter, void *cls) -{ - struct CadetTConnection *ct; - - for (ct = t->connection_head; NULL != ct; ct = ct->next) - iter (cls, ct->c); -} - - -/** - * Iterate all channels of a tunnel. - * - * @param t Tunnel whose channels to iterate. - * @param iter Iterator. - * @param cls Closure for @c iter. - */ -void -GCT_iterate_channels (struct CadetTunnel *t, GCT_chan_iter iter, void *cls) -{ - struct CadetTChannel *cht; - - for (cht = t->channel_head; NULL != cht; cht = cht->next) - iter (cls, cht->ch); -} diff --git a/src/cadet/gnunet-service-cadet_tunnel.h b/src/cadet/gnunet-service-cadet_tunnel.h deleted file mode 100644 index 1b56a06326..0000000000 --- a/src/cadet/gnunet-service-cadet_tunnel.h +++ /dev/null @@ -1,616 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2013 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file cadet/gnunet-service-cadet_tunnel.h - * @brief cadet service; dealing with tunnels and crypto - * @author Bartlomiej Polot - * - * All functions in this file should use the prefix GMT (Gnunet Cadet Tunnel) - */ - -#ifndef GNUNET_SERVICE_CADET_TUNNEL_H -#define GNUNET_SERVICE_CADET_TUNNEL_H - -#ifdef __cplusplus -extern "C" -{ -#if 0 /* keep Emacsens' auto-indent happy */ -} -#endif -#endif - -#include "platform.h" -#include "gnunet_util_lib.h" - -#define CONNECTIONS_PER_TUNNEL 3 - -/** - * All the connectivity states a tunnel can be in. - */ -enum CadetTunnelCState -{ - /** - * Uninitialized status, should never appear in operation. - */ - CADET_TUNNEL_NEW, - - /** - * No path to the peer known yet. - */ - CADET_TUNNEL_SEARCHING, - - /** - * Request sent, not yet answered. - */ - CADET_TUNNEL_WAITING, - - /** - * Peer connected and ready to accept data. - */ - CADET_TUNNEL_READY, - - /** - * Tunnel being shut down, don't try to keep it alive. - */ - CADET_TUNNEL_SHUTDOWN -}; - - -/** - * All the encryption states a tunnel can be in. - */ -enum CadetTunnelEState -{ - /** - * Uninitialized status, should never appear in operation. - */ - CADET_TUNNEL_KEY_UNINITIALIZED, - - /** - * Ephemeral key sent, waiting for peer's key. - */ - CADET_TUNNEL_KEY_AX_SENT, - - /** - * In OTR: New ephemeral key and ping sent, waiting for pong. - * - * This means that we DO have the peer's ephemeral key, otherwise the - * state would be KEY_SENT. We DO NOT have a valid session key (either no - * previous key or previous key expired). - * - * - * In Axolotl: Key sent and received but no deciphered traffic yet. - * - * This means that we can send traffic (otherwise we would never complete - * the handshake), but we don't have complete confirmation. Since the first - * traffic MUST be a complete channel creation 3-way handshake, no payload - * will be sent before confirmation. - */ - CADET_TUNNEL_KEY_AX_AUTH_SENT, - - /** - * Handshake completed: session key available. - */ - CADET_TUNNEL_KEY_OK, - - /** - * New ephemeral key and ping sent, waiting for pong. Unlike KEY_PING, - * we still have a valid session key and therefore we *can* still send - * traffic on the tunnel. - */ - CADET_TUNNEL_KEY_REKEY -}; - -/** - * Struct containing all information regarding a given peer - */ -struct CadetTunnel; - - -#include "gnunet-service-cadet_channel.h" -#include "gnunet-service-cadet_connection.h" -#include "gnunet-service-cadet_peer.h" - -/** - * Handle for messages queued but not yet sent. - */ -struct CadetTunnelQueue; - -/** - * Callback called when a queued message is sent. - * - * @param cls Closure. - * @param t Tunnel this message was on. - * @param type Type of message sent. - * @param size Size of the message. - */ -typedef void -(*GCT_sent) (void *cls, - struct CadetTunnel *t, - struct CadetTunnelQueue *q, - uint16_t type, size_t size); - -typedef void -(*GCT_conn_iter) (void *cls, struct CadetConnection *c); - - -typedef void -(*GCT_chan_iter) (void *cls, struct CadetChannel *ch); - - -/******************************************************************************/ -/******************************** API ***********************************/ -/******************************************************************************/ - -/** - * Initialize tunnel subsystem. - * - * @param c Configuration handle. - * @param key ECC private key, to derive all other keys and do crypto. - */ -void -GCT_init (const struct GNUNET_CONFIGURATION_Handle *c, - const struct GNUNET_CRYPTO_EddsaPrivateKey *key); - - -/** - * Shut down the tunnel subsystem. - */ -void -GCT_shutdown (void); - - -/** - * Create a tunnel. - * - * @param destination Peer this tunnel is towards. - */ -struct CadetTunnel * -GCT_new (struct CadetPeer *destination); - - -/** - * Tunnel is empty: destroy it. - * - * Notifies all connections about the destruction. - * - * @param t Tunnel to destroy. - */ -void -GCT_destroy_empty (struct CadetTunnel *t); - - -/** - * Destroy tunnel if empty (no more channels). - * - * @param t Tunnel to destroy if empty. - */ -void -GCT_destroy_if_empty (struct CadetTunnel *t); - - -/** - * Destroy the tunnel. - * - * This function does not generate any warning traffic to clients or peers. - * - * Tasks: - * Cancel messages belonging to this tunnel queued to neighbors. - * Free any allocated resources linked to the tunnel. - * - * @param t The tunnel to destroy. - */ -void -GCT_destroy (struct CadetTunnel *t); - - -/** - * Change the tunnel's connection state. - * - * @param t Tunnel whose connection state to change. - * @param cstate New connection state. - */ -void -GCT_change_cstate (struct CadetTunnel* t, enum CadetTunnelCState cstate); - - -/** - * Change the tunnel encryption state. - * - * @param t Tunnel whose encryption state to change. - * @param state New encryption state. - */ -void -GCT_change_estate (struct CadetTunnel* t, enum CadetTunnelEState state); - - -/** - * Add a connection to a tunnel. - * - * @param t Tunnel. - * @param c Connection. - */ -void -GCT_add_connection (struct CadetTunnel *t, struct CadetConnection *c); - - -/** - * Remove a connection from a tunnel. - * - * @param t Tunnel. - * @param c Connection. - */ -void -GCT_remove_connection (struct CadetTunnel *t, struct CadetConnection *c); - - -/** - * Add a channel to a tunnel. - * - * @param t Tunnel. - * @param ch Channel. - */ -void -GCT_add_channel (struct CadetTunnel *t, struct CadetChannel *ch); - - -/** - * Remove a channel from a tunnel. - * - * @param t Tunnel. - * @param ch Channel. - */ -void -GCT_remove_channel (struct CadetTunnel *t, struct CadetChannel *ch); - - -/** - * Search for a channel by global ID. - * - * @param t Tunnel containing the channel. - * @param ctn Public channel number. - * - * @return channel handler, NULL if doesn't exist - */ -struct CadetChannel * -GCT_get_channel (struct CadetTunnel *t, struct GNUNET_CADET_ChannelTunnelNumber ctn); - - -/** - * Decrypt and process an encrypted message. - * - * Calls the appropriate handler for a message in a channel of a local tunnel. - * - * @param t Tunnel this message came on. - * @param msg Message header. - */ -void -GCT_handle_encrypted (struct CadetTunnel *t, - const struct GNUNET_CADET_TunnelEncryptedMessage *msg); - - -/** - * Handle a Key eXchange message. - * - * @param t Tunnel on which the message came. - * @param msg KX message itself. - */ -void -GCT_handle_kx (struct CadetTunnel *t, - const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg); - - -/** - * @brief Use the given path for the tunnel. - * Update the next and prev hops (and RCs). - * (Re)start the path refresh in case the tunnel is locally owned. - * - * @param t Tunnel to update. - * @param p Path to use. - * - * @return Connection created. - */ -struct CadetConnection * -GCT_use_path (struct CadetTunnel *t, struct CadetPeerPath *p); - - -/** - * Count all created connections of a tunnel. Not necessarily ready connections! - * - * @param t Tunnel on which to count. - * - * @return Number of connections created, either being established or ready. - */ -unsigned int -GCT_count_any_connections (struct CadetTunnel *t); - - -/** - * Count established (ready) connections of a tunnel. - * - * @param t Tunnel on which to count. - * - * @return Number of connections. - */ -unsigned int -GCT_count_connections (struct CadetTunnel *t); - - -/** - * Count channels of a tunnel. - * - * @param t Tunnel on which to count. - * - * @return Number of channels. - */ -unsigned int -GCT_count_channels (struct CadetTunnel *t); - - -/** - * Get the connectivity state of a tunnel. - * - * @param t Tunnel. - * - * @return Tunnel's connectivity state. - */ -enum CadetTunnelCState -GCT_get_cstate (struct CadetTunnel *t); - - -/** - * Get the encryption state of a tunnel. - * - * @param t Tunnel. - * - * @return Tunnel's encryption state. - */ -enum CadetTunnelEState -GCT_get_estate (struct CadetTunnel *t); - - -/** - * Get the maximum buffer space for a tunnel towards a local client. - * - * @param t Tunnel. - * - * @return Biggest buffer space offered by any channel in the tunnel. - */ -unsigned int -GCT_get_channels_buffer (struct CadetTunnel *t); - - -/** - * Get the total buffer space for a tunnel for P2P traffic. - * - * @param t Tunnel. - * - * @return Buffer space offered by all connections in the tunnel. - */ -unsigned int -GCT_get_connections_buffer (struct CadetTunnel *t); - - -/** - * Get the tunnel's destination. - * - * @param t Tunnel. - * - * @return ID of the destination peer. - */ -const struct GNUNET_PeerIdentity * -GCT_get_destination (struct CadetTunnel *t); - - -/** - * Get the tunnel's next free Channel ID. - * - * @param t Tunnel. - * - * @return ID of a channel free to use. - */ -struct GNUNET_CADET_ChannelTunnelNumber -GCT_get_next_ctn (struct CadetTunnel *t); - - -/** - * Send ACK on one or more channels due to buffer in connections. - * - * @param t Channel which has some free buffer space. - */ -void -GCT_unchoke_channels (struct CadetTunnel *t); - - -/** - * Send ACK on one or more connections due to buffer space to the client. - * - * Iterates all connections of the tunnel and sends ACKs appropriately. - * - * @param t Tunnel which has some free buffer space. - */ -void -GCT_send_connection_acks (struct CadetTunnel *t); - - -/** - * Cancel a previously sent message while it's in the queue. - * - * ONLY can be called before the continuation given to the send function - * is called. Once the continuation is called, the message is no longer in the - * queue. - * - * @param q Handle to the queue. - */ -void -GCT_cancel (struct CadetTunnelQueue *q); - - -/** - * Check if the tunnel has queued traffic. - * - * @param t Tunnel to check. - * - * @return #GNUNET_YES if there is queued traffic - * #GNUNET_NO otherwise - */ -int -GCT_has_queued_traffic (struct CadetTunnel *t); - -/** - * Sends an already built message on a tunnel, encrypting it and - * choosing the best connection. - * - * @param message Message to send. Function modifies it. - * @param t Tunnel on which this message is transmitted. - * @param c Connection to use (autoselect if NULL). - * @param force Force the tunnel to take the message (buffer overfill). - * @param cont Continuation to call once message is really sent. - * @param cont_cls Closure for @c cont. - * - * @return Handle to cancel message. NULL if @c cont is NULL. - */ -struct CadetTunnelQueue * -GCT_send_prebuilt_message (const struct GNUNET_MessageHeader *message, - struct CadetTunnel *t, struct CadetConnection *c, - int force, GCT_sent cont, void *cont_cls); - - -/** - * Send a KX message. - * - * @param t Tunnel on which to send it. - * @param force_reply Force the other peer to reply with a KX message. - */ -void -GCT_send_kx (struct CadetTunnel *t, int force_reply); - - -/** - * Is the tunnel directed towards the local peer? - * - * @param t Tunnel. - * - * @return #GNUNET_YES if it is loopback. - */ -int -GCT_is_loopback (const struct CadetTunnel *t); - - -/** - * Is the tunnel using this path already? - * - * @param t Tunnel. - * @param p Path. - * - * @return #GNUNET_YES a connection uses this path. - */ -int -GCT_is_path_used (const struct CadetTunnel *t, const struct CadetPeerPath *p); - - -/** - * Get a cost of a path for a tunnel considering existing connections. - * - * @param t Tunnel. - * @param path Candidate path. - * - * @return Cost of the path (path length + number of overlapping nodes) - */ -unsigned int -GCT_get_path_cost (const struct CadetTunnel *t, - const struct CadetPeerPath *path); - - -/** - * Get the static string for the peer this tunnel is directed. - * - * @param t Tunnel. - * - * @return Static string the destination peer's ID. - */ -const char * -GCT_2s (const struct CadetTunnel *t); - - -/** - * Log all possible info about the tunnel state. - * - * @param t Tunnel to debug. - * @param level Debug level to use. - */ -void -GCT_debug (const struct CadetTunnel *t, enum GNUNET_ErrorType level); - - -/** - * Iterate all tunnels. - * - * @param iter Iterator. - * @param cls Closure for @c iter. - */ -void -GCT_iterate_all (GNUNET_CONTAINER_PeerMapIterator iter, void *cls); - - -/** - * Count all tunnels. - * - * @return Number of tunnels to remote peers kept by this peer. - */ -unsigned int -GCT_count_all (void); - - -/** - * Iterate all connections of a tunnel. - * - * @param t Tunnel whose connections to iterate. - * @param iter Iterator. - * @param cls Closure for @c iter. - */ -void -GCT_iterate_connections (struct CadetTunnel *t, GCT_conn_iter iter, void *cls); - - -/** - * Iterate all channels of a tunnel. - * - * @param t Tunnel whose channels to iterate. - * @param iter Iterator. - * @param cls Closure for @c iter. - */ -void -GCT_iterate_channels (struct CadetTunnel *t, - GCT_chan_iter iter, - void *cls); - - -#if 0 /* keep Emacsens' auto-indent happy */ -{ -#endif -#ifdef __cplusplus -} -#endif - -/* ifndef GNUNET_CADET_SERVICE_TUNNEL_H */ -#endif -/* end of gnunet-cadet-service_tunnel.h */ diff --git a/src/cadet/gnunet-service-cadet-new_tunnels.c b/src/cadet/gnunet-service-cadet_tunnels.c index d508606293..bcdeeb4dac 100644 --- a/src/cadet/gnunet-service-cadet-new_tunnels.c +++ b/src/cadet/gnunet-service-cadet_tunnels.c @@ -18,7 +18,7 @@ Boston, MA 02110-1301, USA. */ /** - * @file cadet/gnunet-service-cadet-new_tunnels.c + * @file cadet/gnunet-service-cadet_tunnels.c * @brief Information we track per tunnel. * @author Bartlomiej Polot * @author Christian Grothoff @@ -34,13 +34,12 @@ #include "gnunet_util_lib.h" #include "gnunet_statistics_service.h" #include "gnunet_signatures.h" -#include "gnunet-service-cadet-new.h" #include "cadet_protocol.h" -#include "gnunet-service-cadet-new_channel.h" -#include "gnunet-service-cadet-new_connection.h" -#include "gnunet-service-cadet-new_tunnels.h" -#include "gnunet-service-cadet-new_peer.h" -#include "gnunet-service-cadet-new_paths.h" +#include "gnunet-service-cadet_channel.h" +#include "gnunet-service-cadet_connection.h" +#include "gnunet-service-cadet_tunnels.h" +#include "gnunet-service-cadet_peer.h" +#include "gnunet-service-cadet_paths.h" #define LOG(level, ...) GNUNET_log_from(level,"cadet-tun",__VA_ARGS__) diff --git a/src/cadet/gnunet-service-cadet-new_tunnels.h b/src/cadet/gnunet-service-cadet_tunnels.h index a81bc23412..4a3619ab6c 100644 --- a/src/cadet/gnunet-service-cadet-new_tunnels.h +++ b/src/cadet/gnunet-service-cadet_tunnels.h @@ -20,7 +20,7 @@ */ /** - * @file cadet/gnunet-service-cadet-new_tunnels.h + * @file cadet/gnunet-service-cadet_tunnels.h * @brief Information we track per tunnel. * @author Bartlomiej Polot * @author Christian Grothoff @@ -28,7 +28,7 @@ #ifndef GNUNET_SERVICE_CADET_TUNNELS_H #define GNUNET_SERVICE_CADET_TUNNELS_H -#include "gnunet-service-cadet-new.h" +#include "gnunet-service-cadet.h" #include "cadet_protocol.h" diff --git a/src/cadet/test_cadet.c b/src/cadet/test_cadet.c index e57c01be27..4fe43b3bf0 100644 --- a/src/cadet/test_cadet.c +++ b/src/cadet/test_cadet.c @@ -21,7 +21,7 @@ * @file cadet/test_cadet.c * @author Bart Polot * @author Christian Grothoff - * @brief Test for the cadet service: retransmission of traffic. + * @brief Test for the cadet service using mq API. */ #include <stdio.h> #include "platform.h" @@ -32,9 +32,20 @@ /** - * How many messages to send + * Ugly workaround to unify data handlers on incoming and outgoing channels. */ -#define TOTAL_PACKETS 500 /* Cannot exceed 64k! */ +struct CadetTestChannelWrapper +{ + /** + * Channel pointer. + */ + struct GNUNET_CADET_Channel *ch; +}; + +/** + * How many messages to send by default. + */ +#define TOTAL_PACKETS 500 /* Cannot exceed 64k! */ /** * How long until we give up on connecting the peers? @@ -42,7 +53,7 @@ #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120) /** - * Time to wait for stuff that should be rather fast + * Time to wait by default for stuff that should be rather fast. */ #define SHORT_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20) @@ -73,6 +84,16 @@ static char *test_name; static int test_backwards = GNUNET_NO; /** + * How many packets to send. + */ +static unsigned int total_packets; + +/** + * Time to wait for fast operations. + */ +static struct GNUNET_TIME_Relative short_time; + +/** * How many events have happened */ static int ok; @@ -83,9 +104,9 @@ static int ok; static int ok_goal; /** - * Size of each test packet + * Size of each test packet's payload */ -static size_t size_payload = sizeof (struct GNUNET_MessageHeader) + sizeof (uint32_t); +static size_t size_payload = sizeof (uint32_t); /** * Operation to get peer ids. @@ -158,9 +179,9 @@ static struct GNUNET_SCHEDULER_Task *disconnect_task; static struct GNUNET_SCHEDULER_Task *test_task; /** - * Task runnining #data_task(). + * Task runnining #send_next_msg(). */ -static struct GNUNET_SCHEDULER_Task *data_job; +static struct GNUNET_SCHEDULER_Task *send_next_msg_task; /** * Cadet handle for the root peer @@ -175,7 +196,7 @@ static struct GNUNET_CADET_Handle *h2; /** * Channel handle for the root peer */ -static struct GNUNET_CADET_Channel *ch; +static struct GNUNET_CADET_Channel *outgoing_ch; /** * Channel handle for the dest peer @@ -183,17 +204,6 @@ static struct GNUNET_CADET_Channel *ch; static struct GNUNET_CADET_Channel *incoming_ch; /** - * Transmit handle for root data calls - */ -static struct GNUNET_CADET_TransmitHandle *th; - -/** - * Transmit handle for root data calls - */ -static struct GNUNET_CADET_TransmitHandle *incoming_th; - - -/** * Time we started the data transmission (after channel has been established * and initilized). */ @@ -225,20 +235,26 @@ static unsigned int ka_received; static unsigned int msg_dropped; +/******************************************************************************/ + + +/******************************************************************************/ + + /** - * Get the client number considered as the "target" or "receiver", depending on + * Get the channel considered as the "target" or "receiver", depending on * the test type and size. * - * @return Peer # of the target client, either 0 (for backward tests) or - * the last peer in the line (for other tests). + * @return Channel handle of the target client, either 0 (for backward tests) + * or the last peer in the line (for other tests). */ -static unsigned int -get_expected_target () +static struct GNUNET_CADET_Channel * +get_target_channel () { if (SPEED == test && GNUNET_YES == test_backwards) - return 0; + return outgoing_ch; else - return peers_requested - 1; + return incoming_ch; } @@ -251,18 +267,15 @@ show_end_data (void) 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); + 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 %s\n", - GNUNET_STRINGS_relative_time_to_string (total_time, - GNUNET_YES)); - FPRINTF (stderr, "Test bandwidth: %f kb/s\n", - 4 * TOTAL_PACKETS * 1.0 / (total_time.rel_value_us / 1000)); // 4bytes * ms - FPRINTF (stderr, "Test throughput: %f packets/s\n\n", - TOTAL_PACKETS * 1000.0 / (total_time.rel_value_us / 1000)); // packets * ms + GNUNET_STRINGS_relative_time_to_string (total_time, GNUNET_YES)); + FPRINTF (stderr, "Test bandwidth: %f kb/s\n", 4 * total_packets * 1.0 / (total_time.rel_value_us / 1000)); // 4bytes * ms + FPRINTF (stderr, "Test throughput: %f packets/s\n\n", total_packets * 1000.0 / (total_time.rel_value_us / 1000)); // packets * ms GAUGER ("CADET", test_name, - TOTAL_PACKETS * 1000.0 / (total_time.rel_value_us / 1000), + total_packets * 1000.0 / (total_time.rel_value_us / 1000), "packets/s"); } @@ -281,29 +294,19 @@ disconnect_cadet_peers (void *cls) disconnect_task = NULL; GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "disconnecting cadet service of peers, called from line %ld\n", - line); + "disconnecting cadet service of peers, called from line %ld\n", + line); for (i = 0; i < 2; i++) { GNUNET_TESTBED_operation_done (t_op[i]); } - if (NULL != ch) + if (NULL != outgoing_ch) { - if (NULL != th) - { - GNUNET_CADET_notify_transmit_ready_cancel (th); - th = NULL; - } - GNUNET_CADET_channel_destroy (ch); - ch = NULL; + GNUNET_CADET_channel_destroy (outgoing_ch); + outgoing_ch = NULL; } if (NULL != incoming_ch) { - if (NULL != incoming_th) - { - GNUNET_CADET_notify_transmit_ready_cancel (incoming_th); - incoming_th = NULL; - } GNUNET_CADET_channel_destroy (incoming_ch); incoming_ch = NULL; } @@ -322,10 +325,10 @@ static void shutdown_task (void *cls) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending test.\n"); - if (NULL != data_job) + if (NULL != send_next_msg_task) { - GNUNET_SCHEDULER_cancel (data_job); - data_job = NULL; + GNUNET_SCHEDULER_cancel (send_next_msg_task); + send_next_msg_task = NULL; } if (NULL != test_task) { @@ -335,8 +338,8 @@ shutdown_task (void *cls) if (NULL != disconnect_task) { GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_cadet_peers, - (void *) __LINE__); + disconnect_task = + GNUNET_SCHEDULER_add_now (&disconnect_cadet_peers, (void *) __LINE__); } } @@ -351,17 +354,11 @@ shutdown_task (void *cls) * operation has executed successfully. */ static void -stats_cont (void *cls, - struct GNUNET_TESTBED_Operation *op, - const char *emsg) +stats_cont (void *cls, struct GNUNET_TESTBED_Operation *op, const char *emsg) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - " KA sent: %u, KA received: %u\n", - ka_sent, - ka_received); - if ( (KEEPALIVE == test) && - ( (ka_sent < 2) || - (ka_sent > ka_received + 1)) ) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, " KA sent: %u, KA received: %u\n", + ka_sent, ka_received); + if ((KEEPALIVE == test) && ((ka_sent < 2) || (ka_sent > ka_received + 1))) { GNUNET_break (0); ok--; @@ -370,8 +367,7 @@ stats_cont (void *cls, if (NULL != disconnect_task) GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_cadet_peers, - cls); + disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_cadet_peers, cls); } @@ -387,11 +383,8 @@ stats_cont (void *cls, * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration */ static int -stats_iterator (void *cls, - const struct GNUNET_TESTBED_Peer *peer, - const char *subsystem, - const char *name, - uint64_t value, +stats_iterator (void *cls, const struct GNUNET_TESTBED_Peer *peer, + const char *subsystem, const char *name, uint64_t value, int is_persistent) { static const char *s_sent = "# keepalives sent"; @@ -401,19 +394,15 @@ stats_iterator (void *cls, uint32_t i; i = GNUNET_TESTBED_get_index (peer); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "STATS PEER %u - %s [%s]: %llu\n", - i, - subsystem, - name, - (unsigned long long) value); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "STATS PEER %u - %s [%s]: %llu\n", i, + subsystem, name, (unsigned long long) value); if (0 == strncmp (s_sent, name, strlen (s_sent)) && 0 == i) ka_sent = value; - if (0 == strncmp(s_recv, name, strlen (s_recv)) && peers_requested - 1 == i) + if (0 == strncmp (s_recv, name, strlen (s_recv)) && peers_requested - 1 == i) ka_received = value; - if (0 == strncmp(rdrops, name, strlen (rdrops))) + if (0 == strncmp (rdrops, name, strlen (rdrops))) msg_dropped += value; - if (0 == strncmp(cdrops, name, strlen (cdrops))) + if (0 == strncmp (cdrops, name, strlen (cdrops))) msg_dropped += value; return GNUNET_OK; @@ -423,7 +412,7 @@ stats_iterator (void *cls, /** * Task to gather all statistics. * - * @param cls Closure (NULL). + * @param cls Closure (line from which the task was scheduled). */ static void gather_stats_and_exit (void *cls) @@ -432,21 +421,20 @@ gather_stats_and_exit (void *cls) disconnect_task = NULL; GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "gathering statistics from line %d\n", - (int) l); - if (NULL != ch) + "gathering statistics from line %ld\n", + l); + if (NULL != outgoing_ch) { - if (NULL != th) - { - GNUNET_CADET_notify_transmit_ready_cancel (th); - th = NULL; - } - GNUNET_CADET_channel_destroy (ch); - ch = NULL; + GNUNET_CADET_channel_destroy (outgoing_ch); + outgoing_ch = NULL; } - stats_op = GNUNET_TESTBED_get_statistics (peers_running, testbed_peers, - "cadet", NULL, - &stats_iterator, stats_cont, cls); + stats_op = GNUNET_TESTBED_get_statistics (peers_running, + testbed_peers, + "cadet", + NULL, + &stats_iterator, + stats_cont, + cls); } @@ -462,163 +450,151 @@ abort_test (long line) if (NULL != disconnect_task) { GNUNET_SCHEDULER_cancel (disconnect_task); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Aborting test from %ld\n", line); - disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_cadet_peers, - (void *) line); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Aborting test from %ld\n", line); + disconnect_task = + GNUNET_SCHEDULER_add_now (&disconnect_cadet_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 request a new data transmission. + * Send a message on the channel with the appropriate size and payload. + * + * Update the appropriate *_sent counter. * - * @param cls Closure (peer #). + * @param channel Channel to send the message on. */ static void -data_task (void *cls) +send_test_message (struct GNUNET_CADET_Channel *channel) { - struct GNUNET_CADET_Channel *channel; - static struct GNUNET_CADET_TransmitHandle **pth; - long src; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_MessageHeader *msg; + uint32_t *data; + int payload; + int size; - data_job = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Data task\n"); - if (GNUNET_YES == test_backwards) - { - channel = incoming_ch; - pth = &incoming_th; - src = peers_requested - 1; - } - else + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending test message on channel %p\n", + channel); + size = size_payload; + if (GNUNET_NO == initialized) { - channel = ch; - pth = &th; - src = 0; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending INITIALIZER\n"); + size += 1000; + payload = data_sent; + if (SPEED_ACK == test) // FIXME unify SPEED_ACK with an initializer + data_sent++; } - - GNUNET_assert (NULL != channel); - GNUNET_assert (NULL == *pth); - - *pth = GNUNET_CADET_notify_transmit_ready (channel, GNUNET_NO, - GNUNET_TIME_UNIT_FOREVER_REL, - size_payload + data_sent, - &tmt_rdy, (void *) src); - if (NULL == *pth) + else if (SPEED == test || SPEED_ACK == test) { - unsigned long i = (unsigned long) cls; - - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Retransmission\n"); - if (0 == i) + if (get_target_channel() == channel) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, " in 1 ms\n"); - data_job = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, - &data_task, (void *) 1L); + payload = ack_sent; + size += ack_sent; + ack_sent++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending ACK %u [%d bytes]\n", + payload, size); } else { - i++; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "in %llu ms\n", - (unsigned long long) i); - data_job = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, - i), - &data_task, (void *) i); + payload = data_sent; + size += data_sent; + data_sent++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending DATA %u [%d bytes]\n", + data_sent, size); } } -} + else if (FORWARD == test) + { + payload = ack_sent; + } + else if (P2P_SIGNAL == test) + { + payload = data_sent; + } + else + { + GNUNET_assert (0); + } + env = GNUNET_MQ_msg_extra (msg, size, GNUNET_MESSAGE_TYPE_DUMMY); + data = (uint32_t *) &msg[1]; + *data = htonl (payload); + GNUNET_MQ_send (GNUNET_CADET_get_mq (channel), env); +} /** - * Transmit ready callback + * Task to request a new data transmission in a SPEED test, without waiting + * for previous messages to be sent/arrrive. * - * @param cls Closure (peer # which is sending the data). - * @param size Size of the buffer we have. - * @param buf Buffer to copy data to. + * @param cls Closure (unused). */ -static size_t -tmt_rdy (void *cls, size_t size, void *buf) +static void +send_next_msg (void *cls) { - struct GNUNET_MessageHeader *msg = buf; - size_t msg_size; - uint32_t *data; - long id = (long) cls; - unsigned int counter; + struct GNUNET_CADET_Channel *channel; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "tmt_rdy on %ld, filling buffer\n", - id); - if (0 == id) - th = NULL; - else if ((peers_requested - 1) == id) - incoming_th = NULL; - else - GNUNET_assert (0); - counter = get_expected_target () == id ? ack_sent : data_sent; - msg_size = size_payload + counter; - GNUNET_assert (msg_size > sizeof (struct GNUNET_MessageHeader)); - if ( (size < msg_size) || - (NULL == buf) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "size %u, buf %p, data_sent %u, ack_received %u\n", - (unsigned int) size, - buf, - data_sent, - ack_received); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ok %u, ok goal %u\n", ok, ok_goal); - GNUNET_break (ok >= ok_goal - 2); - - return 0; - } - msg->size = htons (msg_size); - msg->type = htons (GNUNET_MESSAGE_TYPE_DUMMY); - data = (uint32_t *) &msg[1]; - *data = htonl (counter); - if (GNUNET_NO == initialized) + send_next_msg_task = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending next message: %d\n", data_sent); + + channel = GNUNET_YES == test_backwards ? incoming_ch : outgoing_ch; + GNUNET_assert (NULL != channel); + GNUNET_assert (SPEED == test); + send_test_message (channel); + if (data_sent < total_packets) { + /* SPEED test: Send all messages as soon as possible */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "sending initializer\n"); - msg_size = size_payload + 1000; - msg->size = htons (msg_size); - if (SPEED_ACK == test) - data_sent++; + "Scheduling message %d\n", + data_sent + 1); + send_next_msg_task = + GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_SECONDS, + &send_next_msg, + NULL); } - else if ( (SPEED == test) || - (SPEED_ACK == test) ) +} + + +/** + * Every few messages cancel the timeout task and re-schedule it again, to + * avoid timing out when traffic keeps coming. + * + * @param line Code line number to log if a timeout occurs. + */ +static void +reschedule_timeout_task (long line) +{ + if ((ok % 10) == 0) { - if (get_expected_target() == id) - ack_sent++; - else - data_sent++; - counter++; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - " Sent message %u size %u\n", - counter, - (unsigned int) msg_size); - if ( (data_sent < TOTAL_PACKETS) && - (SPEED == test) ) + if (NULL != disconnect_task) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - " Scheduling message %d\n", - counter + 1); - data_job = GNUNET_SCHEDULER_add_now (&data_task, NULL); + " reschedule timeout every 10 messages\n"); + GNUNET_SCHEDULER_cancel (disconnect_task); + disconnect_task = GNUNET_SCHEDULER_add_delayed (short_time, + &gather_stats_and_exit, + (void *) line); } } +} - return msg_size; + +/** + * Check if payload is sane (size contains payload). + * + * @param cls should match #ch + * @param message The actual message. + * @return #GNUNET_OK to keep the channel open, + * #GNUNET_SYSERR to close it (signal serious error). + */ +static int +check_data (void *cls, const struct GNUNET_MessageHeader *message) +{ + if (sizeof (struct GNUNET_MessageHeader) >= ntohs (message->size)) + return GNUNET_SYSERR; + return GNUNET_OK; /* all is well-formed */ } @@ -626,75 +602,50 @@ tmt_rdy (void *cls, size_t size, void *buf) * Function is called whenever a message is received. * * @param cls closure (set from GNUNET_CADET_connect(), peer number) - * @param channel connection to the other end - * @param channel_ctx place to store local state associated with the channel * @param message the actual message - * @return #GNUNET_OK to keep the connection open, - * #GNUNET_SYSERR to close it (signal serious error) */ -static int -data_callback (void *cls, - struct GNUNET_CADET_Channel *channel, - void **channel_ctx, - const struct GNUNET_MessageHeader *message) +static void +handle_data (void *cls, const struct GNUNET_MessageHeader *message) { - struct GNUNET_CADET_TransmitHandle **pth; - long client = (long) cls; - long expected_target_client; + struct CadetTestChannelWrapper *ch = cls; + struct GNUNET_CADET_Channel *channel = ch->ch; uint32_t *data; uint32_t payload; - unsigned int counter; + int *counter; ok++; - counter = get_expected_target () == client ? data_received : ack_received; - GNUNET_CADET_receive_done (channel); + counter = get_target_channel () == channel ? &data_received : &ack_received; - if ((ok % 10) == 0) + reschedule_timeout_task ((long) __LINE__); + + if (channel == outgoing_ch) { - if (NULL != disconnect_task) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - " reschedule timeout\n"); - GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME, - &gather_stats_and_exit, - (void *) __LINE__); - } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Root client got a message.\n"); } - - switch (client) + else if (channel == incoming_ch) { - case 0L: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Root client got a message!\n"); - GNUNET_assert (channel == ch); - pth = &th; - break; - case 1L: - case 4L: - GNUNET_assert (client == peers_requested - 1); - GNUNET_assert (channel == incoming_ch); - pth = &incoming_th; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Leaf client %ld got a message.\n", - client); - break; - default: - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Client %ld not valid.\n", client); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Leaf client got a message.\n"); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unknown channel %p.\n", channel); GNUNET_assert (0); } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: (%d/%d)\n", ok, ok_goal); data = (uint32_t *) &message[1]; payload = ntohl (*data); - if (payload == counter) + if (payload == *counter) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, " payload as expected: %u\n", payload); } else { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, " payload %u, expected: %u\n", - payload, counter); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + " payload %u, expected: %u\n", + payload, *counter); } - expected_target_client = get_expected_target (); if (GNUNET_NO == initialized) { @@ -702,189 +653,152 @@ data_callback (void *cls, start_time = GNUNET_TIME_absolute_get (); if (SPEED == test) { - GNUNET_assert (peers_requested - 1 == client); - data_job = GNUNET_SCHEDULER_add_now (&data_task, NULL); - return GNUNET_OK; + GNUNET_assert (incoming_ch == channel); + send_next_msg_task = GNUNET_SCHEDULER_add_now (&send_next_msg, NULL); + return; } } - counter++; - if (client == expected_target_client) /* Normally 4 */ + (*counter)++; + if (get_target_channel () == channel) /* Got "data" */ { - data_received++; GNUNET_log (GNUNET_ERROR_TYPE_INFO, " received data %u\n", data_received); if (SPEED != test || (ok_goal - 2) == ok) { /* Send ACK */ - GNUNET_assert (NULL == *pth); - *pth = GNUNET_CADET_notify_transmit_ready (channel, GNUNET_NO, - GNUNET_TIME_UNIT_FOREVER_REL, - size_payload + ack_sent, - &tmt_rdy, (void *) client); - return GNUNET_OK; + send_test_message (channel); + return; } else { - if (data_received < TOTAL_PACKETS) - return GNUNET_OK; + if (data_received < total_packets) + return; } } - else /* Normally 0 */ + else /* Got "ack" */ { if (SPEED_ACK == test || SPEED == test) { - ack_received++; GNUNET_log (GNUNET_ERROR_TYPE_INFO, " received ack %u\n", ack_received); - /* send more data */ - GNUNET_assert (NULL == *pth); - *pth = GNUNET_CADET_notify_transmit_ready (channel, GNUNET_NO, - GNUNET_TIME_UNIT_FOREVER_REL, - size_payload + data_sent, - &tmt_rdy, (void *) client); - if (ack_received < TOTAL_PACKETS && SPEED != test) - return GNUNET_OK; + /* Send more data */ + send_test_message (channel); + if (ack_received < total_packets && SPEED != test) + return; if (ok == 2 && SPEED == test) - return GNUNET_OK; - show_end_data(); + return; + show_end_data (); } if (test == P2P_SIGNAL) { - if (NULL != incoming_th) - { - GNUNET_CADET_notify_transmit_ready_cancel (incoming_th); - incoming_th = NULL; - } GNUNET_CADET_channel_destroy (incoming_ch); incoming_ch = NULL; } else { - if (NULL != th) - { - GNUNET_CADET_notify_transmit_ready_cancel (th); - th = NULL; - } - GNUNET_CADET_channel_destroy (ch); - ch = NULL; + GNUNET_CADET_channel_destroy (outgoing_ch); + outgoing_ch = NULL; } } - - return GNUNET_OK; } /** - * Data handlers for every message type of CADET's payload. - * {callback_function, message_type, size_expected} - */ -static struct GNUNET_CADET_MessageHandler handlers[] = { - {&data_callback, - GNUNET_MESSAGE_TYPE_DUMMY, - sizeof (struct GNUNET_MessageHeader)}, - {NULL, 0, 0} -}; - - -/** - * Method called whenever another peer has added us to a channel - * the other peer initiated. + * Method called whenever a peer connects to a port in MQ-based CADET. * - * @param cls Closure. + * @param cls Closure from #GNUNET_CADET_open_porT (peer # as long). * @param channel New handle to the channel. - * @param initiator Peer that started the channel. - * @param port Port this channel is connected to. - * @param options channel option flags - * @return Initial channel context for the channel - * (can be NULL -- that's not an error). + * @param source Peer that started this channel. + * @return Closure for the incoming @a channel. It's given to: + * - The #GNUNET_CADET_DisconnectEventHandler (given to + * #GNUNET_CADET_open_porT) when the channel dies. + * - Each the #GNUNET_MQ_MessageCallback handlers for each message + * received on the @a channel. */ static void * -incoming_channel (void *cls, - struct GNUNET_CADET_Channel *channel, - const struct GNUNET_PeerIdentity *initiator, - const struct GNUNET_HashCode *port, - enum GNUNET_CADET_ChannelOption options) +connect_handler (void *cls, struct GNUNET_CADET_Channel *channel, + const struct GNUNET_PeerIdentity *source) { + struct CadetTestChannelWrapper *ch; + long peer = (long) cls; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Incoming channel from %s to peer %d:%s\n", - GNUNET_i2s (initiator), - (int) (long) cls, GNUNET_h2s (port)); + "Incoming channel from %s to %ld: %p\n", + GNUNET_i2s (source), peer, channel); ok++; GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok); - if ((long) cls == peers_requested - 1) + if (peer == peers_requested - 1) { if (NULL != incoming_ch) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Duplicate incoming channel for client %lu\n", - (long) cls); - GNUNET_break(0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Duplicate incoming channel for client %lu\n", (long) cls); + GNUNET_assert (0); } incoming_ch = channel; } else { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Incoming channel for unexpected peer #%lu\n", - (long) cls); - GNUNET_break (0); + "Incoming channel for unexpected peer #%lu\n", (long) cls); + GNUNET_assert (0); } if (NULL != disconnect_task) { GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME, + disconnect_task = GNUNET_SCHEDULER_add_delayed (short_time, &gather_stats_and_exit, (void *) __LINE__); } - return NULL; + /* TODO: cannot return channel as-is, in order to unify the data handlers */ + ch = GNUNET_new (struct CadetTestChannelWrapper); + ch->ch = channel; + + return ch; } /** - * Function called whenever an inbound channel is destroyed. Should clean up - * any associated state. + * Function called whenever an MQ-channel is destroyed, even if the destruction + * was requested by #GNUNET_CADET_channel_destroy. + * It must NOT call #GNUNET_CADET_channel_destroy on the channel. * - * @param cls closure (set from GNUNET_CADET_connect, peer number) - * @param channel connection to the other end (henceforth invalid) - * @param channel_ctx place where local state associated - * with the channel is stored + * It should clean up any associated state, including cancelling any pending + * transmission on this channel. + * + * @param cls Channel closure (channel wrapper). + * @param channel Connection to the other end (henceforth invalid). */ static void -channel_cleaner (void *cls, - const struct GNUNET_CADET_Channel *channel, - void *channel_ctx) +disconnect_handler (void *cls, const struct GNUNET_CADET_Channel *channel) { - long i = (long) cls; + struct CadetTestChannelWrapper *ch_w = cls; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Incoming channel disconnected at peer %ld\n", - i); - if (peers_running - 1 == i) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Channel disconnected\n"); + GNUNET_assert (ch_w->ch == channel); + if (channel == incoming_ch) { ok++; - GNUNET_break (channel == incoming_ch); incoming_ch = NULL; } - else if (0L == i) + else if (outgoing_ch == channel + ) { if (P2P_SIGNAL == test) { ok++; } - GNUNET_break (channel == ch); - ch = NULL; + outgoing_ch = NULL; } else - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Unknown peer! %d\n", - (int) i); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unknown channel! %p\n", channel); GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok); if (NULL != disconnect_task) { GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = GNUNET_SCHEDULER_add_now (&gather_stats_and_exit, - (void *) __LINE__); + disconnect_task = + GNUNET_SCHEDULER_add_now (&gather_stats_and_exit, (void *) __LINE__); } } @@ -898,13 +812,20 @@ channel_cleaner (void *cls, * @param cls Closure (unused). */ static void -do_test (void *cls) +start_test (void *cls) { + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_var_size (data, + GNUNET_MESSAGE_TYPE_DUMMY, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_handler_end () + }; + struct CadetTestChannelWrapper *ch; enum GNUNET_CADET_ChannelOption flags; test_task = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "do_test\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "start_test\n"); if (NULL != disconnect_task) { GNUNET_SCHEDULER_cancel (disconnect_task); @@ -918,30 +839,33 @@ do_test (void *cls) flags |= GNUNET_CADET_OPTION_RELIABLE; } - ch = GNUNET_CADET_channel_create (h1, - NULL, - p_id[1], - &port, - flags); + ch = GNUNET_new (struct CadetTestChannelWrapper); + outgoing_ch = GNUNET_CADET_channel_creatE (h1, + ch, + p_id[1], + &port, + flags, + NULL, + &disconnect_handler, + handlers); + + ch->ch = outgoing_ch; - disconnect_task - = GNUNET_SCHEDULER_add_delayed (SHORT_TIME, - &gather_stats_and_exit, - (void *) __LINE__); + disconnect_task = GNUNET_SCHEDULER_add_delayed (short_time, + &gather_stats_and_exit, + (void *) __LINE__); if (KEEPALIVE == test) - return; /* Don't send any data. */ + return; /* Don't send any data. */ + - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending data initializer...\n"); data_received = 0; data_sent = 0; ack_received = 0; ack_sent = 0; - th = GNUNET_CADET_notify_transmit_ready (ch, - GNUNET_NO, - GNUNET_TIME_UNIT_FOREVER_REL, - size_payload + 1000, - &tmt_rdy, (void *) 0L); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending data initializer on channel %p...\n", + outgoing_ch); + send_test_message (outgoing_ch); } @@ -955,35 +879,26 @@ do_test (void *cls) * NULL if the operation is successfull */ static void -pi_cb (void *cls, - struct GNUNET_TESTBED_Operation *op, - const struct GNUNET_TESTBED_PeerInformation *pinfo, - const char *emsg) +pi_cb (void *cls, struct GNUNET_TESTBED_Operation *op, + const struct GNUNET_TESTBED_PeerInformation *pinfo, const char *emsg) { long i = (long) cls; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "id callback for %ld\n", i); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ID callback for %ld\n", i); - if ( (NULL == pinfo) || - (NULL != emsg) ) + if ((NULL == pinfo) || (NULL != emsg)) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "pi_cb: %s\n", emsg); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "pi_cb: %s\n", emsg); abort_test (__LINE__); return; } p_id[i] = pinfo->result.id; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - " id: %s\n", GNUNET_i2s (p_id[i])); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " id: %s\n", GNUNET_i2s (p_id[i])); p_ids++; if (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); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got all IDs, starting test\n"); + test_task = GNUNET_SCHEDULER_add_now (&start_test, NULL); } @@ -994,7 +909,7 @@ pi_cb (void *cls, * @param ctx Argument to give to GNUNET_CADET_TEST_cleanup on test end. * @param num_peers Number of peers that are running. * @param peers Array of peers. - * @param cadetes Handle to each of the CADETs of the peers. + * @param cadets Handle to each of the CADETs of the peers. */ static void tmain (void *cls, @@ -1011,16 +926,18 @@ tmain (void *cls, testbed_peers = peers; h1 = cadets[0]; h2 = cadets[num_peers - 1]; - disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME, + disconnect_task = GNUNET_SCHEDULER_add_delayed (short_time, &disconnect_cadet_peers, (void *) __LINE__); GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); t_op[0] = GNUNET_TESTBED_peer_get_information (peers[0], GNUNET_TESTBED_PIT_IDENTITY, - &pi_cb, (void *) 0L); + &pi_cb, + (void *) 0L); t_op[1] = GNUNET_TESTBED_peer_get_information (peers[num_peers - 1], GNUNET_TESTBED_PIT_IDENTITY, - &pi_cb, (void *) 1L); + &pi_cb, + (void *) 1L); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "requested peer ids\n"); } @@ -1031,16 +948,42 @@ tmain (void *cls, int main (int argc, char *argv[]) { + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_var_size (data, + GNUNET_MESSAGE_TYPE_DUMMY, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_handler_end () + }; + initialized = GNUNET_NO; static const struct GNUNET_HashCode *ports[2]; const char *config_file; char port_id[] = "test port"; - GNUNET_CRYPTO_hash (port_id, sizeof (port_id), &port); + + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + {'t', "time", "short_time", + gettext_noop ("set short timeout"), + GNUNET_YES, &GNUNET_GETOPT_set_relative_time, &short_time}, + {'m', "messages", "NUM_MESSAGES", + gettext_noop ("set number of messages to send"), + GNUNET_YES, &GNUNET_GETOPT_set_uint, &total_packets}, + + GNUNET_GETOPT_OPTION_END + }; GNUNET_log_setup ("test", "DEBUG", NULL); - config_file = "test_cadet.conf"; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start\n"); + total_packets = TOTAL_PACKETS; + short_time = SHORT_TIME; + if (-1 == GNUNET_GETOPT_run (argv[0], options, argc, argv)) + { + FPRINTF (stderr, "test failed: problem with CLI parameters\n"); + exit (1); + } + + config_file = "test_cadet.conf"; + GNUNET_CRYPTO_hash (port_id, sizeof (port_id), &port); /* Find out requested size */ if (strstr (argv[0], "_2_") != NULL) @@ -1078,11 +1021,11 @@ main (int argc, char *argv[]) { /* Test is supposed to generate the following callbacks: * 1 incoming channel (@dest) - * TOTAL_PACKETS received data packet (@dest) - * TOTAL_PACKETS received data packet (@orig) + * total_packets received data packet (@dest) + * total_packets received data packet (@orig) * 1 received channel destroy (@dest) */ - ok_goal = TOTAL_PACKETS * 2 + 2; + ok_goal = total_packets * 2 + 2; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED_ACK\n"); test = SPEED_ACK; test_name = "speed ack"; @@ -1092,11 +1035,11 @@ main (int argc, char *argv[]) /* Test is supposed to generate the following callbacks: * 1 incoming channel (@dest) * 1 initial packet (@dest) - * TOTAL_PACKETS received data packet (@dest) + * total_packets received data packet (@dest) * 1 received data packet (@orig) * 1 received channel destroy (@dest) */ - ok_goal = TOTAL_PACKETS + 4; + ok_goal = total_packets + 4; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED\n"); if (strstr (argv[0], "_reliable") != NULL) { @@ -1137,22 +1080,22 @@ main (int argc, char *argv[]) p_ids = 0; ports[0] = &port; ports[1] = NULL; - GNUNET_CADET_TEST_run ("test_cadet_small", - config_file, - peers_requested, - &tmain, - NULL, /* tmain cls */ - &incoming_channel, - &channel_cleaner, - handlers, - ports); + GNUNET_CADET_TEST_ruN ("test_cadet_small", + config_file, + peers_requested, + &tmain, + NULL, /* tmain cls */ + &connect_handler, + NULL, + &disconnect_handler, + handlers, + ports); if (NULL != strstr (argv[0], "_reliable")) - msg_dropped = 0; /* dropped should be retransmitted */ + msg_dropped = 0; /* dropped should be retransmitted */ if (ok_goal > ok - msg_dropped) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "FAILED! (%d/%d)\n", ok, ok_goal); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "FAILED! (%d/%d)\n", ok, ok_goal); return 1; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "success\n"); diff --git a/src/cadet/test_cadet_local.c b/src/cadet/test_cadet_local.c deleted file mode 100644 index 2b915ab813..0000000000 --- a/src/cadet/test_cadet_local.c +++ /dev/null @@ -1,351 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2011 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file cadet/test_cadet_local.c - * @brief test cadet local: test of cadet channels with just one peer - * @author Bartlomiej Polot - */ - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_dht_service.h" -#include "gnunet_testing_lib.h" -#include "gnunet_cadet_service.h" - -struct GNUNET_TESTING_Peer *me; - -static struct GNUNET_CADET_Handle *cadet_peer_1; - -static struct GNUNET_CADET_Handle *cadet_peer_2; - -static struct GNUNET_CADET_Channel *ch; - -static int result = GNUNET_OK; - -static int got_data = GNUNET_NO; - -static struct GNUNET_SCHEDULER_Task *abort_task; - -static struct GNUNET_SCHEDULER_Task *connect_task; - -static struct GNUNET_CADET_TransmitHandle *mth; - - -/** - * Connect to other client and send data - * - * @param cls Closue (unused). - */ -static void -do_connect (void *cls); - - -/** - * Shutdown nicely - */ -static void -do_shutdown (void *cls) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "shutdown\n"); - if (NULL != abort_task) - { - GNUNET_SCHEDULER_cancel (abort_task); - abort_task = NULL; - } - if (NULL != ch) - { - GNUNET_CADET_channel_destroy (ch); - ch = NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Disconnect client 1\n"); - if (NULL != cadet_peer_1) - { - GNUNET_CADET_disconnect (cadet_peer_1); - cadet_peer_1 = NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Disconnect client 2\n"); - if (NULL != cadet_peer_2) - { - GNUNET_CADET_disconnect (cadet_peer_2); - cadet_peer_2 = NULL; - } - if (NULL != connect_task) - { - GNUNET_SCHEDULER_cancel (connect_task); - connect_task = NULL; - } -} - - -/** - * Something went wrong and timed out. Kill everything and set error flag - */ -static void -do_abort (void *cls) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ABORT\n"); - result = GNUNET_SYSERR; - abort_task = NULL; - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Function is called whenever a message is received. - * - * @param cls closure (set from GNUNET_CADET_connect) - * @param channel connection to the other end - * @param channel_ctx place to store local state associated with the channel - * @param message the actual message - * @return #GNUNET_OK to keep the connection open, - * #GNUNET_SYSERR to close it (signal serious error) - */ -static int -data_callback (void *cls, - struct GNUNET_CADET_Channel *channel, - void **channel_ctx, - const struct GNUNET_MessageHeader *message) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Data callback! Shutting down.\n"); - got_data = GNUNET_YES; - GNUNET_SCHEDULER_shutdown (); - GNUNET_CADET_receive_done (channel); - return GNUNET_OK; -} - - -/** - * Method called whenever another peer has added us to a channel - * the other peer initiated. - * - * @param cls closure - * @param channel new handle to the channel - * @param initiator peer that started the channel - * @param port port number - * @param options channel options - * @return initial channel context for the channel - * (can be NULL -- that's not an error) - */ -static void * -inbound_channel (void *cls, - struct GNUNET_CADET_Channel *channel, - const struct GNUNET_PeerIdentity *initiator, - const struct GNUNET_HashCode *port, - enum GNUNET_CADET_ChannelOption options) -{ - long id = (long) cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "received incoming channel on peer %d, port %s\n", - (int) id, - GNUNET_h2s (port)); - if (id != 2L) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "wrong peer\n"); - result = GNUNET_SYSERR; - } - return NULL; -} - - -/** - * Function called whenever an channel is destroyed. Should clean up - * any associated state. - * - * @param cls closure (set from GNUNET_CADET_connect) - * @param channel connection to the other end (henceforth invalid) - * @param channel_ctx place where local state associated - * with the channel is stored - */ -static void -channel_end (void *cls, - const struct GNUNET_CADET_Channel *channel, - void *channel_ctx) -{ - long id = (long) cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "incoming channel closed at peer %ld\n", - id); - if (NULL != mth) - { - GNUNET_CADET_notify_transmit_ready_cancel (mth); - mth = NULL; - } - if (channel == ch) - ch = NULL; - if (GNUNET_NO == got_data) - { - if (NULL == connect_task) - connect_task - = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, - 2), - &do_connect, - NULL); - } -} - - -/** - * Handler array for traffic received on peer1 - */ -static struct GNUNET_CADET_MessageHandler handlers1[] = { - {&data_callback, 1, 0}, - {NULL, 0, 0} -}; - - -/** - * Handler array for traffic received on peer2 (none expected) - */ -static struct GNUNET_CADET_MessageHandler handlers2[] = { - {&data_callback, 1, 0}, - {NULL, 0, 0} -}; - - -/** - * Data send callback: fillbuffer with test packet. - * - * @param cls Closure (unused). - * @param size Buffer size. - * @param buf Buffer to fill. - * - * @return size of test packet. - */ -static size_t -do_send (void *cls, size_t size, void *buf) -{ - struct GNUNET_MessageHeader *m = buf; - - mth = NULL; - if (NULL == buf) - { - GNUNET_break (0); - result = GNUNET_SYSERR; - return 0; - } - m->size = htons (sizeof (struct GNUNET_MessageHeader)); - m->type = htons (1); - GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader)); - return sizeof (struct GNUNET_MessageHeader); -} - - -/** - * Connect to other client and send data - * - * @param cls Closue (unused). - */ -static void -do_connect (void *cls) -{ - struct GNUNET_PeerIdentity id; - - connect_task = NULL; - GNUNET_TESTING_peer_get_identity (me, &id); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "CONNECT BY PORT\n"); - ch = GNUNET_CADET_channel_create (cadet_peer_1, - NULL, - &id, GC_u2h (1), - GNUNET_CADET_OPTION_DEFAULT); - mth = GNUNET_CADET_notify_transmit_ready (ch, GNUNET_NO, - GNUNET_TIME_UNIT_FOREVER_REL, - sizeof (struct GNUNET_MessageHeader), - &do_send, NULL); -} - - -/** - * Initialize framework and start test - * - * @param cls Closure (unused). - * @param cfg Configuration handle. - * @param peer Testing peer handle. - */ -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - me = peer; - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, - NULL); - abort_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 15), - &do_abort, - NULL); - cadet_peer_1 = GNUNET_CADET_connect (cfg, /* configuration */ - (void *) 1L, /* cls */ - &channel_end, /* channel end hndlr */ - handlers1); /* traffic handlers */ - cadet_peer_2 = GNUNET_CADET_connect (cfg, /* configuration */ - (void *) 2L, /* cls */ - &channel_end, /* channel end hndlr */ - handlers2); /* traffic handlers */ - - if ( (NULL == cadet_peer_1) || - (NULL == cadet_peer_2) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Couldn't connect to cadet :(\n"); - result = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - return; - } - GNUNET_CADET_open_port (cadet_peer_2, - GC_u2h (1), - &inbound_channel, - (void *) 2L); - if (NULL == connect_task) - connect_task - = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, - 2), - &do_connect, - NULL); -} - - -/** - * Main - */ -int -main (int argc, char *argv[]) -{ - if (0 != GNUNET_TESTING_peer_run ("test-cadet-local", - "test_cadet.conf", - &run, NULL)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "run failed\n"); - return 2; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Final result: %d\n", result); - return (result == GNUNET_OK) ? 0 : 1; -} - -/* end of test_cadet_local_1.c */ diff --git a/src/cadet/test_cadet_new.c b/src/cadet/test_cadet_new.c deleted file mode 100644 index 374e86bf3c..0000000000 --- a/src/cadet/test_cadet_new.c +++ /dev/null @@ -1,1105 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2011, 2017 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file cadet/test_cadet_mq.c - * @author Bart Polot - * @author Christian Grothoff - * @brief Test for the cadet service using mq API. - */ -#include <stdio.h> -#include "platform.h" -#include "cadet_test_lib_new.h" -#include "gnunet_cadet_service.h" -#include "gnunet_statistics_service.h" -#include <gauger.h> - - -/** - * Ugly workaround to unify data handlers on incoming and outgoing channels. - */ -struct CadetTestChannelWrapper -{ - /** - * Channel pointer. - */ - struct GNUNET_CADET_Channel *ch; -}; - -/** - * How many messages to send by default. - */ -#define TOTAL_PACKETS 500 /* Cannot exceed 64k! */ - -/** - * How long until we give up on connecting the peers? - */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120) - -/** - * Time to wait by default for stuff that should be rather fast. - */ -#define SHORT_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20) - -/** - * DIFFERENT TESTS TO RUN - */ -#define SETUP 0 -#define FORWARD 1 -#define KEEPALIVE 2 -#define SPEED 3 -#define SPEED_ACK 4 -#define SPEED_REL 8 -#define P2P_SIGNAL 10 - -/** - * Which test are we running? - */ -static int test; - -/** - * String with test name - */ -static 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 packets to send. - */ -static unsigned int total_packets; - -/** - * Time to wait for fast operations. - */ -static struct GNUNET_TIME_Relative short_time; - -/** - * How many events have happened - */ -static int ok; - -/** - * Number of events expected to conclude the test successfully. - */ -static int ok_goal; - -/** - * Size of each test packet's payload - */ -static size_t size_payload = sizeof (uint32_t); - -/** - * Operation to get peer ids. - */ -static struct GNUNET_TESTBED_Operation *t_op[2]; - -/** - * Peer ids. - */ -static struct GNUNET_PeerIdentity *p_id[2]; - -/** - * Port ID - */ -static struct GNUNET_HashCode port; - -/** - * Peer ids counter. - */ -static unsigned int p_ids; - -/** - * Is the setup initialized? - */ -static int initialized; - -/** - * Number of payload packes sent. - */ -static int data_sent; - -/** - * Number of payload packets received. - */ -static int data_received; - -/** - * Number of payload packed acknowledgements sent. - */ -static int ack_sent; - -/** - * Number of payload packed explicitly (app level) acknowledged. - */ -static int ack_received; - -/** - * Total number of peers asked to run. - */ -static unsigned long long peers_requested; - -/** - * Number of currently running peers (should be same as @c peers_requested). - */ -static unsigned long long peers_running; - -/** - * Test context (to shut down). - */ -struct GNUNET_CADET_TEST_Context *test_ctx; - -/** - * Task called to disconnect peers. - */ -static struct GNUNET_SCHEDULER_Task *disconnect_task; - -/** - * Task To perform tests - */ -static struct GNUNET_SCHEDULER_Task *test_task; - -/** - * Task runnining #send_next_msg(). - */ -static struct GNUNET_SCHEDULER_Task *send_next_msg_task; - -/** - * Cadet handle for the root peer - */ -static struct GNUNET_CADET_Handle *h1; - -/** - * Cadet handle for the first leaf peer - */ -static struct GNUNET_CADET_Handle *h2; - -/** - * Channel handle for the root peer - */ -static struct GNUNET_CADET_Channel *outgoing_ch; - -/** - * Channel handle for the dest peer - */ -static struct GNUNET_CADET_Channel *incoming_ch; - -/** - * Time we started the data transmission (after channel has been established - * and initilized). - */ -static struct GNUNET_TIME_Absolute start_time; - -/** - * Peers handle. - */ -static struct GNUNET_TESTBED_Peer **testbed_peers; - -/** - * Statistics operation handle. - */ -static struct GNUNET_TESTBED_Operation *stats_op; - -/** - * Keepalives sent. - */ -static unsigned int ka_sent; - -/** - * Keepalives received. - */ -static unsigned int ka_received; - -/** - * How many messages were dropped by CADET because of full buffers? - */ -static unsigned int msg_dropped; - - -/******************************************************************************/ - - -/******************************************************************************/ - - -/** - * Get the channel considered as the "target" or "receiver", depending on - * the test type and size. - * - * @return Channel handle of the target client, either 0 (for backward tests) - * or the last peer in the line (for other tests). - */ -static struct GNUNET_CADET_Channel * -get_target_channel () -{ - if (SPEED == test && GNUNET_YES == test_backwards) - return outgoing_ch; - else - return incoming_ch; -} - - -/** - * Show the results of the test (banwidth acheived) and log them to GAUGER - */ -static void -show_end_data (void) -{ - 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 %s\n", - GNUNET_STRINGS_relative_time_to_string (total_time, GNUNET_YES)); - FPRINTF (stderr, "Test bandwidth: %f kb/s\n", 4 * total_packets * 1.0 / (total_time.rel_value_us / 1000)); // 4bytes * ms - FPRINTF (stderr, "Test throughput: %f packets/s\n\n", total_packets * 1000.0 / (total_time.rel_value_us / 1000)); // packets * ms - GAUGER ("CADET", test_name, - total_packets * 1000.0 / (total_time.rel_value_us / 1000), - "packets/s"); -} - - -/** - * Disconnect from cadet services af all peers, call shutdown. - * - * @param cls Closure (line number from which termination was requested). - * @param tc Task Context. - */ -static void -disconnect_cadet_peers (void *cls) -{ - long line = (long) cls; - unsigned int i; - - disconnect_task = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "disconnecting cadet service of peers, called from line %ld\n", - line); - for (i = 0; i < 2; i++) - { - GNUNET_TESTBED_operation_done (t_op[i]); - } - if (NULL != outgoing_ch) - { - GNUNET_CADET_channel_destroy (outgoing_ch); - outgoing_ch = NULL; - } - if (NULL != incoming_ch) - { - GNUNET_CADET_channel_destroy (incoming_ch); - incoming_ch = NULL; - } - GNUNET_CADET_TEST_cleanup (test_ctx); - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Shut down peergroup, clean up. - * - * @param cls Closure (unused). - * @param tc Task Context. - */ -static void -shutdown_task (void *cls) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending test.\n"); - if (NULL != send_next_msg_task) - { - GNUNET_SCHEDULER_cancel (send_next_msg_task); - send_next_msg_task = NULL; - } - if (NULL != test_task) - { - GNUNET_SCHEDULER_cancel (test_task); - test_task = NULL; - } - if (NULL != disconnect_task) - { - GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = - GNUNET_SCHEDULER_add_now (&disconnect_cadet_peers, (void *) __LINE__); - } -} - - -/** - * Stats callback. Finish the stats testbed operation and when all stats have - * been iterated, shutdown the test. - * - * @param cls Closure (line number from which termination was requested). - * @param op the operation that has been finished - * @param emsg error message in case the operation has failed; will be NULL if - * operation has executed successfully. - */ -static void -stats_cont (void *cls, struct GNUNET_TESTBED_Operation *op, const char *emsg) -{ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, " KA sent: %u, KA received: %u\n", - ka_sent, ka_received); - if ((KEEPALIVE == test) && ((ka_sent < 2) || (ka_sent > ka_received + 1))) - { - GNUNET_break (0); - ok--; - } - GNUNET_TESTBED_operation_done (stats_op); - - if (NULL != disconnect_task) - GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_cadet_peers, cls); -} - - -/** - * Process statistic values. - * - * @param cls closure (line number, unused) - * @param peer the peer the statistic belong to - * @param subsystem name of subsystem that created the statistic - * @param name the name of the datum - * @param value the current value - * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not - * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration - */ -static int -stats_iterator (void *cls, const struct GNUNET_TESTBED_Peer *peer, - const char *subsystem, const char *name, uint64_t value, - int is_persistent) -{ - static const char *s_sent = "# keepalives sent"; - static const char *s_recv = "# keepalives received"; - static const char *rdrops = "# messages dropped due to full buffer"; - static const char *cdrops = "# messages dropped due to slow client"; - uint32_t i; - - i = GNUNET_TESTBED_get_index (peer); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "STATS PEER %u - %s [%s]: %llu\n", i, - subsystem, name, (unsigned long long) value); - if (0 == strncmp (s_sent, name, strlen (s_sent)) && 0 == i) - ka_sent = value; - if (0 == strncmp (s_recv, name, strlen (s_recv)) && peers_requested - 1 == i) - ka_received = value; - if (0 == strncmp (rdrops, name, strlen (rdrops))) - msg_dropped += value; - if (0 == strncmp (cdrops, name, strlen (cdrops))) - msg_dropped += value; - - return GNUNET_OK; -} - - -/** - * Task to gather all statistics. - * - * @param cls Closure (line from which the task was scheduled). - */ -static void -gather_stats_and_exit (void *cls) -{ - long l = (long) cls; - - disconnect_task = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "gathering statistics from line %ld\n", - l); - if (NULL != outgoing_ch) - { - GNUNET_CADET_channel_destroy (outgoing_ch); - outgoing_ch = NULL; - } - stats_op = GNUNET_TESTBED_get_statistics (peers_running, - testbed_peers, - "cadet", - NULL, - &stats_iterator, - stats_cont, - cls); -} - - - -/** - * Abort test: schedule disconnect and shutdown immediately - * - * @param line Line in the code the abort is requested from (__LINE__). - */ -static void -abort_test (long line) -{ - if (NULL != disconnect_task) - { - GNUNET_SCHEDULER_cancel (disconnect_task); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Aborting test from %ld\n", line); - disconnect_task = - GNUNET_SCHEDULER_add_now (&disconnect_cadet_peers, (void *) line); - } -} - - -/** - * Send a message on the channel with the appropriate size and payload. - * - * Update the appropriate *_sent counter. - * - * @param channel Channel to send the message on. - */ -static void -send_test_message (struct GNUNET_CADET_Channel *channel) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_MessageHeader *msg; - uint32_t *data; - int payload; - int size; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending test message on channel %p\n", - channel); - size = size_payload; - if (GNUNET_NO == initialized) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending INITIALIZER\n"); - size += 1000; - payload = data_sent; - if (SPEED_ACK == test) // FIXME unify SPEED_ACK with an initializer - data_sent++; - } - else if (SPEED == test || SPEED_ACK == test) - { - if (get_target_channel() == channel) - { - payload = ack_sent; - size += ack_sent; - ack_sent++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending ACK %u [%d bytes]\n", - payload, size); - } - else - { - payload = data_sent; - size += data_sent; - data_sent++; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending DATA %u [%d bytes]\n", - data_sent, size); - } - } - else if (FORWARD == test) - { - payload = ack_sent; - } - else if (P2P_SIGNAL == test) - { - payload = data_sent; - } - else - { - GNUNET_assert (0); - } - env = GNUNET_MQ_msg_extra (msg, size, GNUNET_MESSAGE_TYPE_DUMMY); - - data = (uint32_t *) &msg[1]; - *data = htonl (payload); - GNUNET_MQ_send (GNUNET_CADET_get_mq (channel), env); -} - -/** - * Task to request a new data transmission in a SPEED test, without waiting - * for previous messages to be sent/arrrive. - * - * @param cls Closure (unused). - */ -static void -send_next_msg (void *cls) -{ - struct GNUNET_CADET_Channel *channel; - - send_next_msg_task = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending next message: %d\n", data_sent); - - channel = GNUNET_YES == test_backwards ? incoming_ch : outgoing_ch; - GNUNET_assert (NULL != channel); - GNUNET_assert (SPEED == test); - send_test_message (channel); - if (data_sent < total_packets) - { - /* SPEED test: Send all messages as soon as possible */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Scheduling message %d\n", - data_sent + 1); - send_next_msg_task = - GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_SECONDS, - &send_next_msg, - NULL); - } -} - - -/** - * Every few messages cancel the timeout task and re-schedule it again, to - * avoid timing out when traffic keeps coming. - * - * @param line Code line number to log if a timeout occurs. - */ -static void -reschedule_timeout_task (long line) -{ - if ((ok % 10) == 0) - { - if (NULL != disconnect_task) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - " reschedule timeout every 10 messages\n"); - GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = GNUNET_SCHEDULER_add_delayed (short_time, - &gather_stats_and_exit, - (void *) line); - } - } -} - - -/** - * Check if payload is sane (size contains payload). - * - * @param cls should match #ch - * @param message The actual message. - * @return #GNUNET_OK to keep the channel open, - * #GNUNET_SYSERR to close it (signal serious error). - */ -static int -check_data (void *cls, const struct GNUNET_MessageHeader *message) -{ - if (sizeof (struct GNUNET_MessageHeader) >= ntohs (message->size)) - return GNUNET_SYSERR; - return GNUNET_OK; /* all is well-formed */ -} - - -/** - * Function is called whenever a message is received. - * - * @param cls closure (set from GNUNET_CADET_connect(), peer number) - * @param message the actual message - */ -static void -handle_data (void *cls, const struct GNUNET_MessageHeader *message) -{ - struct CadetTestChannelWrapper *ch = cls; - struct GNUNET_CADET_Channel *channel = ch->ch; - uint32_t *data; - uint32_t payload; - int *counter; - - ok++; - GNUNET_CADET_receive_done (channel); - counter = get_target_channel () == channel ? &data_received : &ack_received; - - reschedule_timeout_task ((long) __LINE__); - - if (channel == outgoing_ch) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Root client got a message.\n"); - } - else if (channel == incoming_ch) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Leaf client got a message.\n"); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unknown channel %p.\n", channel); - GNUNET_assert (0); - } - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: (%d/%d)\n", ok, ok_goal); - data = (uint32_t *) &message[1]; - payload = ntohl (*data); - if (payload == *counter) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, " payload as expected: %u\n", payload); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - " payload %u, expected: %u\n", - payload, *counter); - } - - if (GNUNET_NO == initialized) - { - initialized = GNUNET_YES; - start_time = GNUNET_TIME_absolute_get (); - if (SPEED == test) - { - GNUNET_assert (incoming_ch == channel); - send_next_msg_task = GNUNET_SCHEDULER_add_now (&send_next_msg, NULL); - return; - } - } - - (*counter)++; - if (get_target_channel () == channel) /* Got "data" */ - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, " received data %u\n", data_received); - if (SPEED != test || (ok_goal - 2) == ok) - { - /* Send ACK */ - send_test_message (channel); - return; - } - else - { - if (data_received < total_packets) - return; - } - } - else /* Got "ack" */ - { - if (SPEED_ACK == test || SPEED == test) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, " received ack %u\n", ack_received); - /* Send more data */ - send_test_message (channel); - if (ack_received < total_packets && SPEED != test) - return; - if (ok == 2 && SPEED == test) - return; - show_end_data (); - } - if (test == P2P_SIGNAL) - { - GNUNET_CADET_channel_destroy (incoming_ch); - incoming_ch = NULL; - } - else - { - GNUNET_CADET_channel_destroy (outgoing_ch); - outgoing_ch = NULL; - } - } -} - - -/** - * Method called whenever a peer connects to a port in MQ-based CADET. - * - * @param cls Closure from #GNUNET_CADET_open_porT (peer # as long). - * @param channel New handle to the channel. - * @param source Peer that started this channel. - * @return Closure for the incoming @a channel. It's given to: - * - The #GNUNET_CADET_DisconnectEventHandler (given to - * #GNUNET_CADET_open_porT) when the channel dies. - * - Each the #GNUNET_MQ_MessageCallback handlers for each message - * received on the @a channel. - */ -static void * -connect_handler (void *cls, struct GNUNET_CADET_Channel *channel, - const struct GNUNET_PeerIdentity *source) -{ - struct CadetTestChannelWrapper *ch; - long peer = (long) cls; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Incoming channel from %s to %ld: %p\n", - GNUNET_i2s (source), peer, channel); - ok++; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok); - if (peer == peers_requested - 1) - { - if (NULL != incoming_ch) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Duplicate incoming channel for client %lu\n", (long) cls); - GNUNET_assert (0); - } - incoming_ch = channel; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Incoming channel for unexpected peer #%lu\n", (long) cls); - GNUNET_assert (0); - } - if (NULL != disconnect_task) - { - GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = GNUNET_SCHEDULER_add_delayed (short_time, - &gather_stats_and_exit, - (void *) __LINE__); - } - - /* TODO: cannot return channel as-is, in order to unify the data handlers */ - ch = GNUNET_new (struct CadetTestChannelWrapper); - ch->ch = channel; - - return ch; -} - - -/** - * Function called whenever an MQ-channel is destroyed, even if the destruction - * was requested by #GNUNET_CADET_channel_destroy. - * It must NOT call #GNUNET_CADET_channel_destroy on the channel. - * - * It should clean up any associated state, including cancelling any pending - * transmission on this channel. - * - * @param cls Channel closure (channel wrapper). - * @param channel Connection to the other end (henceforth invalid). - */ -static void -disconnect_handler (void *cls, const struct GNUNET_CADET_Channel *channel) -{ - struct CadetTestChannelWrapper *ch_w = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Channel disconnected\n"); - GNUNET_assert (ch_w->ch == channel); - if (channel == incoming_ch) - { - ok++; - incoming_ch = NULL; - } - else if (outgoing_ch == channel - ) - { - if (P2P_SIGNAL == test) - { - ok++; - } - outgoing_ch = NULL; - } - else - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unknown channel! %p\n", channel); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok); - - if (NULL != disconnect_task) - { - GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = - GNUNET_SCHEDULER_add_now (&gather_stats_and_exit, (void *) __LINE__); - } -} - - -/** - * START THE TESTCASE ITSELF, AS WE ARE CONNECTED TO THE CADET SERVICES. - * - * Testcase continues when the root receives confirmation of connected peers, - * on callback function ch. - * - * @param cls Closure (unused). - */ -static void -start_test (void *cls) -{ - struct GNUNET_MQ_MessageHandler handlers[] = { - GNUNET_MQ_hd_var_size (data, - GNUNET_MESSAGE_TYPE_DUMMY, - struct GNUNET_MessageHeader, - NULL), - GNUNET_MQ_handler_end () - }; - struct CadetTestChannelWrapper *ch; - enum GNUNET_CADET_ChannelOption flags; - - test_task = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "start_test\n"); - if (NULL != disconnect_task) - { - GNUNET_SCHEDULER_cancel (disconnect_task); - disconnect_task = NULL; - } - - flags = GNUNET_CADET_OPTION_DEFAULT; - if (SPEED_REL == test) - { - test = SPEED; - flags |= GNUNET_CADET_OPTION_RELIABLE; - } - - ch = GNUNET_new (struct CadetTestChannelWrapper); - outgoing_ch = GNUNET_CADET_channel_creatE (h1, - ch, - p_id[1], - &port, - flags, - NULL, - &disconnect_handler, - handlers); - - ch->ch = outgoing_ch; - - disconnect_task = GNUNET_SCHEDULER_add_delayed (short_time, - &gather_stats_and_exit, - (void *) __LINE__); - if (KEEPALIVE == test) - return; /* Don't send any data. */ - - - data_received = 0; - data_sent = 0; - ack_received = 0; - ack_sent = 0; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending data initializer on channel %p...\n", - outgoing_ch); - send_test_message (outgoing_ch); -} - - -/** - * Callback to be called when the requested peer information is available - * - * @param cls the closure from GNUNET_TESTBED_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 - */ -static void -pi_cb (void *cls, struct GNUNET_TESTBED_Operation *op, - const struct GNUNET_TESTBED_PeerInformation *pinfo, const char *emsg) -{ - long i = (long) cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ID callback for %ld\n", i); - - if ((NULL == pinfo) || (NULL != emsg)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "pi_cb: %s\n", emsg); - abort_test (__LINE__); - return; - } - p_id[i] = pinfo->result.id; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " id: %s\n", GNUNET_i2s (p_id[i])); - p_ids++; - if (p_ids < 2) - return; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got all IDs, starting test\n"); - test_task = GNUNET_SCHEDULER_add_now (&start_test, NULL); -} - - -/** - * test main: start test when all peers are connected - * - * @param cls Closure. - * @param ctx Argument to give to GNUNET_CADET_TEST_cleanup on test end. - * @param num_peers Number of peers that are running. - * @param peers Array of peers. - * @param cadets Handle to each of the CADETs of the peers. - */ -static void -tmain (void *cls, - struct GNUNET_CADET_TEST_Context *ctx, - unsigned int num_peers, - struct GNUNET_TESTBED_Peer **peers, - struct GNUNET_CADET_Handle **cadets) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test main\n"); - ok = 0; - test_ctx = ctx; - peers_running = num_peers; - GNUNET_assert (peers_running == peers_requested); - testbed_peers = peers; - h1 = cadets[0]; - h2 = cadets[num_peers - 1]; - disconnect_task = GNUNET_SCHEDULER_add_delayed (short_time, - &disconnect_cadet_peers, - (void *) __LINE__); - GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); - t_op[0] = GNUNET_TESTBED_peer_get_information (peers[0], - GNUNET_TESTBED_PIT_IDENTITY, - &pi_cb, - (void *) 0L); - t_op[1] = GNUNET_TESTBED_peer_get_information (peers[num_peers - 1], - GNUNET_TESTBED_PIT_IDENTITY, - &pi_cb, - (void *) 1L); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "requested peer ids\n"); -} - - -/** - * Main: start test - */ -int -main (int argc, char *argv[]) -{ - struct GNUNET_MQ_MessageHandler handlers[] = { - GNUNET_MQ_hd_var_size (data, - GNUNET_MESSAGE_TYPE_DUMMY, - struct GNUNET_MessageHeader, - NULL), - GNUNET_MQ_handler_end () - }; - - initialized = GNUNET_NO; - static const struct GNUNET_HashCode *ports[2]; - const char *config_file; - char port_id[] = "test port"; - - static const struct GNUNET_GETOPT_CommandLineOption options[] = { - {'t', "time", "short_time", - gettext_noop ("set short timeout"), - GNUNET_YES, &GNUNET_GETOPT_set_relative_time, &short_time}, - {'m', "messages", "NUM_MESSAGES", - gettext_noop ("set number of messages to send"), - GNUNET_YES, &GNUNET_GETOPT_set_uint, &total_packets}, - - GNUNET_GETOPT_OPTION_END - }; - - GNUNET_log_setup ("test", "DEBUG", NULL); - - total_packets = TOTAL_PACKETS; - short_time = SHORT_TIME; - if (-1 == GNUNET_GETOPT_run (argv[0], options, argc, argv)) - { - FPRINTF (stderr, "test failed: problem with CLI parameters\n"); - exit (1); - } - - config_file = "test_cadet.conf"; - GNUNET_CRYPTO_hash (port_id, sizeof (port_id), &port); - - /* Find out requested size */ - if (strstr (argv[0], "_2_") != NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "DIRECT CONNECTIONs\n"); - peers_requested = 2; - } - else if (strstr (argv[0], "_5_") != NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "5 PEER LINE\n"); - peers_requested = 5; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "SIZE UNKNOWN, USING 2\n"); - peers_requested = 2; - } - - /* Find out requested test */ - if (strstr (argv[0], "_forward") != NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "FORWARD\n"); - test = FORWARD; - test_name = "unicast"; - ok_goal = 4; - } - else if (strstr (argv[0], "_signal") != NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SIGNAL\n"); - test = P2P_SIGNAL; - test_name = "signal"; - ok_goal = 4; - } - else if (strstr (argv[0], "_speed_ack") != NULL) - { - /* Test is supposed to generate the following callbacks: - * 1 incoming channel (@dest) - * total_packets received data packet (@dest) - * total_packets received data packet (@orig) - * 1 received channel destroy (@dest) - */ - ok_goal = total_packets * 2 + 2; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED_ACK\n"); - test = SPEED_ACK; - test_name = "speed ack"; - } - else if (strstr (argv[0], "_speed") != NULL) - { - /* Test is supposed to generate the following callbacks: - * 1 incoming channel (@dest) - * 1 initial packet (@dest) - * total_packets received data packet (@dest) - * 1 received data packet (@orig) - * 1 received channel destroy (@dest) - */ - ok_goal = total_packets + 4; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED\n"); - if (strstr (argv[0], "_reliable") != NULL) - { - test = SPEED_REL; - test_name = "speed reliable"; - config_file = "test_cadet_drop.conf"; - } - else - { - test = SPEED; - test_name = "speed"; - } - } - else if (strstr (argv[0], "_keepalive") != NULL) - { - test = KEEPALIVE; - /* Test is supposed to generate the following callbacks: - * 1 incoming channel (@dest) - * [wait] - * 1 received channel destroy (@dest) - */ - ok_goal = 2; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "UNKNOWN\n"); - test = SETUP; - ok_goal = 0; - } - - if (strstr (argv[0], "backwards") != NULL) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "BACKWARDS (LEAF TO ROOT)\n"); - test_backwards = GNUNET_YES; - GNUNET_asprintf (&test_name, "backwards %s", test_name); - } - - p_ids = 0; - ports[0] = &port; - ports[1] = NULL; - GNUNET_CADET_TEST_ruN ("test_cadet_small", - config_file, - peers_requested, - &tmain, - NULL, /* tmain cls */ - &connect_handler, - NULL, - &disconnect_handler, - handlers, - ports); - if (NULL != strstr (argv[0], "_reliable")) - msg_dropped = 0; /* dropped should be retransmitted */ - - if (ok_goal > ok - msg_dropped) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "FAILED! (%d/%d)\n", ok, ok_goal); - return 1; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "success\n"); - return 0; -} - -/* end of test_cadet.c */ diff --git a/src/cadet/test_cadet_single.c b/src/cadet/test_cadet_single.c deleted file mode 100644 index b45b0af5df..0000000000 --- a/src/cadet/test_cadet_single.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2011 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file cadet/test_cadet_single.c - * @brief test cadet single: test of cadet channels with just one client - * @author Bartlomiej Polot - */ - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_dht_service.h" -#include "gnunet_testing_lib.h" -#include "gnunet_cadet_service.h" - -#define REPETITIONS 5 -#define DATA_SIZE 35000 - -struct GNUNET_TESTING_Peer *me; - -static struct GNUNET_CADET_Handle *cadet; - -static struct GNUNET_CADET_Channel *ch1; - -static struct GNUNET_CADET_Channel *ch2; - -static int result; - -static struct GNUNET_SCHEDULER_Task *abort_task; - -static struct GNUNET_SCHEDULER_Task *connect_task; - -static unsigned int repetition; - -static struct GNUNET_CADET_TransmitHandle *nth; - -static struct GNUNET_CADET_Port *port; - - -/* forward declaration */ -static size_t -do_send (void *cls, size_t size, void *buf); - - -/** - * Shutdown nicely - */ -static void -do_shutdown (void *cls) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "shutdown\n"); - if (NULL != port) - { - GNUNET_CADET_close_port (port); - port = NULL; - } - if (NULL != nth) - { - GNUNET_CADET_notify_transmit_ready_cancel (nth); - nth = NULL; - } - if (NULL != abort_task) - { - GNUNET_SCHEDULER_cancel (abort_task); - abort_task = NULL; - } - if (NULL != connect_task) - { - GNUNET_SCHEDULER_cancel (connect_task); - connect_task = NULL; - } - if (NULL != ch1) - { - GNUNET_CADET_channel_destroy (ch1); - ch1 = NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Disconnect clients\n"); - if (NULL != cadet) - { - GNUNET_CADET_disconnect (cadet); - cadet = NULL; - } - else - { - GNUNET_break (0); - } -} - - -/** - * Something went wrong and timed out. Kill everything and set error flag - */ -static void -do_abort (void *cls) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ABORT\n"); - result = GNUNET_SYSERR; - abort_task = NULL; - GNUNET_SCHEDULER_shutdown (); -} - - -/** - * Function is called whenever a message is received. - * - * @param cls closure (set from GNUNET_CADET_connect) - * @param channel connection to the other end - * @param channel_ctx place to store local state associated with the channel - * @param message the actual message - * @return #GNUNET_OK to keep the connection open, - * #GNUNET_SYSERR to close it (signal serious error) - */ -static int -data_callback (void *cls, - struct GNUNET_CADET_Channel *channel, - void **channel_ctx, - const struct GNUNET_MessageHeader *message) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Data callback! Repetition %u/%u\n", - repetition, REPETITIONS); - repetition++; - if (repetition < REPETITIONS) - { - struct GNUNET_CADET_Channel *my_channel; - if (0 == repetition % 2) - my_channel = ch1; - else - my_channel = ch2; - nth = GNUNET_CADET_notify_transmit_ready (my_channel, - GNUNET_NO, - GNUNET_TIME_UNIT_FOREVER_REL, - sizeof (struct GNUNET_MessageHeader) - + DATA_SIZE, - &do_send, NULL); - GNUNET_CADET_receive_done (channel); - return GNUNET_OK; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "All data OK. Destroying channel.\n"); - GNUNET_assert (NULL == nth); - GNUNET_CADET_channel_destroy (ch1); - ch1 = NULL; - return GNUNET_OK; -} - - -/** - * Method called whenever another peer has added us to a channel - * the other peer initiated. - * - * @param cls closure - * @param channel new handle to the channel - * @param initiator peer that started the channel - * @param port port number - * @param options channel option flags - * @return initial channel context for the channel - * (can be NULL -- that's not an error) - */ -static void * -inbound_channel (void *cls, - struct GNUNET_CADET_Channel *channel, - const struct GNUNET_PeerIdentity *initiator, - const struct GNUNET_HashCode *port, - enum GNUNET_CADET_ChannelOption options) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "received incoming channel on port %s\n", - GNUNET_h2s (port)); - ch2 = channel; - return NULL; -} - - -/** - * Function called whenever an inbound channel is destroyed. Should clean up - * any associated state. - * - * @param cls closure (set from GNUNET_CADET_connect) - * @param channel connection to the other end (henceforth invalid) - * @param channel_ctx place where local state associated - * with the channel is stored - */ -static void -channel_end (void *cls, - const struct GNUNET_CADET_Channel *channel, - void *channel_ctx) -{ - long id = (long) cls; - - nth = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "incoming channel closed at peer %ld\n", - id); - if ( (REPETITIONS == repetition) && - (channel == ch2) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "everything fine! finishing!\n"); - result = GNUNET_OK; - GNUNET_SCHEDULER_shutdown (); - } - if (channel == ch2) - ch2 = NULL; - if (channel == ch1) - ch1 = NULL; -} - - -/** - * Handler array for traffic received on peer1 - */ -static struct GNUNET_CADET_MessageHandler handlers1[] = { - {&data_callback, 1, 0}, - {NULL, 0, 0} -}; - - -/** - * Data send callback: fillbuffer with test packet. - * - * @param cls Closure (unused). - * @param size Buffer size. - * @param buf Buffer to fill. - * @return size of test packet. - */ -static size_t -do_send (void *cls, size_t size, void *buf) -{ - struct GNUNET_MessageHeader *m = buf; - - nth = NULL; - if (NULL == buf) - { - GNUNET_break (0); - result = GNUNET_SYSERR; - return 0; - } - m->size = htons (sizeof (struct GNUNET_MessageHeader) + DATA_SIZE); - m->type = htons (1); - memset (&m[1], 0, DATA_SIZE); - GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader) + DATA_SIZE); - return sizeof (struct GNUNET_MessageHeader) + DATA_SIZE; -} - -/** - * Connect to other client and send data - * - * @param cls Closue (unused). - */ -static void -do_connect (void *cls) -{ - struct GNUNET_PeerIdentity id; - size_t size = sizeof (struct GNUNET_MessageHeader) + DATA_SIZE; - - connect_task = NULL; - GNUNET_TESTING_peer_get_identity (me, &id); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "CONNECT BY PORT\n"); - ch1 = GNUNET_CADET_channel_create (cadet, NULL, &id, GC_u2h (1), - GNUNET_CADET_OPTION_DEFAULT); - nth = GNUNET_CADET_notify_transmit_ready (ch1, - GNUNET_NO, - GNUNET_TIME_UNIT_FOREVER_REL, - size, - &do_send, - NULL); -} - - -/** - * Initialize framework and start test - * - * @param cls Closure (unused). - * @param cfg Configuration handle. - * @param peer Testing peer handle. - */ -static void -run (void *cls, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_TESTING_Peer *peer) -{ - me = peer; - GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); - abort_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 15), &do_abort, - NULL); - cadet = GNUNET_CADET_connect (cfg, /* configuration */ - (void *) 1L, /* cls */ - &channel_end, /* inbound end hndlr */ - handlers1); /* traffic handlers */ - port = GNUNET_CADET_open_port (cadet, - GC_u2h (1), - &inbound_channel, - (void *) 1L); - - - if (NULL == cadet) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Couldn't connect to cadet :(\n"); - result = GNUNET_SYSERR; - return; - } - connect_task - = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &do_connect, - NULL); -} - - -/** - * Main - */ -int -main (int argc, - char *argv[]) -{ - result = GNUNET_NO; - if (0 != GNUNET_TESTING_peer_run ("test-cadet-local", - "test_cadet.conf", - &run, NULL)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "run failed\n"); - return 2; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Final result: %d\n", - result); - return (result == GNUNET_OK) ? 0 : 1; -} - -/* end of test_cadet_single.c */ diff --git a/src/datacache/Makefile.am b/src/datacache/Makefile.am index 431b3179e9..670a64926e 100644 --- a/src/datacache/Makefile.am +++ b/src/datacache/Makefile.am @@ -53,6 +53,7 @@ libgnunet_plugin_datacache_sqlite_la_SOURCES = \ plugin_datacache_sqlite.c libgnunet_plugin_datacache_sqlite_la_LIBADD = \ $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/sq/libgnunetsq.la \ $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) -lsqlite3 \ $(LTLIBINTL) libgnunet_plugin_datacache_sqlite_la_LDFLAGS = \ diff --git a/src/datacache/plugin_datacache_sqlite.c b/src/datacache/plugin_datacache_sqlite.c index 5567077d3f..5cc48b26c9 100644 --- a/src/datacache/plugin_datacache_sqlite.c +++ b/src/datacache/plugin_datacache_sqlite.c @@ -26,6 +26,7 @@ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_datacache_plugin.h" +#include "gnunet_sq_lib.h" #include <sqlite3.h> #define LOG(kind,...) GNUNET_log_from (kind, "datacache-sqlite", __VA_ARGS__) @@ -60,6 +61,41 @@ struct Plugin char *fn; /** + * Prepared statement for #sqlite_plugin_put. + */ + sqlite3_stmt *insert_stmt; + + /** + * Prepared statement for #sqlite_plugin_get. + */ + sqlite3_stmt *get_count_stmt; + + /** + * Prepared statement for #sqlite_plugin_get. + */ + sqlite3_stmt *get_stmt; + + /** + * Prepared statement for #sqlite_plugin_del. + */ + sqlite3_stmt *del_select_stmt; + + /** + * Prepared statement for #sqlite_plugin_del. + */ + sqlite3_stmt *del_stmt; + + /** + * Prepared statement for #sqlite_plugin_get_random. + */ + sqlite3_stmt *get_random_stmt; + + /** + * Prepared statement for #sqlite_plugin_get_closest. + */ + sqlite3_stmt *get_closest_stmt; + + /** * Number of key-value pairs in the database. */ unsigned int num_items; @@ -132,60 +168,47 @@ sqlite_plugin_put (void *cls, const struct GNUNET_PeerIdentity *path_info) { struct Plugin *plugin = cls; - sqlite3_stmt *stmt; - int64_t dval; + uint32_t type32 = type; + struct GNUNET_SQ_QueryParam params[] = { + GNUNET_SQ_query_param_uint32 (&type32), + GNUNET_SQ_query_param_absolute_time (&discard_time), + GNUNET_SQ_query_param_auto_from_type (key), + GNUNET_SQ_query_param_fixed_size (data, size), + GNUNET_SQ_query_param_fixed_size (path_info, + path_info_len * sizeof (struct GNUNET_PeerIdentity)), + GNUNET_SQ_query_param_end + }; LOG (GNUNET_ERROR_TYPE_DEBUG, - "Processing PUT of %u bytes with key `%4s' and expiration %s\n", + "Processing PUT of %u bytes with key `%s' and expiration %s\n", (unsigned int) size, GNUNET_h2s (key), - GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (discard_time), GNUNET_YES)); - dval = (int64_t) discard_time.abs_value_us; - if (dval < 0) - dval = INT64_MAX; - if (sq_prepare - (plugin->dbh, - "INSERT INTO ds090 (type, expire, key, value, path) VALUES (?, ?, ?, ?, ?)", - &stmt) != SQLITE_OK) - { - LOG_SQLITE (plugin->dbh, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sq_prepare"); - return -1; - } - if ((SQLITE_OK != sqlite3_bind_int (stmt, 1, type)) || - (SQLITE_OK != sqlite3_bind_int64 (stmt, 2, dval)) || - (SQLITE_OK != - sqlite3_bind_blob (stmt, 3, - key, sizeof (struct GNUNET_HashCode), - SQLITE_TRANSIENT)) || - (SQLITE_OK != sqlite3_bind_blob (stmt, 4, - data, size, - SQLITE_TRANSIENT)) || - (SQLITE_OK != sqlite3_bind_blob (stmt, 5, - path_info, - path_info_len * sizeof (struct GNUNET_PeerIdentity), - SQLITE_TRANSIENT))) + GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_remaining (discard_time), + GNUNET_YES)); + if (GNUNET_OK != + GNUNET_SQ_bind (plugin->insert_stmt, + params)) { LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite3_bind_xxx"); - sqlite3_finalize (stmt); + GNUNET_SQ_reset (plugin->dbh, + plugin->insert_stmt); return -1; } - if (SQLITE_DONE != sqlite3_step (stmt)) + if (SQLITE_DONE != + sqlite3_step (plugin->insert_stmt)) { LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite3_step"); - sqlite3_finalize (stmt); + GNUNET_SQ_reset (plugin->dbh, + plugin->insert_stmt); return -1; } plugin->num_items++; - if (SQLITE_OK != sqlite3_finalize (stmt)) - LOG_SQLITE (plugin->dbh, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_finalize"); + GNUNET_SQ_reset (plugin->dbh, + plugin->insert_stmt); return size + OVERHEAD; } @@ -209,120 +232,119 @@ sqlite_plugin_get (void *cls, void *iter_cls) { struct Plugin *plugin = cls; - sqlite3_stmt *stmt; + uint32_t type32 = type; struct GNUNET_TIME_Absolute now; struct GNUNET_TIME_Absolute exp; - unsigned int size; - const char *dat; + size_t size; + void *dat; unsigned int cnt; - unsigned int off; + uint32_t off; unsigned int total; - unsigned int psize; - char scratch[256]; - int64_t ntime; - const struct GNUNET_PeerIdentity *path; + size_t psize; + struct GNUNET_PeerIdentity *path; + struct GNUNET_SQ_QueryParam params_count[] = { + GNUNET_SQ_query_param_auto_from_type (key), + GNUNET_SQ_query_param_uint32 (&type32), + GNUNET_SQ_query_param_absolute_time (&now), + GNUNET_SQ_query_param_end + }; + struct GNUNET_SQ_QueryParam params_select[] = { + GNUNET_SQ_query_param_auto_from_type (key), + GNUNET_SQ_query_param_uint32 (&type32), + GNUNET_SQ_query_param_absolute_time (&now), + GNUNET_SQ_query_param_uint32 (&off), + GNUNET_SQ_query_param_end + }; + struct GNUNET_SQ_ResultSpec rs[] = { + GNUNET_SQ_result_spec_variable_size (&dat, + &size), + GNUNET_SQ_result_spec_absolute_time (&exp), + GNUNET_SQ_result_spec_variable_size ((void **) &path, + &psize), + GNUNET_SQ_result_spec_end + }; now = GNUNET_TIME_absolute_get (); LOG (GNUNET_ERROR_TYPE_DEBUG, - "Processing GET for key `%4s'\n", + "Processing GET for key `%s'\n", GNUNET_h2s (key)); - if (sq_prepare - (plugin->dbh, - "SELECT count(*) FROM ds090 WHERE key=? AND type=? AND expire >= ?", - &stmt) != SQLITE_OK) - { - LOG_SQLITE (plugin->dbh, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sq_prepare"); - return 0; - } - ntime = (int64_t) now.abs_value_us; - GNUNET_assert (ntime >= 0); - if ((SQLITE_OK != - sqlite3_bind_blob (stmt, 1, key, sizeof (struct GNUNET_HashCode), - SQLITE_TRANSIENT)) || - (SQLITE_OK != sqlite3_bind_int (stmt, 2, type)) || - (SQLITE_OK != sqlite3_bind_int64 (stmt, 3, now.abs_value_us))) + + if (GNUNET_OK != + GNUNET_SQ_bind (plugin->get_count_stmt, + params_count)) { LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite3_bind_xxx"); - sqlite3_finalize (stmt); + GNUNET_SQ_reset (plugin->dbh, + plugin->get_count_stmt); return 0; } - - if (SQLITE_ROW != sqlite3_step (stmt)) + if (SQLITE_ROW != + sqlite3_step (plugin->get_count_stmt)) { LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite_step"); - sqlite3_finalize (stmt); + GNUNET_SQ_reset (plugin->dbh, + plugin->get_count_stmt); LOG (GNUNET_ERROR_TYPE_DEBUG, - "No content found when processing GET for key `%4s'\n", + "No content found when processing GET for key `%s'\n", GNUNET_h2s (key)); return 0; } - total = sqlite3_column_int (stmt, 0); - sqlite3_finalize (stmt); - if ((0 == total) || (NULL == iter)) + total = sqlite3_column_int (plugin->get_count_stmt, + 0); + GNUNET_SQ_reset (plugin->dbh, + plugin->get_count_stmt); + if ( (0 == total) || + (NULL == iter) ) { if (0 == total) LOG (GNUNET_ERROR_TYPE_DEBUG, - "No content found when processing GET for key `%4s'\n", + "No content found when processing GET for key `%s'\n", GNUNET_h2s (key)); return total; } cnt = 0; - off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, total); + off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, + total); while (cnt < total) { off = (off + 1) % total; - GNUNET_snprintf (scratch, sizeof (scratch), - "SELECT value,expire,path FROM ds090 WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET %u", - off); - if (sq_prepare (plugin->dbh, scratch, &stmt) != SQLITE_OK) - { - LOG_SQLITE (plugin->dbh, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sq_prepare"); - return cnt; - } - if ((SQLITE_OK != - sqlite3_bind_blob (stmt, 1, - key, - sizeof (struct GNUNET_HashCode), - SQLITE_TRANSIENT)) || - (SQLITE_OK != sqlite3_bind_int (stmt, 2, type)) || - (SQLITE_OK != sqlite3_bind_int64 (stmt, 3, now.abs_value_us))) + if (GNUNET_OK != + GNUNET_SQ_bind (plugin->get_stmt, + params_select)) { LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite3_bind_xxx"); - sqlite3_finalize (stmt); + GNUNET_SQ_reset (plugin->dbh, + plugin->get_stmt); return cnt; } - if (sqlite3_step (stmt) != SQLITE_ROW) + if (SQLITE_ROW != + sqlite3_step (plugin->get_stmt)) break; - size = sqlite3_column_bytes (stmt, 0); - dat = sqlite3_column_blob (stmt, 0); - exp.abs_value_us = sqlite3_column_int64 (stmt, 1); - psize = sqlite3_column_bytes (stmt, 2); + if (GNUNET_OK != + GNUNET_SQ_extract_result (plugin->get_stmt, + rs)) + { + GNUNET_break (0); + GNUNET_SQ_reset (plugin->dbh, + plugin->get_stmt); + break; + } if (0 != psize % sizeof (struct GNUNET_PeerIdentity)) { GNUNET_break (0); psize = 0; + path = NULL; } psize /= sizeof (struct GNUNET_PeerIdentity); - if (0 != psize) - path = sqlite3_column_blob (stmt, 2); - else - path = NULL; - ntime = (int64_t) exp.abs_value_us; - if (ntime == INT64_MAX) - exp = GNUNET_TIME_UNIT_FOREVER_ABS; cnt++; LOG (GNUNET_ERROR_TYPE_DEBUG, - "Found %u-byte result when processing GET for key `%4s'\n", + "Found %u-byte result when processing GET for key `%s'\n", (unsigned int) size, GNUNET_h2s (key)); if (GNUNET_OK != iter (iter_cls, @@ -334,11 +356,17 @@ sqlite_plugin_get (void *cls, psize, path)) { - sqlite3_finalize (stmt); + GNUNET_SQ_cleanup_result (rs); + GNUNET_SQ_reset (plugin->dbh, + plugin->get_stmt); break; } - sqlite3_finalize (stmt); + GNUNET_SQ_cleanup_result (rs); + GNUNET_SQ_reset (plugin->dbh, + plugin->get_stmt); } + GNUNET_SQ_reset (plugin->dbh, + plugin->get_stmt); return cnt; } @@ -354,79 +382,73 @@ static int sqlite_plugin_del (void *cls) { struct Plugin *plugin = cls; - unsigned long long rowid; - unsigned int dsize; - sqlite3_stmt *stmt; - sqlite3_stmt *dstmt; + uint64_t rowid; + void *data; + size_t dsize; struct GNUNET_HashCode hc; + struct GNUNET_SQ_ResultSpec rs[] = { + GNUNET_SQ_result_spec_uint64 (&rowid), + GNUNET_SQ_result_spec_auto_from_type (&hc), + GNUNET_SQ_result_spec_variable_size ((void **) &data, + &dsize), + GNUNET_SQ_result_spec_end + }; + struct GNUNET_SQ_QueryParam params[] = { + GNUNET_SQ_query_param_uint64 (&rowid), + GNUNET_SQ_query_param_end + }; LOG (GNUNET_ERROR_TYPE_DEBUG, "Processing DEL\n"); - stmt = NULL; - dstmt = NULL; - if (SQLITE_OK != - sq_prepare (plugin->dbh, - "SELECT _ROWID_,key,value FROM ds090 ORDER BY expire ASC LIMIT 1", - &stmt)) - { - LOG_SQLITE (plugin->dbh, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sq_prepare"); - if (stmt != NULL) - (void) sqlite3_finalize (stmt); - return GNUNET_SYSERR; - } - if (SQLITE_ROW != sqlite3_step (stmt)) + if (SQLITE_ROW != + sqlite3_step (plugin->del_select_stmt)) { LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite3_step"); - (void) sqlite3_finalize (stmt); + GNUNET_SQ_reset (plugin->dbh, + plugin->del_select_stmt); return GNUNET_SYSERR; } - rowid = sqlite3_column_int64 (stmt, 0); - GNUNET_assert (sqlite3_column_bytes (stmt, 1) == sizeof (struct GNUNET_HashCode)); - GNUNET_memcpy (&hc, sqlite3_column_blob (stmt, 1), sizeof (struct GNUNET_HashCode)); - dsize = sqlite3_column_bytes (stmt, 2); - if (SQLITE_OK != sqlite3_finalize (stmt)) - LOG_SQLITE (plugin->dbh, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_step"); - if (SQLITE_OK != - sq_prepare (plugin->dbh, - "DELETE FROM ds090 WHERE _ROWID_=?", &dstmt)) + if (GNUNET_OK != + GNUNET_SQ_extract_result (plugin->del_select_stmt, + rs)) { - LOG_SQLITE (plugin->dbh, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sq_prepare"); - if (stmt != NULL) - (void) sqlite3_finalize (stmt); + GNUNET_break (0); + GNUNET_SQ_reset (plugin->dbh, + plugin->del_select_stmt); return GNUNET_SYSERR; } - if (SQLITE_OK != sqlite3_bind_int64 (dstmt, 1, rowid)) + GNUNET_SQ_cleanup_result (rs); + GNUNET_SQ_reset (plugin->dbh, + plugin->del_select_stmt); + if (GNUNET_OK != + GNUNET_SQ_bind (plugin->del_stmt, + params)) { LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite3_bind"); - (void) sqlite3_finalize (dstmt); + GNUNET_SQ_reset (plugin->dbh, + plugin->del_stmt); return GNUNET_SYSERR; } - if (SQLITE_DONE != sqlite3_step (dstmt)) + if (SQLITE_DONE != + sqlite3_step (plugin->del_stmt)) { LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite3_step"); - (void) sqlite3_finalize (dstmt); + GNUNET_SQ_reset (plugin->dbh, + plugin->del_stmt); return GNUNET_SYSERR; } plugin->num_items--; plugin->env->delete_notify (plugin->env->cls, &hc, dsize + OVERHEAD); - if (SQLITE_OK != sqlite3_finalize (dstmt)) - LOG_SQLITE (plugin->dbh, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_finalize"); + GNUNET_SQ_reset (plugin->dbh, + plugin->del_stmt); return GNUNET_OK; } @@ -445,17 +467,28 @@ sqlite_plugin_get_random (void *cls, void *iter_cls) { struct Plugin *plugin = cls; - sqlite3_stmt *stmt; struct GNUNET_TIME_Absolute exp; - unsigned int size; - const char *dat; - unsigned int off; - unsigned int psize; - unsigned int type; - char scratch[256]; - int64_t ntime; - const struct GNUNET_PeerIdentity *path; - const struct GNUNET_HashCode *key; + size_t size; + void *dat; + uint32_t off; + size_t psize; + uint32_t type; + struct GNUNET_PeerIdentity *path; + struct GNUNET_HashCode key; + struct GNUNET_SQ_QueryParam params[] = { + GNUNET_SQ_query_param_uint32 (&off), + GNUNET_SQ_query_param_end + }; + struct GNUNET_SQ_ResultSpec rs[] = { + GNUNET_SQ_result_spec_variable_size (&dat, + &size), + GNUNET_SQ_result_spec_absolute_time (&exp), + GNUNET_SQ_result_spec_variable_size ((void **) &path, + &psize), + GNUNET_SQ_result_spec_auto_from_type (&key), + GNUNET_SQ_result_spec_uint32 (&type), + GNUNET_SQ_result_spec_end + }; if (0 == plugin->num_items) return 0; @@ -463,60 +496,51 @@ sqlite_plugin_get_random (void *cls, return 1; off = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, plugin->num_items); - GNUNET_snprintf (scratch, - sizeof (scratch), - "SELECT value,expire,path,key,type FROM ds090 ORDER BY key LIMIT 1 OFFSET %u", - off); - if (SQLITE_OK != - sq_prepare (plugin->dbh, scratch, &stmt)) + if (GNUNET_OK != + GNUNET_SQ_bind (plugin->get_random_stmt, + params)) { - LOG_SQLITE (plugin->dbh, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sq_prepare"); return 0; } - if (SQLITE_ROW != sqlite3_step (stmt)) + if (SQLITE_ROW != + sqlite3_step (plugin->get_random_stmt)) + { + GNUNET_break (0); + GNUNET_SQ_reset (plugin->dbh, + plugin->get_random_stmt); + return 0; + } + if (GNUNET_OK != + GNUNET_SQ_extract_result (plugin->get_random_stmt, + rs)) { GNUNET_break (0); - sqlite3_finalize (stmt); + GNUNET_SQ_reset (plugin->dbh, + plugin->get_random_stmt); return 0; } - size = sqlite3_column_bytes (stmt, 0); - dat = sqlite3_column_blob (stmt, 0); - exp.abs_value_us = sqlite3_column_int64 (stmt, 1); - psize = sqlite3_column_bytes (stmt, 2); if (0 != psize % sizeof (struct GNUNET_PeerIdentity)) { GNUNET_break (0); psize = 0; + path = NULL; } psize /= sizeof (struct GNUNET_PeerIdentity); - if (0 != psize) - path = sqlite3_column_blob (stmt, 2); - else - path = NULL; - - GNUNET_assert (sizeof (struct GNUNET_HashCode) == - sqlite3_column_bytes (stmt, 3)); - key = sqlite3_column_blob (stmt, 3); - type = sqlite3_column_int (stmt, 4); - - ntime = (int64_t) exp.abs_value_us; - if (ntime == INT64_MAX) - exp = GNUNET_TIME_UNIT_FOREVER_ABS; LOG (GNUNET_ERROR_TYPE_DEBUG, "Found %u-byte result with key %s when processing GET-RANDOM\n", (unsigned int) size, - GNUNET_h2s (key)); + GNUNET_h2s (&key)); (void) iter (iter_cls, - key, + &key, size, dat, - type, + (enum GNUNET_BLOCK_Type) type, exp, psize, path); - sqlite3_finalize (stmt); + GNUNET_SQ_cleanup_result (rs); + GNUNET_SQ_reset (plugin->dbh, + plugin->get_random_stmt); return 1; } @@ -542,83 +566,73 @@ sqlite_plugin_get_closest (void *cls, void *iter_cls) { struct Plugin *plugin = cls; - sqlite3_stmt *stmt; + uint32_t num_results32 = num_results; struct GNUNET_TIME_Absolute now; struct GNUNET_TIME_Absolute exp; - unsigned int size; - const char *dat; + size_t size; + void *dat; unsigned int cnt; - unsigned int psize; - unsigned int type; - int64_t ntime; - const struct GNUNET_PeerIdentity *path; + size_t psize; + uint32_t type; + struct GNUNET_HashCode hc; + struct GNUNET_PeerIdentity *path; + struct GNUNET_SQ_QueryParam params[] = { + GNUNET_SQ_query_param_auto_from_type (key), + GNUNET_SQ_query_param_absolute_time (&now), + GNUNET_SQ_query_param_uint32 (&num_results32), + GNUNET_SQ_query_param_end + }; + struct GNUNET_SQ_ResultSpec rs[] = { + GNUNET_SQ_result_spec_variable_size (&dat, + &size), + GNUNET_SQ_result_spec_absolute_time (&exp), + GNUNET_SQ_result_spec_variable_size ((void **) &path, + &psize), + GNUNET_SQ_result_spec_uint32 (&type), + GNUNET_SQ_result_spec_auto_from_type (&hc), + GNUNET_SQ_result_spec_end + }; now = GNUNET_TIME_absolute_get (); LOG (GNUNET_ERROR_TYPE_DEBUG, - "Processing GET_CLOSEST for key `%4s'\n", + "Processing GET_CLOSEST for key `%s'\n", GNUNET_h2s (key)); - if (SQLITE_OK != - sq_prepare (plugin->dbh, - "SELECT value,expire,path,type,key FROM ds090 WHERE key>=? AND expire >= ? ORDER BY KEY ASC LIMIT ?", - &stmt)) - { - LOG_SQLITE (plugin->dbh, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sq_prepare"); - return 0; - } - ntime = (int64_t) now.abs_value_us; - GNUNET_assert (ntime >= 0); - if ((SQLITE_OK != - sqlite3_bind_blob (stmt, - 1, - key, - sizeof (struct GNUNET_HashCode), - SQLITE_TRANSIENT)) || - (SQLITE_OK != sqlite3_bind_int64 (stmt, 2, now.abs_value_us)) || - (SQLITE_OK != sqlite3_bind_int (stmt, 3, num_results)) ) + if (GNUNET_OK != + GNUNET_SQ_bind (plugin->get_closest_stmt, + params)) { LOG_SQLITE (plugin->dbh, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite3_bind_xxx"); - sqlite3_finalize (stmt); + GNUNET_SQ_reset (plugin->dbh, + plugin->get_closest_stmt); return 0; } cnt = 0; - while (SQLITE_ROW == sqlite3_step (stmt)) + while (SQLITE_ROW == + sqlite3_step (plugin->get_closest_stmt)) { - if (sizeof (struct GNUNET_HashCode) != - sqlite3_column_bytes (stmt, 4)) + if (GNUNET_OK != + GNUNET_SQ_extract_result (plugin->get_closest_stmt, + rs)) { GNUNET_break (0); break; } - size = sqlite3_column_bytes (stmt, 0); - dat = sqlite3_column_blob (stmt, 0); - exp.abs_value_us = sqlite3_column_int64 (stmt, 1); - psize = sqlite3_column_bytes (stmt, 2); - type = sqlite3_column_int (stmt, 3); - key = sqlite3_column_blob (stmt, 4); if (0 != psize % sizeof (struct GNUNET_PeerIdentity)) { GNUNET_break (0); psize = 0; + path = NULL; } psize /= sizeof (struct GNUNET_PeerIdentity); - if (0 != psize) - path = sqlite3_column_blob (stmt, 2); - else - path = NULL; - ntime = (int64_t) exp.abs_value_us; - if (ntime == INT64_MAX) - exp = GNUNET_TIME_UNIT_FOREVER_ABS; cnt++; LOG (GNUNET_ERROR_TYPE_DEBUG, "Found %u-byte result at %s when processing GET_CLOSE\n", (unsigned int) size, - GNUNET_h2s (key)); + GNUNET_h2s (&hc)); if (GNUNET_OK != iter (iter_cls, - key, + &hc, size, dat, type, @@ -626,11 +640,13 @@ sqlite_plugin_get_closest (void *cls, psize, path)) { - sqlite3_finalize (stmt); + GNUNET_SQ_cleanup_result (rs); break; } + GNUNET_SQ_cleanup_result (rs); } - sqlite3_finalize (stmt); + GNUNET_SQ_reset (plugin->dbh, + plugin->get_closest_stmt); return cnt; } @@ -703,6 +719,50 @@ libgnunet_plugin_datacache_sqlite_init (void *cls) plugin->env = env; plugin->dbh = dbh; plugin->fn = fn_utf8; + + if ( (SQLITE_OK != + sq_prepare (plugin->dbh, + "INSERT INTO ds090 (type, expire, key, value, path) " + "VALUES (?, ?, ?, ?, ?)", + &plugin->insert_stmt)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT count(*) FROM ds090 " + "WHERE key=? AND type=? AND expire >= ?", + &plugin->get_count_stmt)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT value,expire,path FROM ds090 " + "WHERE key=? AND type=? AND expire >= ? LIMIT 1 OFFSET ?", + &plugin->get_stmt)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT _ROWID_,key,value FROM ds090 ORDER BY expire ASC LIMIT 1", + &plugin->del_select_stmt)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "DELETE FROM ds090 WHERE _ROWID_=?", + &plugin->del_stmt)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT value,expire,path,key,type FROM ds090 " + "ORDER BY key LIMIT 1 OFFSET ?", + &plugin->get_random_stmt)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT value,expire,path,type,key FROM ds090 " + "WHERE key>=? AND expire >= ? ORDER BY KEY ASC LIMIT ?", + &plugin->get_closest_stmt)) + ) + { + LOG_SQLITE (plugin->dbh, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sq_prepare"); + (void) sqlite3_close (plugin->dbh); + GNUNET_free (plugin); + return NULL; + } + api = GNUNET_new (struct GNUNET_DATACACHE_PluginFunctions); api->cls = plugin; api->get = &sqlite_plugin_get; @@ -741,6 +801,13 @@ libgnunet_plugin_datacache_sqlite_done (void *cls) plugin->fn); GNUNET_free_non_null (plugin->fn); #endif + sqlite3_finalize (plugin->insert_stmt); + sqlite3_finalize (plugin->get_count_stmt); + sqlite3_finalize (plugin->get_stmt); + sqlite3_finalize (plugin->del_select_stmt); + sqlite3_finalize (plugin->del_stmt); + sqlite3_finalize (plugin->get_random_stmt); + sqlite3_finalize (plugin->get_closest_stmt); result = sqlite3_close (plugin->dbh); #if SQLITE_VERSION_NUMBER >= 3007000 if (SQLITE_BUSY == result) diff --git a/src/datastore/plugin_datastore_sqlite.c b/src/datastore/plugin_datastore_sqlite.c index 9c67d242eb..491f73ed5e 100644 --- a/src/datastore/plugin_datastore_sqlite.c +++ b/src/datastore/plugin_datastore_sqlite.c @@ -128,6 +128,46 @@ struct Plugin sqlite3_stmt *insertContent; /** + * Precompiled SQL for selection + */ + sqlite3_stmt *count_key; + + /** + * Precompiled SQL for selection + */ + sqlite3_stmt *count_key_vhash; + + /** + * Precompiled SQL for selection + */ + sqlite3_stmt *count_key_type; + + /** + * Precompiled SQL for selection + */ + sqlite3_stmt *count_key_vhash_type; + + /** + * Precompiled SQL for selection + */ + sqlite3_stmt *get_key; + + /** + * Precompiled SQL for selection + */ + sqlite3_stmt *get_key_vhash; + + /** + * Precompiled SQL for selection + */ + sqlite3_stmt *get_key_type; + + /** + * Precompiled SQL for selection + */ + sqlite3_stmt *get_key_vhash_type; + + /** * Should the database be dropped on shutdown? */ int drop_on_shutdown; @@ -151,11 +191,17 @@ sq_prepare (sqlite3 *dbh, char *dummy; int result; - result = - sqlite3_prepare_v2 (dbh, zSql, strlen (zSql), ppStmt, - (const char **) &dummy); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite", - "Prepared `%s' / %p: %d\n", zSql, *ppStmt, result); + result = sqlite3_prepare_v2 (dbh, + zSql, + strlen (zSql), + ppStmt, + (const char **) &dummy); + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "sqlite", + "Prepared `%s' / %p: %d\n", + zSql, + *ppStmt, + result); return result; } @@ -311,80 +357,134 @@ database_setup (const struct GNUNET_CONFIGURATION_Handle *cfg, * we do math or inequality tests, so we can't handle the entire range of uint32_t. * This will also cause problems for expiration times after 294247-01-10-04:00:54 UTC. */ - if ((sqlite3_step (stmt) == SQLITE_DONE) && - (sqlite3_exec - (plugin->dbh, - "CREATE TABLE gn090 (" " repl INT4 NOT NULL DEFAULT 0," - " type INT4 NOT NULL DEFAULT 0," " prio INT4 NOT NULL DEFAULT 0," - " anonLevel INT4 NOT NULL DEFAULT 0," - " expire INT8 NOT NULL DEFAULT 0," " rvalue INT8 NOT NULL," - " hash TEXT NOT NULL DEFAULT ''," " vhash TEXT NOT NULL DEFAULT ''," - " value BLOB NOT NULL DEFAULT '')", NULL, NULL, NULL) != SQLITE_OK)) + if ( (SQLITE_DONE == + sqlite3_step (stmt)) && + (SQLITE_OK != + sqlite3_exec (plugin->dbh, + "CREATE TABLE gn090 (" " repl INT4 NOT NULL DEFAULT 0," + " type INT4 NOT NULL DEFAULT 0," " prio INT4 NOT NULL DEFAULT 0," + " anonLevel INT4 NOT NULL DEFAULT 0," + " expire INT8 NOT NULL DEFAULT 0," " rvalue INT8 NOT NULL," + " hash TEXT NOT NULL DEFAULT ''," " vhash TEXT NOT NULL DEFAULT ''," + " value BLOB NOT NULL DEFAULT '')", + NULL, + NULL, + NULL)) ) { - LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite3_exec"); + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR, + "sqlite3_exec"); sqlite3_finalize (stmt); return GNUNET_SYSERR; } sqlite3_finalize (stmt); create_indices (plugin->dbh); - if ((sq_prepare - (plugin->dbh, - "UPDATE gn090 " - "SET prio = prio + ?, expire = MAX(expire,?) WHERE _ROWID_ = ?", - &plugin->updPrio) != SQLITE_OK) || - (sq_prepare - (plugin->dbh, - "UPDATE gn090 " "SET repl = MAX (0, repl - 1) WHERE _ROWID_ = ?", - &plugin->updRepl) != SQLITE_OK) || - (sq_prepare - (plugin->dbh, - "SELECT type,prio,anonLevel,expire,hash,value,_ROWID_ " "FROM gn090 " + if ( (SQLITE_OK != + sq_prepare (plugin->dbh, + "UPDATE gn090 " + "SET prio = prio + ?, expire = MAX(expire,?) WHERE _ROWID_ = ?", + &plugin->updPrio)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "UPDATE gn090 " "SET repl = MAX (0, repl - 1) WHERE _ROWID_ = ?", + &plugin->updRepl)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT type,prio,anonLevel,expire,hash,value,_ROWID_ " "FROM gn090 " #if SQLITE_VERSION_NUMBER >= 3007000 - "INDEXED BY idx_repl_rvalue " + "INDEXED BY idx_repl_rvalue " #endif - "WHERE repl=?2 AND " " (rvalue>=?1 OR " - " NOT EXISTS (SELECT 1 FROM gn090 " + "WHERE repl=?2 AND " " (rvalue>=?1 OR " + " NOT EXISTS (SELECT 1 FROM gn090 " #if SQLITE_VERSION_NUMBER >= 3007000 - "INDEXED BY idx_repl_rvalue " + "INDEXED BY idx_repl_rvalue " #endif - "WHERE repl=?2 AND rvalue>=?1 LIMIT 1) ) " - "ORDER BY rvalue ASC LIMIT 1", &plugin->selRepl) != SQLITE_OK) || - (sq_prepare (plugin->dbh, "SELECT MAX(repl) FROM gn090" + "WHERE repl=?2 AND rvalue>=?1 LIMIT 1) ) " + "ORDER BY rvalue ASC LIMIT 1", + &plugin->selRepl)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT MAX(repl) FROM gn090" #if SQLITE_VERSION_NUMBER >= 3007000 - " INDEXED BY idx_repl_rvalue" + " INDEXED BY idx_repl_rvalue" #endif - "", &plugin->maxRepl) != SQLITE_OK) || - (sq_prepare - (plugin->dbh, - "SELECT type,prio,anonLevel,expire,hash,value,_ROWID_ " "FROM gn090 " + "", + &plugin->maxRepl)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT type,prio,anonLevel,expire,hash,value,_ROWID_ " "FROM gn090 " #if SQLITE_VERSION_NUMBER >= 3007000 - "INDEXED BY idx_expire " + "INDEXED BY idx_expire " #endif - "WHERE NOT EXISTS (SELECT 1 FROM gn090 WHERE expire < ?1 LIMIT 1) OR (expire < ?1) " - "ORDER BY expire ASC LIMIT 1", &plugin->selExpi) != SQLITE_OK) || - (sq_prepare - (plugin->dbh, - "SELECT type,prio,anonLevel,expire,hash,value,_ROWID_ " "FROM gn090 " + "WHERE NOT EXISTS (SELECT 1 FROM gn090 WHERE expire < ?1 LIMIT 1) OR (expire < ?1) " + "ORDER BY expire ASC LIMIT 1", + &plugin->selExpi)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT type,prio,anonLevel,expire,hash,value,_ROWID_ " "FROM gn090 " #if SQLITE_VERSION_NUMBER >= 3007000 - "INDEXED BY idx_anon_type_hash " + "INDEXED BY idx_anon_type_hash " #endif - "WHERE (anonLevel = 0 AND type=?1) " - "ORDER BY hash DESC LIMIT 1 OFFSET ?2", - &plugin->selZeroAnon) != SQLITE_OK) || - (sq_prepare - (plugin->dbh, - "INSERT INTO gn090 (repl, type, prio, anonLevel, expire, rvalue, hash, vhash, value) " - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", - &plugin->insertContent) != SQLITE_OK) || - (sq_prepare - (plugin->dbh, "DELETE FROM gn090 WHERE _ROWID_ = ?", - &plugin->delRow) != SQLITE_OK)) + "WHERE (anonLevel = 0 AND type=?1) " + "ORDER BY hash DESC LIMIT 1 OFFSET ?2", + &plugin->selZeroAnon)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "INSERT INTO gn090 (repl, type, prio, anonLevel, expire, rvalue, hash, vhash, value) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", + &plugin->insertContent)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT count(*) FROM gn090 WHERE hash=?", + &plugin->count_key)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT count(*) FROM gn090 WHERE hash=? AND vhash=?", + &plugin->count_key_vhash)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT count(*) FROM gn090 WHERE hash=? AND type=?", + &plugin->count_key_type)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT count(*) FROM gn090 WHERE hash=? AND vhash=? AND type=?", + &plugin->count_key_vhash_type)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT type, prio, anonLevel, expire, hash, value, _ROWID_ FROM gn090 " + "WHERE hash=?" + "ORDER BY _ROWID_ ASC LIMIT 1 OFFSET ?", + &plugin->get_key)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT type, prio, anonLevel, expire, hash, value, _ROWID_ FROM gn090 " + "WHERE hash=? AND vhash=?" + "ORDER BY _ROWID_ ASC LIMIT 1 OFFSET ?", + &plugin->get_key_vhash)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT type, prio, anonLevel, expire, hash, value, _ROWID_ FROM gn090 " + "WHERE hash=? AND type=?" + "ORDER BY _ROWID_ ASC LIMIT 1 OFFSET ?", + &plugin->get_key_type)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT type, prio, anonLevel, expire, hash, value, _ROWID_ FROM gn090 " + "WHERE hash=? AND vhash=? AND type=?" + "ORDER BY _ROWID_ ASC LIMIT 1 OFFSET ?", + &plugin->get_key_vhash_type)) || + (SQLITE_OK != + sq_prepare (plugin->dbh, + "DELETE FROM gn090 WHERE _ROWID_ = ?", + &plugin->delRow)) + ) { - LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "precompiling"); + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR, + "precompiling"); return GNUNET_SYSERR; } - return GNUNET_OK; } @@ -399,51 +499,74 @@ static void database_shutdown (struct Plugin *plugin) { int result; - #if SQLITE_VERSION_NUMBER >= 3007000 sqlite3_stmt *stmt; #endif - if (plugin->delRow != NULL) + if (NULL != plugin->delRow) sqlite3_finalize (plugin->delRow); - if (plugin->updPrio != NULL) + if (NULL != plugin->updPrio) sqlite3_finalize (plugin->updPrio); - if (plugin->updRepl != NULL) + if (NULL != plugin->updRepl) sqlite3_finalize (plugin->updRepl); - if (plugin->selRepl != NULL) + if (NULL != plugin->selRepl) sqlite3_finalize (plugin->selRepl); - if (plugin->maxRepl != NULL) + if (NULL != plugin->maxRepl) sqlite3_finalize (plugin->maxRepl); - if (plugin->selExpi != NULL) + if (NULL != plugin->selExpi) sqlite3_finalize (plugin->selExpi); - if (plugin->selZeroAnon != NULL) + if (NULL != plugin->selZeroAnon) sqlite3_finalize (plugin->selZeroAnon); - if (plugin->insertContent != NULL) + if (NULL != plugin->insertContent) sqlite3_finalize (plugin->insertContent); + if (NULL != plugin->count_key) + sqlite3_finalize (plugin->count_key); + if (NULL != plugin->count_key_vhash) + sqlite3_finalize (plugin->count_key_vhash); + if (NULL != plugin->count_key_type) + sqlite3_finalize (plugin->count_key_type); + if (NULL != plugin->count_key_vhash_type) + sqlite3_finalize (plugin->count_key_vhash_type); + if (NULL != plugin->count_key) + sqlite3_finalize (plugin->get_key); + if (NULL != plugin->count_key_vhash) + sqlite3_finalize (plugin->get_key_vhash); + if (NULL != plugin->count_key_type) + sqlite3_finalize (plugin->get_key_type); + if (NULL != plugin->count_key_vhash_type) + sqlite3_finalize (plugin->get_key_vhash_type); result = sqlite3_close (plugin->dbh); #if SQLITE_VERSION_NUMBER >= 3007000 if (result == SQLITE_BUSY) { - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "sqlite", - _ - ("Tried to close sqlite without finalizing all prepared statements.\n")); - stmt = sqlite3_next_stmt (plugin->dbh, NULL); - while (stmt != NULL) + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, + "sqlite", + _("Tried to close sqlite without finalizing all prepared statements.\n")); + stmt = sqlite3_next_stmt (plugin->dbh, + NULL); + while (NULL != stmt) { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite", - "Closing statement %p\n", stmt); + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "sqlite", + "Closing statement %p\n", + stmt); result = sqlite3_finalize (stmt); if (result != SQLITE_OK) - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "sqlite", - "Failed to close statement %p: %d\n", stmt, result); - stmt = sqlite3_next_stmt (plugin->dbh, NULL); + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, + "sqlite", + "Failed to close statement %p: %d\n", + stmt, + result); + stmt = sqlite3_next_stmt (plugin->dbh, + NULL); } result = sqlite3_close (plugin->dbh); } #endif if (SQLITE_OK != result) - LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close"); - + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR, + "sqlite3_close"); GNUNET_free_non_null (plugin->fn); } @@ -472,15 +595,12 @@ delete_by_rowid (struct Plugin *plugin, { LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite3_step"); - if (SQLITE_OK != sqlite3_reset (plugin->delRow)) - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_reset"); + GNUNET_SQ_reset (plugin->dbh, + plugin->delRow); return GNUNET_SYSERR; } - if (SQLITE_OK != sqlite3_reset (plugin->delRow)) - LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_reset"); + GNUNET_SQ_reset (plugin->dbh, + plugin->delRow); return GNUNET_OK; } @@ -515,9 +635,10 @@ sqlite_plugin_put (void *cls, { uint64_t rvalue; struct GNUNET_HashCode vhash; + uint32_t type32 = (uint32_t) type; struct GNUNET_SQ_QueryParam params[] = { GNUNET_SQ_query_param_uint32 (&replication), - GNUNET_SQ_query_param_uint32 (&type), + GNUNET_SQ_query_param_uint32 (&type32), GNUNET_SQ_query_param_uint32 (&priority), GNUNET_SQ_query_param_uint32 (&anonymity), GNUNET_SQ_query_param_absolute_time (&expiration), @@ -578,19 +699,16 @@ sqlite_plugin_put (void *cls, default: LOG_SQLITE_MSG (plugin, &msg, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite3_step"); - if (SQLITE_OK != sqlite3_reset (stmt)) - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_reset"); + GNUNET_SQ_reset (plugin->dbh, + stmt); database_shutdown (plugin); database_setup (plugin->env->cfg, plugin); cont (cont_cls, key, size, GNUNET_SYSERR, msg); GNUNET_free_non_null(msg); return; } - if (SQLITE_OK != sqlite3_reset (stmt)) - LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_reset"); + GNUNET_SQ_reset (plugin->dbh, + stmt); cont (cont_cls, key, size, ret, msg); GNUNET_free_non_null(msg); } @@ -644,9 +762,8 @@ sqlite_plugin_update (void *cls, return; } n = sqlite3_step (plugin->updPrio); - if (SQLITE_OK != sqlite3_reset (plugin->updPrio)) - LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_reset"); + GNUNET_SQ_reset (plugin->dbh, + plugin->updPrio); switch (n) { case SQLITE_DONE: @@ -687,75 +804,84 @@ execute_get (struct Plugin *plugin, { int n; struct GNUNET_TIME_Absolute expiration; - unsigned long long rowid; - unsigned int size; + uint32_t type; + uint32_t priority; + uint32_t anonymity; + uint64_t rowid; + void *value; + size_t value_size; + struct GNUNET_HashCode key; int ret; + struct GNUNET_SQ_ResultSpec rs[] = { + GNUNET_SQ_result_spec_uint32 (&type), + GNUNET_SQ_result_spec_uint32 (&priority), + GNUNET_SQ_result_spec_uint32 (&anonymity), + GNUNET_SQ_result_spec_absolute_time (&expiration), + GNUNET_SQ_result_spec_auto_from_type (&key), + GNUNET_SQ_result_spec_variable_size (&value, + &value_size), + GNUNET_SQ_result_spec_uint64 (&rowid), + GNUNET_SQ_result_spec_end + }; n = sqlite3_step (stmt); switch (n) { case SQLITE_ROW: - size = sqlite3_column_bytes (stmt, 5); - rowid = sqlite3_column_int64 (stmt, 6); - if (sqlite3_column_bytes (stmt, 4) != sizeof (struct GNUNET_HashCode)) + if (GNUNET_OK != + GNUNET_SQ_extract_result (stmt, + rs)) { - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "sqlite", - _("Invalid data in database. Trying to fix (by deletion).\n")); - if (SQLITE_OK != sqlite3_reset (stmt)) - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_reset"); - if ( (GNUNET_OK == delete_by_rowid (plugin, rowid)) && - (NULL != plugin->env->duc) ) - plugin->env->duc (plugin->env->cls, - -(size + GNUNET_DATASTORE_ENTRY_OVERHEAD)); + GNUNET_break (0); break; } - expiration.abs_value_us = sqlite3_column_int64 (stmt, 3); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite", + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "sqlite", "Found reply in database with expiration %s\n", GNUNET_STRINGS_absolute_time_to_string (expiration)); - ret = proc (proc_cls, sqlite3_column_blob (stmt, 4) /* key */ , - size, sqlite3_column_blob (stmt, 5) /* data */ , - sqlite3_column_int (stmt, 0) /* type */ , - sqlite3_column_int (stmt, 1) /* priority */ , - sqlite3_column_int (stmt, 2) /* anonymity */ , - expiration, rowid); - if (SQLITE_OK != sqlite3_reset (stmt)) - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_reset"); + ret = proc (proc_cls, + &key, + value_size, + value, + type, + priority, + anonymity, + expiration, + rowid); + GNUNET_SQ_cleanup_result (rs); + GNUNET_SQ_reset (plugin->dbh, + stmt); if ( (GNUNET_NO == ret) && - (GNUNET_OK == delete_by_rowid (plugin, rowid)) && + (GNUNET_OK == delete_by_rowid (plugin, + rowid)) && (NULL != plugin->env->duc) ) plugin->env->duc (plugin->env->cls, - -(size + GNUNET_DATASTORE_ENTRY_OVERHEAD)); + -(value_size + GNUNET_DATASTORE_ENTRY_OVERHEAD)); return; case SQLITE_DONE: /* database must be empty */ - if (SQLITE_OK != sqlite3_reset (stmt)) - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_reset"); break; case SQLITE_BUSY: case SQLITE_ERROR: case SQLITE_MISUSE: default: - LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite3_step"); - if (SQLITE_OK != sqlite3_reset (stmt)) + if (SQLITE_OK != + sqlite3_reset (stmt)) LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite3_reset"); GNUNET_break (0); + proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); database_shutdown (plugin); - database_setup (plugin->env->cfg, plugin); - break; + database_setup (plugin->env->cfg, + plugin); + return; } - if (SQLITE_OK != sqlite3_reset (stmt)) - LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_reset"); + GNUNET_SQ_reset (plugin->dbh, + stmt); proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); } @@ -774,7 +900,8 @@ execute_get (struct Plugin *plugin, * @param proc_cls closure for @a proc */ static void -sqlite_plugin_get_zero_anonymity (void *cls, uint64_t offset, +sqlite_plugin_get_zero_anonymity (void *cls, + uint64_t offset, enum GNUNET_BLOCK_Type type, PluginDatumProcessor proc, void *proc_cls) @@ -826,97 +953,133 @@ sqlite_plugin_get_key (void *cls, void *proc_cls) { struct Plugin *plugin = cls; + uint32_t type32 = (uint32_t) type; int ret; int total; - int limit_off; - unsigned int sqoff; - sqlite3_stmt *stmt; - char scratch[256]; - - GNUNET_assert (proc != NULL); - GNUNET_assert (key != NULL); - GNUNET_snprintf (scratch, sizeof (scratch), - "SELECT count(*) FROM gn090 WHERE hash=?%s%s", - vhash == NULL ? "" : " AND vhash=?", - type == 0 ? "" : " AND type=?"); - if (sq_prepare (plugin->dbh, scratch, &stmt) != SQLITE_OK) + uint32_t limit_off; + struct GNUNET_SQ_QueryParam count_params_key[] = { + GNUNET_SQ_query_param_auto_from_type (key), + GNUNET_SQ_query_param_end + }; + struct GNUNET_SQ_QueryParam count_params_key_vhash[] = { + GNUNET_SQ_query_param_auto_from_type (key), + GNUNET_SQ_query_param_auto_from_type (vhash), + GNUNET_SQ_query_param_end + }; + struct GNUNET_SQ_QueryParam count_params_key_type[] = { + GNUNET_SQ_query_param_auto_from_type (key), + GNUNET_SQ_query_param_uint32 (&type32), + GNUNET_SQ_query_param_end + }; + struct GNUNET_SQ_QueryParam count_params_key_vhash_type[] = { + GNUNET_SQ_query_param_auto_from_type (key), + GNUNET_SQ_query_param_auto_from_type (vhash), + GNUNET_SQ_query_param_uint32 (&type32), + GNUNET_SQ_query_param_end + }; + struct GNUNET_SQ_QueryParam get_params_key[] = { + GNUNET_SQ_query_param_auto_from_type (key), + GNUNET_SQ_query_param_uint32 (&limit_off), + GNUNET_SQ_query_param_end + }; + struct GNUNET_SQ_QueryParam get_params_key_vhash[] = { + GNUNET_SQ_query_param_auto_from_type (key), + GNUNET_SQ_query_param_auto_from_type (vhash), + GNUNET_SQ_query_param_uint32 (&limit_off), + GNUNET_SQ_query_param_end + }; + struct GNUNET_SQ_QueryParam get_params_key_type[] = { + GNUNET_SQ_query_param_auto_from_type (key), + GNUNET_SQ_query_param_uint32 (&type32), + GNUNET_SQ_query_param_uint32 (&limit_off), + GNUNET_SQ_query_param_end + }; + struct GNUNET_SQ_QueryParam get_params_key_vhash_type[] = { + GNUNET_SQ_query_param_auto_from_type (key), + GNUNET_SQ_query_param_auto_from_type (vhash), + GNUNET_SQ_query_param_uint32 (&type32), + GNUNET_SQ_query_param_uint32 (&limit_off), + GNUNET_SQ_query_param_end + }; + struct GNUNET_SQ_QueryParam *count_params; + sqlite3_stmt *count_stmt; + struct GNUNET_SQ_QueryParam *get_params; + sqlite3_stmt *get_stmt; + + if (NULL == vhash) { - LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite_prepare"); - proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); - return; + if (GNUNET_BLOCK_TYPE_ANY == type) + { + count_params = count_params_key; + count_stmt = plugin->count_key; + get_params = get_params_key; + get_stmt = plugin->get_key; + } + else + { + count_params = count_params_key_type; + count_stmt = plugin->count_key_type; + get_params = get_params_key_type; + get_stmt = plugin->get_key_type; + } } - sqoff = 1; - ret = - sqlite3_bind_blob (stmt, sqoff++, key, sizeof (struct GNUNET_HashCode), - SQLITE_TRANSIENT); - if ((vhash != NULL) && (ret == SQLITE_OK)) - ret = - sqlite3_bind_blob (stmt, sqoff++, vhash, sizeof (struct GNUNET_HashCode), - SQLITE_TRANSIENT); - if ((type != 0) && (ret == SQLITE_OK)) - ret = sqlite3_bind_int (stmt, sqoff++, type); - if (SQLITE_OK != ret) + else + { + if (GNUNET_BLOCK_TYPE_ANY == type) + { + count_params = count_params_key_vhash; + count_stmt = plugin->count_key_vhash; + get_params = get_params_key_vhash; + get_stmt = plugin->get_key_vhash; + } + else + { + count_params = count_params_key_vhash_type; + count_stmt = plugin->count_key_vhash_type; + get_params = get_params_key_vhash_type; + get_stmt = plugin->get_key_vhash_type; + } + } + if (GNUNET_OK != + GNUNET_SQ_bind (count_stmt, + count_params)) { - LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite_bind"); - sqlite3_finalize (stmt); proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); return; } - ret = sqlite3_step (stmt); + ret = sqlite3_step (count_stmt); if (ret != SQLITE_ROW) { LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite_step"); - sqlite3_finalize (stmt); + GNUNET_SQ_reset (plugin->dbh, + count_stmt); proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); return; } - total = sqlite3_column_int (stmt, 0); - sqlite3_finalize (stmt); + total = sqlite3_column_int (count_stmt, + 0); + GNUNET_SQ_reset (plugin->dbh, + count_stmt); if (0 == total) { proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); return; } - limit_off = (int) (offset % total); - if (limit_off < 0) - limit_off += total; - GNUNET_snprintf (scratch, sizeof (scratch), - "SELECT type, prio, anonLevel, expire, hash, value, _ROWID_ " - "FROM gn090 WHERE hash=?%s%s " - "ORDER BY _ROWID_ ASC LIMIT 1 OFFSET ?", - vhash == NULL ? "" : " AND vhash=?", - type == 0 ? "" : " AND type=?"); - if (sq_prepare (plugin->dbh, scratch, &stmt) != SQLITE_OK) - { - LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite_prepare"); - proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); - return; - } - sqoff = 1; - ret = sqlite3_bind_blob (stmt, sqoff++, key, - sizeof (struct GNUNET_HashCode), - SQLITE_TRANSIENT); - if ((vhash != NULL) && (ret == SQLITE_OK)) - ret = sqlite3_bind_blob (stmt, sqoff++, vhash, - sizeof (struct GNUNET_HashCode), - SQLITE_TRANSIENT); - if ((type != 0) && (ret == SQLITE_OK)) - ret = sqlite3_bind_int (stmt, sqoff++, type); - if (ret == SQLITE_OK) - ret = sqlite3_bind_int64 (stmt, sqoff++, limit_off); - if (ret != SQLITE_OK) + limit_off = (uint32_t) (offset % total); + if (GNUNET_OK != + GNUNET_SQ_bind (get_stmt, + get_params)) { - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite_bind"); proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); return; } - execute_get (plugin, stmt, proc, proc_cls); - sqlite3_finalize (stmt); + execute_get (plugin, + get_stmt, + proc, + proc_cls); + GNUNET_SQ_reset (plugin->dbh, + get_stmt); } @@ -980,13 +1143,17 @@ repl_proc (void *cls, struct ReplCtx *rc = cls; int ret; + if (GNUNET_SYSERR == rc->have_uid) + rc->have_uid = GNUNET_NO; ret = rc->proc (rc->proc_cls, key, - size, data, + size, + data, type, priority, anonymity, - expiration, uid); + expiration, + uid); if (NULL != key) { rc->uid = uid; @@ -1007,7 +1174,8 @@ repl_proc (void *cls, * @param proc_cls closure for @a proc */ static void -sqlite_plugin_get_replication (void *cls, PluginDatumProcessor proc, +sqlite_plugin_get_replication (void *cls, + PluginDatumProcessor proc, void *proc_cls) { struct Plugin *plugin = cls; @@ -1027,24 +1195,21 @@ sqlite_plugin_get_replication (void *cls, PluginDatumProcessor proc, GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "datastore-sqlite", "Getting random block based on replication order.\n"); - rc.have_uid = GNUNET_NO; - rc.proc = proc; - rc.proc_cls = proc_cls; - if (SQLITE_ROW != sqlite3_step (plugin->maxRepl)) + if (SQLITE_ROW != + sqlite3_step (plugin->maxRepl)) { - if (SQLITE_OK != sqlite3_reset (plugin->maxRepl)) - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_reset"); + GNUNET_SQ_reset (plugin->dbh, + plugin->maxRepl); /* DB empty */ proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); return; } - repl = sqlite3_column_int (plugin->maxRepl, 0); - if (SQLITE_OK != sqlite3_reset (plugin->maxRepl)) - LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_reset"); - rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); + repl = sqlite3_column_int (plugin->maxRepl, + 0); + GNUNET_SQ_reset (plugin->dbh, + plugin->maxRepl); + rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + UINT64_MAX); if (GNUNET_OK != GNUNET_SQ_bind (plugin->selRepl, params_sel_repl)) @@ -1052,7 +1217,13 @@ sqlite_plugin_get_replication (void *cls, PluginDatumProcessor proc, proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); return; } - execute_get (plugin, plugin->selRepl, &repl_proc, &rc); + rc.have_uid = GNUNET_SYSERR; + rc.proc = proc; + rc.proc_cls = proc_cls; + execute_get (plugin, + plugin->selRepl, + &repl_proc, + &rc); if (GNUNET_YES == rc.have_uid) { if (GNUNET_OK != @@ -1062,14 +1233,18 @@ sqlite_plugin_get_replication (void *cls, PluginDatumProcessor proc, proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); return; } - if (SQLITE_DONE != sqlite3_step (plugin->updRepl)) + if (SQLITE_DONE != + sqlite3_step (plugin->updRepl)) LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite3_step"); - if (SQLITE_OK != sqlite3_reset (plugin->updRepl)) - LOG_SQLITE (plugin, - GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "sqlite3_reset"); + GNUNET_SQ_reset (plugin->dbh, + plugin->updRepl); + } + if (GNUNET_SYSERR == rc.have_uid) + { + /* proc was not called at all so far, do it now. */ + proc (proc_cls, NULL, 0, NULL, 0, 0, 0, GNUNET_TIME_UNIT_ZERO_ABS, 0); } } @@ -1094,7 +1269,8 @@ sqlite_plugin_get_expiration (void *cls, PluginDatumProcessor proc, GNUNET_SQ_query_param_end }; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite", + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "sqlite", "Getting random block based on expiration and priority order.\n"); now = GNUNET_TIME_absolute_get (); stmt = plugin->selExpi; @@ -1131,11 +1307,17 @@ sqlite_plugin_get_keys (void *cls, int ret; GNUNET_assert (NULL != proc); - if (sq_prepare (plugin->dbh, "SELECT hash FROM gn090", &stmt) != SQLITE_OK) + if (SQLITE_OK != + sq_prepare (plugin->dbh, + "SELECT hash FROM gn090", + &stmt)) { - LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite_prepare"); - proc (proc_cls, NULL, 0); + proc (proc_cls, + NULL, + 0); return; } while (SQLITE_ROW == (ret = sqlite3_step (stmt))) @@ -1143,14 +1325,20 @@ sqlite_plugin_get_keys (void *cls, if (GNUNET_OK == GNUNET_SQ_extract_result (stmt, results)) - proc (proc_cls, &key, 1); + proc (proc_cls, + &key, + 1); else GNUNET_break (0); } if (SQLITE_DONE != ret) - LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite_step"); + LOG_SQLITE (plugin, + GNUNET_ERROR_TYPE_ERROR, + "sqlite_step"); sqlite3_finalize (stmt); - proc (proc_cls, NULL, 0); + proc (proc_cls, + NULL, + 0); } @@ -1176,7 +1364,8 @@ sqlite_plugin_drop (void *cls) * @return the size of the database on disk (estimate) */ static void -sqlite_plugin_estimate_size (void *cls, unsigned long long *estimate) +sqlite_plugin_estimate_size (void *cls, + unsigned long long *estimate) { struct Plugin *plugin = cls; sqlite3_stmt *stmt; @@ -1191,29 +1380,45 @@ sqlite_plugin_estimate_size (void *cls, unsigned long long *estimate) return; if (SQLITE_VERSION_NUMBER < 3006000) { - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "datastore-sqlite", - _ - ("sqlite version to old to determine size, assuming zero\n")); + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, + "datastore-sqlite", + _("sqlite version to old to determine size, assuming zero\n")); *estimate = 0; return; } - CHECK (SQLITE_OK == sqlite3_exec (plugin->dbh, "VACUUM", NULL, NULL, ENULL)); CHECK (SQLITE_OK == - sqlite3_exec (plugin->dbh, "PRAGMA auto_vacuum=INCREMENTAL", NULL, + sqlite3_exec (plugin->dbh, + "VACUUM", + NULL, + NULL, + ENULL)); + CHECK (SQLITE_OK == + sqlite3_exec (plugin->dbh, + "PRAGMA auto_vacuum=INCREMENTAL", + NULL, NULL, ENULL)); - CHECK (SQLITE_OK == sq_prepare (plugin->dbh, "PRAGMA page_count", &stmt)); + CHECK (SQLITE_OK == + sq_prepare (plugin->dbh, + "PRAGMA page_count", + &stmt)); if (SQLITE_ROW == sqlite3_step (stmt)) - pages = sqlite3_column_int64 (stmt, 0); + pages = sqlite3_column_int64 (stmt, + 0); else pages = 0; sqlite3_finalize (stmt); - CHECK (SQLITE_OK == sq_prepare (plugin->dbh, "PRAGMA page_size", &stmt)); - CHECK (SQLITE_ROW == sqlite3_step (stmt)); + CHECK (SQLITE_OK == + sq_prepare (plugin->dbh, + "PRAGMA page_size", + &stmt)); + CHECK (SQLITE_ROW == + sqlite3_step (stmt)); page_size = sqlite3_column_int64 (stmt, 0); sqlite3_finalize (stmt); GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Using sqlite page utilization to estimate payload (%llu pages of size %llu bytes)\n"), - (unsigned long long) pages, (unsigned long long) page_size); + (unsigned long long) pages, + (unsigned long long) page_size); *estimate = pages * page_size; } @@ -1231,9 +1436,11 @@ libgnunet_plugin_datastore_sqlite_init (void *cls) struct GNUNET_DATASTORE_PluginEnvironment *env = cls; struct GNUNET_DATASTORE_PluginFunctions *api; - if (plugin.env != NULL) + if (NULL != plugin.env) return NULL; /* can only initialize once! */ - memset (&plugin, 0, sizeof (struct Plugin)); + memset (&plugin, + 0, + sizeof (struct Plugin)); plugin.env = env; if (GNUNET_OK != database_setup (env->cfg, &plugin)) { @@ -1251,7 +1458,8 @@ libgnunet_plugin_datastore_sqlite_init (void *cls) api->get_zero_anonymity = &sqlite_plugin_get_zero_anonymity; api->get_keys = &sqlite_plugin_get_keys; api->drop = &sqlite_plugin_drop; - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "sqlite", + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, + "sqlite", _("Sqlite database running\n")); return api; } @@ -1270,13 +1478,12 @@ libgnunet_plugin_datastore_sqlite_done (void *cls) struct GNUNET_DATASTORE_PluginFunctions *api = cls; struct Plugin *plugin = api->cls; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite", + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, + "sqlite", "sqlite plugin is done\n"); fn = NULL; if (plugin->drop_on_shutdown) fn = GNUNET_strdup (plugin->fn); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "sqlite", - "Shutting down database\n"); database_shutdown (plugin); plugin->env = NULL; GNUNET_free (api); @@ -1288,9 +1495,6 @@ libgnunet_plugin_datastore_sqlite_done (void *cls) fn); GNUNET_free (fn); } - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - "sqlite", - "sqlite plugin is finished\n"); return NULL; } diff --git a/src/include/gnunet_sq_lib.h b/src/include/gnunet_sq_lib.h index 4d2510ee56..c196d7767a 100644 --- a/src/include/gnunet_sq_lib.h +++ b/src/include/gnunet_sq_lib.h @@ -194,6 +194,17 @@ GNUNET_SQ_bind (sqlite3_stmt *stmt, /** + * Reset @a stmt and log error. + * + * @param dbh database handle + * @param stmt statement to reset + */ +void +GNUNET_SQ_reset (sqlite3 *dbh, + sqlite3_stmt *stmt); + + +/** * Extract data from a Postgres database @a result at row @a row. * * @param cls closure diff --git a/src/revocation/test_revocation.c b/src/revocation/test_revocation.c index d3bbb879a8..8d55936941 100644 --- a/src/revocation/test_revocation.c +++ b/src/revocation/test_revocation.c @@ -104,8 +104,8 @@ revocation_remote_cb (void *cls, if (GNUNET_NO == is_valid) { - fprintf (stderr, - "Local revocation successful\n"); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Local revocation successful\n"); ok = 0; GNUNET_SCHEDULER_shutdown (); return; @@ -118,8 +118,8 @@ revocation_remote_cb (void *cls, NULL); return; } - fprintf (stderr, - "Flooding of revocation failed\n"); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Flooding of revocation failed\n"); ok = 2; GNUNET_SCHEDULER_shutdown (); } @@ -141,8 +141,8 @@ revocation_cb (void *cls, testpeers[1].revok_handle = NULL; if (GNUNET_NO == is_valid) { - fprintf (stderr, - "Revocation successful\n"); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Revocation successful\n"); check_revocation (NULL); } } @@ -386,8 +386,8 @@ test_connection (void *cls, /* We are generating a CLIQUE */ if (NUM_TEST_PEERS * (NUM_TEST_PEERS -1) == links_succeeded) { - fprintf (stderr, - "Testbed connected peers, initializing test\n"); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Testbed connected peers, initializing test\n"); for (c = 0; c < num_peers; c++) { testpeers[c].p = peers[c]; @@ -403,8 +403,8 @@ test_connection (void *cls, } else { - fprintf (stderr, - "Testbed failed to connect peers\n"); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Testbed failed to connect peers\n"); ok = 5; GNUNET_SCHEDULER_shutdown (); return; diff --git a/src/set/Makefile.am b/src/set/Makefile.am index cfe95bc1a6..03c258352c 100644 --- a/src/set/Makefile.am +++ b/src/set/Makefile.am @@ -51,7 +51,7 @@ gnunet_set_ibf_profiler_LDADD = \ gnunet_service_set_SOURCES = \ gnunet-service-set.c gnunet-service-set.h \ - gnunet-service-set_union.c \ + gnunet-service-set_union.c gnunet-service-set_union.h \ gnunet-service-set_intersection.c \ ibf.c ibf.h \ gnunet-service-set_union_strata_estimator.c gnunet-service-set_union_strata_estimator.h \ diff --git a/src/set/gnunet-service-set.c b/src/set/gnunet-service-set.c index 454ad97843..8f1506c6ab 100644 --- a/src/set/gnunet-service-set.c +++ b/src/set/gnunet-service-set.c @@ -24,6 +24,8 @@ * @author Christian Grothoff */ #include "gnunet-service-set.h" +#include "gnunet-service-set_union.h" +#include "gnunet-service-set_intersection.h" #include "gnunet-service-set_protocol.h" #include "gnunet_statistics_service.h" @@ -476,6 +478,7 @@ _GSS_operation_destroy (struct Operation *op, op->channel = NULL; GNUNET_CADET_channel_destroy (channel); } + if (GNUNET_YES == gc) collect_generation_garbage (set); /* We rely on the channel end handler to free 'op'. When 'op->channel' was NULL, @@ -682,7 +685,7 @@ client_disconnect_cb (void *cls, { struct Operation *curr = op; op = op->next; - if ( (GNUNET_YES == curr->is_incoming) && + if ( (GNUNET_YES == curr->is_incoming) && (curr->listener == listener) ) incoming_destroy (curr); } @@ -733,6 +736,38 @@ incoming_suggest (struct Operation *incoming, /** + * Check a request for a set operation from another peer. + * + * @param cls the operation state + * @param msg the received message + * @return #GNUNET_OK if the channel should be kept alive, + * #GNUNET_SYSERR to destroy the channel + */ +static int +check_incoming_msg (void *cls, + const struct OperationRequestMessage *msg) +{ + struct Operation *op = cls; + const struct GNUNET_MessageHeader *nested_context; + + /* double operation request */ + if (NULL != op->spec) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + nested_context = GNUNET_MQ_extract_nested_mh (msg); + if ( (NULL != nested_context) && + (ntohs (nested_context->size) > GNUNET_SET_CONTEXT_MESSAGE_MAX_SIZE) ) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** * Handle a request for a set operation from another peer. Checks if we * have a listener waiting for such a request (and in that case initiates * asking the listener about accepting the connection). If no listener @@ -744,42 +779,23 @@ incoming_suggest (struct Operation *incoming, * our virtual table and subsequent msgs would be routed differently (as * we then know what type of operation this is). * - * @param op the operation state - * @param mh the received message + * @param cls the operation state + * @param msg the received message * @return #GNUNET_OK if the channel should be kept alive, * #GNUNET_SYSERR to destroy the channel */ -static int -handle_incoming_msg (struct Operation *op, - const struct GNUNET_MessageHeader *mh) +static void +handle_incoming_msg (void *cls, + const struct OperationRequestMessage *msg) { - const struct OperationRequestMessage *msg; + struct Operation *op = cls; struct Listener *listener = op->listener; struct OperationSpecification *spec; const struct GNUNET_MessageHeader *nested_context; - msg = (const struct OperationRequestMessage *) mh; GNUNET_assert (GNUNET_YES == op->is_incoming); - if (GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST != ntohs (mh->type)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - /* double operation request */ - if (NULL != op->spec) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } spec = GNUNET_new (struct OperationSpecification); nested_context = GNUNET_MQ_extract_nested_mh (msg); - if ( (NULL != nested_context) && - (ntohs (nested_context->size) > GNUNET_SET_CONTEXT_MESSAGE_MAX_SIZE) ) - { - GNUNET_break_op (0); - GNUNET_free (spec); - return GNUNET_SYSERR; - } /* Make a copy of the nested_context (application-specific context information that is opaque to set) so we can pass it to the listener later on */ @@ -792,7 +808,6 @@ handle_incoming_msg (struct Operation *op, spec->peer = op->peer; spec->remote_element_count = ntohl (msg->element_count); op->spec = spec; - listener = op->listener; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received P2P operation request (op %u, port %s) for active listener\n", @@ -800,7 +815,6 @@ handle_incoming_msg (struct Operation *op, GNUNET_h2s (&listener->app_id)); incoming_suggest (op, listener); - return GNUNET_OK; } @@ -1103,9 +1117,11 @@ handle_client_create_set (void *cls, { case GNUNET_SET_OPERATION_INTERSECTION: set->vt = _GSS_intersection_vt (); + set->type = OT_INTERSECTION; break; case GNUNET_SET_OPERATION_UNION: set->vt = _GSS_union_vt (); + set->type = OT_UNION; break; default: GNUNET_free (set); @@ -1196,7 +1212,6 @@ channel_new_cb (void *cls, const struct GNUNET_PeerIdentity *source) { static const struct SetVT incoming_vt = { - .msg_handler = &handle_incoming_msg, .peer_disconnect = &handle_incoming_disconnect }; struct Listener *listener = cls; @@ -1290,60 +1305,6 @@ channel_window_cb (void *cls, /* FIXME: not implemented, we could do flow control here... */ } -/** - * FIXME: hack-job. Migrate to proper handler array use! - * - * @param cls local state associated with the channel. - * @param message The actual message. - */ -static int -check_p2p_message (void *cls, - const struct GNUNET_MessageHeader *message) -{ - return GNUNET_OK; -} - - -/** - * FIXME: hack-job. Migrate to proper handler array use! - * - * Functions with this signature are called whenever a message is - * received via a cadet channel. - * - * The msg_handler is a virtual table set in initially either when a peer - * creates a new channel with us, or once we create a new channel - * ourselves (evaluate). - * - * Once we know the exact type of operation (union/intersection), the vt is - * replaced with an operation specific instance (_GSS_[op]_vt). - * - * @param cls local state associated with the channel. - * @param message The actual message. - */ -static void -handle_p2p_message (void *cls, - const struct GNUNET_MessageHeader *message) -{ - struct Operation *op = cls; - int ret; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Dispatching cadet message (type: %u)\n", - ntohs (message->type)); - /* do this before the handler, as the handler might kill the channel */ - GNUNET_CADET_receive_done (op->channel); - if (NULL != op->vt) - ret = op->vt->msg_handler (op, - message); - else - ret = GNUNET_SYSERR; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Handled cadet message (type: %u)\n", - ntohs (message->type)); - if (GNUNET_OK != ret) - GNUNET_CADET_channel_destroy (op->channel); -} - /** * Called when a client wants to create a new listener. @@ -1357,66 +1318,66 @@ handle_client_listen (void *cls, { struct GNUNET_SERVICE_Client *client = cls; struct GNUNET_MQ_MessageHandler cadet_handlers[] = { - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_var_size (incoming_msg, GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST, - struct GNUNET_MessageHeader, + struct OperationRequestMessage, NULL), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_var_size (union_p2p_ibf, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF, - struct GNUNET_MessageHeader, + struct IBFMessage, NULL), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_var_size (union_p2p_elements, GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS, - struct GNUNET_MessageHeader, + struct GNUNET_SET_ElementMessage, NULL), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_var_size (union_p2p_offer, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER, struct GNUNET_MessageHeader, NULL), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_var_size (union_p2p_inquiry, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY, - struct GNUNET_MessageHeader, + struct InquiryMessage, NULL), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_var_size (union_p2p_demand, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND, struct GNUNET_MessageHeader, NULL), - GNUNET_MQ_hd_var_size (p2p_message, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE, - struct GNUNET_MessageHeader, - NULL), - GNUNET_MQ_hd_var_size (p2p_message, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE, - struct GNUNET_MessageHeader, - NULL), - GNUNET_MQ_hd_var_size (p2p_message, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_REQUEST_FULL, - struct GNUNET_MessageHeader, - NULL), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_fixed_size (union_p2p_done, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_hd_fixed_size (union_p2p_full_done, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_hd_fixed_size (union_p2p_request_full, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_REQUEST_FULL, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_hd_var_size (union_p2p_strata_estimator, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE, - struct GNUNET_MessageHeader, + struct StrataEstimatorMessage, NULL), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_var_size (union_p2p_strata_estimator, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC, - struct GNUNET_MessageHeader, + struct StrataEstimatorMessage, NULL), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_var_size (union_p2p_full_element, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT, - struct GNUNET_MessageHeader, + struct GNUNET_SET_ElementMessage, NULL), - GNUNET_MQ_hd_var_size (p2p_message, - GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO, - struct GNUNET_MessageHeader, - NULL), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_fixed_size (intersection_p2p_element_info, + GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO, + struct IntersectionElementInfoMessage, + NULL), + GNUNET_MQ_hd_var_size (intersection_p2p_bf, GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF, - struct GNUNET_MessageHeader, - NULL), - GNUNET_MQ_hd_var_size (p2p_message, - GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE, - struct GNUNET_MessageHeader, + struct BFMessage, NULL), + GNUNET_MQ_hd_fixed_size (intersection_p2p_done, + GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE, + struct IntersectionDoneMessage, + NULL), GNUNET_MQ_handler_end () }; struct Listener *listener; @@ -1623,66 +1584,66 @@ handle_client_evaluate (void *cls, struct GNUNET_SERVICE_Client *client = cls; struct Operation *op = GNUNET_new (struct Operation); const struct GNUNET_MQ_MessageHandler cadet_handlers[] = { - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_var_size (incoming_msg, GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST, - struct GNUNET_MessageHeader, + struct OperationRequestMessage, op), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_var_size (union_p2p_ibf, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF, - struct GNUNET_MessageHeader, + struct IBFMessage, op), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_var_size (union_p2p_elements, GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS, - struct GNUNET_MessageHeader, + struct GNUNET_SET_ElementMessage, op), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_var_size (union_p2p_offer, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER, struct GNUNET_MessageHeader, op), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_var_size (union_p2p_inquiry, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY, - struct GNUNET_MessageHeader, + struct InquiryMessage, op), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_var_size (union_p2p_demand, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND, struct GNUNET_MessageHeader, op), - GNUNET_MQ_hd_var_size (p2p_message, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE, - struct GNUNET_MessageHeader, - op), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_fixed_size (union_p2p_done, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE, + struct GNUNET_MessageHeader, + op), + GNUNET_MQ_hd_fixed_size (union_p2p_full_done, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE, + struct GNUNET_MessageHeader, + op), + GNUNET_MQ_hd_fixed_size (union_p2p_request_full, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_REQUEST_FULL, + struct GNUNET_MessageHeader, + op), + GNUNET_MQ_hd_var_size (union_p2p_strata_estimator, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE, - struct GNUNET_MessageHeader, + struct StrataEstimatorMessage, op), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_var_size (union_p2p_strata_estimator, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC, - struct GNUNET_MessageHeader, - op), - GNUNET_MQ_hd_var_size (p2p_message, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE, - struct GNUNET_MessageHeader, - op), - GNUNET_MQ_hd_var_size (p2p_message, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_REQUEST_FULL, - struct GNUNET_MessageHeader, + struct StrataEstimatorMessage, op), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_var_size (union_p2p_full_element, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT, - struct GNUNET_MessageHeader, - op), - GNUNET_MQ_hd_var_size (p2p_message, - GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO, - struct GNUNET_MessageHeader, + struct GNUNET_SET_ElementMessage, op), - GNUNET_MQ_hd_var_size (p2p_message, + GNUNET_MQ_hd_fixed_size (intersection_p2p_element_info, + GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO, + struct IntersectionElementInfoMessage, + op), + GNUNET_MQ_hd_var_size (intersection_p2p_bf, GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF, - struct GNUNET_MessageHeader, - op), - GNUNET_MQ_hd_var_size (p2p_message, - GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE, - struct GNUNET_MessageHeader, + struct BFMessage, op), + GNUNET_MQ_hd_fixed_size (intersection_p2p_done, + GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE, + struct IntersectionDoneMessage, + op), GNUNET_MQ_handler_end () }; struct Set *set; @@ -1717,7 +1678,7 @@ handle_client_evaluate (void *cls, // mutations won't interfer with the running operation. op->generation_created = set->current_generation; advance_generation (set); - + op->type = set->type; op->vt = set->vt; GNUNET_CONTAINER_DLL_insert (set->ops_head, set->ops_tail, @@ -1886,9 +1847,11 @@ handle_client_copy_lazy_connect (void *cls, { case GNUNET_SET_OPERATION_INTERSECTION: set->vt = _GSS_intersection_vt (); + set->type = OT_INTERSECTION; break; case GNUNET_SET_OPERATION_UNION: set->vt = _GSS_union_vt (); + set->type = OT_UNION; break; default: GNUNET_assert (0); @@ -2057,6 +2020,7 @@ handle_client_accept (void *cls, advance_generation (set); op->vt = set->vt; + op->type = set->type; op->vt->accept (op); GNUNET_SERVICE_client_continue (client); } diff --git a/src/set/gnunet-service-set.h b/src/set/gnunet-service-set.h index 68d8fe81f6..c981430ef0 100644 --- a/src/set/gnunet-service-set.h +++ b/src/set/gnunet-service-set.h @@ -213,20 +213,6 @@ typedef void /** - * Signature of functions that implement the message handling for - * the different set operations. - * - * @param op operation state - * @param msg received message - * @return #GNUNET_OK on success, #GNUNET_SYSERR to - * destroy the operation and the tunnel - */ -typedef int -(*MsgHandlerImpl) (struct Operation *op, - const struct GNUNET_MessageHeader *msg); - - -/** * Signature of functions that implement operation cancellation * * @param op operation state @@ -276,11 +262,6 @@ struct SetVT DestroySetImpl destroy_set; /** - * Callback for handling operation-specific messages. - */ - MsgHandlerImpl msg_handler; - - /** * Callback for handling the remote peer's disconnect. */ PeerDisconnectImpl peer_disconnect; @@ -364,6 +345,27 @@ struct Listener; /** + * Possible set operations. + */ +enum OperationType { + /** + * Operation type unknown. + */ + OT_UNKNOWN = 0, + + /** + * We are performing a union. + */ + OT_UNION, + + /** + * We are performing an intersection. + */ + OT_INTERSECTION +}; + + +/** * Operation context used to execute a set operation. */ struct Operation @@ -427,6 +429,11 @@ struct Operation struct GNUNET_SCHEDULER_Task *timeout_task; /** + * What type of operation is this? + */ + enum OperationType type; + + /** * Unique request id for the request from a remote peer, sent to the * client, which will accept or reject the request. Set to '0' iff * the request has not been suggested yet. @@ -582,6 +589,11 @@ struct Set struct Operation *ops_tail; /** + * What type of operation is this set for? + */ + enum OperationType type; + + /** * Current generation, that is, number of previously executed * operations and lazy copies on the underlying set content. */ diff --git a/src/set/gnunet-service-set_intersection.c b/src/set/gnunet-service-set_intersection.c index 9fe1eabe64..b298f7b41a 100644 --- a/src/set/gnunet-service-set_intersection.c +++ b/src/set/gnunet-service-set_intersection.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet - Copyright (C) 2013, 2014 GNUnet e.V. + Copyright (C) 2013-2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -28,6 +28,7 @@ #include "gnunet-service-set.h" #include "gnunet_block_lib.h" #include "gnunet-service-set_protocol.h" +#include "gnunet-service-set_intersection.h" #include <gcrypt.h> @@ -550,6 +551,8 @@ send_remaining_elements (void *cls) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending done and destroy because iterator ran out\n"); op->keep--; + GNUNET_CONTAINER_multihashmap_iterator_destroy (op->state->full_result_iter); + op->state->full_result_iter = NULL; send_client_done_and_destroy (op); return; } @@ -627,9 +630,6 @@ process_bf (struct Operation *op) case PHASE_COUNT_SENT: /* This is the first BF being sent, build our initial map with filtering in place */ - op->state->my_elements - = GNUNET_CONTAINER_multihashmap_create (op->spec->remote_element_count, - GNUNET_YES); op->state->my_element_count = 0; GNUNET_CONTAINER_multihashmap_iterate (op->spec->set->content->elements, &filtered_map_initialization, @@ -665,41 +665,53 @@ process_bf (struct Operation *op) /** + * Check an BF message from a remote peer. + * + * @param cls the intersection operation + * @param msg the header of the message + * @return #GNUNET_OK if @a msg is well-formed + */ +int +check_intersection_p2p_bf (void *cls, + const struct BFMessage *msg) +{ + struct Operation *op = cls; + + if (OT_INTERSECTION != op->type) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** * Handle an BF message from a remote peer. * * @param cls the intersection operation - * @param mh the header of the message + * @param msg the header of the message */ -static void -handle_p2p_bf (void *cls, - const struct GNUNET_MessageHeader *mh) +void +handle_intersection_p2p_bf (void *cls, + const struct BFMessage *msg) { struct Operation *op = cls; - const struct BFMessage *msg; uint32_t bf_size; uint32_t chunk_size; uint32_t bf_bits_per_element; - uint16_t msize; - msize = htons (mh->size); - if (msize < sizeof (struct BFMessage)) - { - GNUNET_break_op (0); - fail_intersection_operation (op); - return; - } - msg = (const struct BFMessage *) mh; switch (op->state->phase) { case PHASE_INITIAL: GNUNET_break_op (0); fail_intersection_operation (op); - break; + return; case PHASE_COUNT_SENT: case PHASE_BF_EXCHANGE: bf_size = ntohl (msg->bloomfilter_total_length); bf_bits_per_element = ntohl (msg->bits_per_element); - chunk_size = msize - sizeof (struct BFMessage); + chunk_size = htons (msg->header.size) - sizeof (struct BFMessage); op->state->other_xor = msg->element_xor_hash; if (bf_size == chunk_size) { @@ -717,7 +729,7 @@ handle_p2p_bf (void *cls, op->state->salt = ntohl (msg->sender_mutator); op->spec->remote_element_count = ntohl (msg->sender_element_count); process_bf (op); - return; + break; } /* multipart chunk */ if (NULL == op->state->bf_data) @@ -764,8 +776,9 @@ handle_p2p_bf (void *cls, default: GNUNET_break_op (0); fail_intersection_operation (op); - break; + return; } + GNUNET_CADET_receive_done (op->channel); } @@ -836,6 +849,7 @@ static void begin_bf_exchange (struct Operation *op) { op->state->phase = PHASE_BF_EXCHANGE; + GNUNET_assert (NULL == op->state->my_elements); op->state->my_elements = GNUNET_CONTAINER_multihashmap_create (op->state->my_element_count, GNUNET_YES); @@ -853,20 +867,18 @@ begin_bf_exchange (struct Operation *op) * @param cls the intersection operation * @param mh the header of the message */ -static void -handle_p2p_element_info (void *cls, - const struct GNUNET_MessageHeader *mh) +void +handle_intersection_p2p_element_info (void *cls, + const struct IntersectionElementInfoMessage *msg) { struct Operation *op = cls; - const struct IntersectionElementInfoMessage *msg; - if (ntohs (mh->size) != sizeof (struct IntersectionElementInfoMessage)) + if (OT_INTERSECTION != op->type) { GNUNET_break_op (0); fail_intersection_operation(op); return; } - msg = (const struct IntersectionElementInfoMessage *) mh; op->spec->remote_element_count = ntohl (msg->sender_element_count); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received remote element count (%u), I have %u\n", @@ -884,6 +896,7 @@ handle_p2p_element_info (void *cls, } GNUNET_break (NULL == op->state->remote_bf); begin_bf_exchange (op); + GNUNET_CADET_receive_done (op->channel); } @@ -955,28 +968,26 @@ filter_all (void *cls, * @param cls the intersection operation * @param mh the message */ -static void -handle_p2p_done (void *cls, - const struct GNUNET_MessageHeader *mh) +void +handle_intersection_p2p_done (void *cls, + const struct IntersectionDoneMessage *idm) { struct Operation *op = cls; - const struct IntersectionDoneMessage *idm; - if (PHASE_BF_EXCHANGE != op->state->phase) + if (OT_INTERSECTION != op->type) { - /* wrong phase to conclude? FIXME: Or should we allow this - if the other peer has _initially_ already an empty set? */ GNUNET_break_op (0); - fail_intersection_operation (op); + fail_intersection_operation(op); return; } - if (ntohs (mh->size) != sizeof (struct IntersectionDoneMessage)) + if (PHASE_BF_EXCHANGE != op->state->phase) { + /* wrong phase to conclude? FIXME: Or should we allow this + if the other peer has _initially_ already an empty set? */ GNUNET_break_op (0); fail_intersection_operation (op); return; } - idm = (const struct IntersectionDoneMessage *) mh; if (0 == ntohl (idm->final_element_count)) { /* other peer determined empty set is the intersection, @@ -1000,6 +1011,7 @@ handle_p2p_done (void *cls, op->state->my_element_count); op->state->phase = PHASE_FINISHED; finish_and_destroy (op); + GNUNET_CADET_receive_done (op->channel); } @@ -1064,11 +1076,11 @@ intersection_accept (struct Operation *op) op->state->phase = PHASE_INITIAL; op->state->my_element_count = op->spec->set->state->current_set_element_count; + GNUNET_assert (NULL == op->state->my_elements); op->state->my_elements - = GNUNET_CONTAINER_multihashmap_create - (GNUNET_MIN (op->state->my_element_count, - op->spec->remote_element_count), - GNUNET_YES); + = GNUNET_CONTAINER_multihashmap_create (GNUNET_MIN (op->state->my_element_count, + op->spec->remote_element_count), + GNUNET_YES); if (op->spec->remote_element_count < op->state->my_element_count) { /* If the other peer (Alice) has fewer elements than us (Bob), @@ -1083,43 +1095,6 @@ intersection_accept (struct Operation *op) /** - * Dispatch messages for a intersection operation. - * - * @param op the state of the intersection evaluate operation - * @param mh the received message - * @return #GNUNET_SYSERR if the tunnel should be disconnected, - * #GNUNET_OK otherwise - */ -static int -intersection_handle_p2p_message (struct Operation *op, - const struct GNUNET_MessageHeader *mh) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received p2p message (t: %u, s: %u)\n", - ntohs (mh->type), ntohs (mh->size)); - switch (ntohs (mh->type)) - { - /* this message handler is not active until after we received an - * operation request message, thus the ops request is not handled here - */ - case GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO: - handle_p2p_element_info (op, mh); - break; - case GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF: - handle_p2p_bf (op, mh); - break; - case GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE: - handle_p2p_done (op, mh); - break; - default: - /* something wrong with cadet's message handlers? */ - GNUNET_assert (0); - } - return GNUNET_OK; -} - - -/** * Handler for peer-disconnects, notifies the client about the aborted * operation. If we did not expect anything from the other peer, we * gracefully terminate the operation. @@ -1168,6 +1143,11 @@ intersection_op_cancel (struct Operation *op) GNUNET_CONTAINER_multihashmap_destroy (op->state->my_elements); op->state->my_elements = NULL; } + if (NULL != op->state->full_result_iter) + { + GNUNET_CONTAINER_multihashmap_iterator_destroy (op->state->full_result_iter); + op->state->full_result_iter = NULL; + } GNUNET_free (op->state); op->state = NULL; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -1245,7 +1225,6 @@ _GSS_intersection_vt () { static const struct SetVT intersection_vt = { .create = &intersection_set_create, - .msg_handler = &intersection_handle_p2p_message, .add = &intersection_add, .remove = &intersection_remove, .destroy_set = &intersection_set_destroy, diff --git a/src/set/gnunet-service-set_union.c b/src/set/gnunet-service-set_union.c index b5b6020741..1ff3d77164 100644 --- a/src/set/gnunet-service-set_union.c +++ b/src/set/gnunet-service-set_union.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet - Copyright (C) 2013-2016 GNUnet e.V. + Copyright (C) 2013-2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -19,15 +19,16 @@ */ /** * @file set/gnunet-service-set_union.c - * @brief two-peer set operations * @author Florian Dold + * @author Christian Grothoff */ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_statistics_service.h" #include "gnunet-service-set.h" #include "ibf.h" +#include "gnunet-service-set_union.h" #include "gnunet-service-set_union_strata_estimator.h" #include "gnunet-service-set_protocol.h" #include <gcrypt.h> @@ -786,11 +787,18 @@ send_element_iterator (void *cls, struct GNUNET_SET_Element *el = &ee->element; struct GNUNET_MQ_Envelope *ev; - - ev = GNUNET_MQ_msg_extra (emsg, el->size, GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT); + LOG (GNUNET_ERROR_TYPE_INFO, + "Sending element %s\n", + GNUNET_h2s (key)); + ev = GNUNET_MQ_msg_extra (emsg, + el->size, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT); emsg->element_type = htons (el->element_type); - GNUNET_memcpy (&emsg[1], el->data, el->size); - GNUNET_MQ_send (op->mq, ev); + GNUNET_memcpy (&emsg[1], + el->data, + el->size); + GNUNET_MQ_send (op->mq, + ev); return GNUNET_YES; } @@ -801,11 +809,14 @@ send_full_set (struct Operation *op) struct GNUNET_MQ_Envelope *ev; op->state->phase = PHASE_FULL_SENDING; - + /* FIXME: use a more memory-friendly way of doing this with an + iterator, just as we do in the non-full case! */ (void) GNUNET_CONTAINER_multihashmap_iterate (op->spec->set->content->elements, - &send_element_iterator, op); + &send_element_iterator, + op); ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE); - GNUNET_MQ_send (op->mq, ev); + GNUNET_MQ_send (op->mq, + ev); } @@ -813,42 +824,56 @@ send_full_set (struct Operation *op) * Handle a strata estimator from a remote peer * * @param cls the union operation - * @param mh the message - * @param is_compressed #GNUNET_YES if the estimator is compressed - * @return #GNUNET_SYSERR if the tunnel should be disconnected, - * #GNUNET_OK otherwise + * @param msg the message */ -static int -handle_p2p_strata_estimator (void *cls, - const struct GNUNET_MessageHeader *mh, - int is_compressed) +int +check_union_p2p_strata_estimator (void *cls, + const struct StrataEstimatorMessage *msg) { struct Operation *op = cls; - struct StrataEstimator *remote_se; - struct StrataEstimatorMessage *msg = (void *) mh; - unsigned int diff; - uint64_t other_size; + int is_compressed; size_t len; - GNUNET_STATISTICS_update (_GSS_statistics, - "# bytes of SE received", - ntohs (mh->size), - GNUNET_NO); - if (op->state->phase != PHASE_EXPECT_SE) { GNUNET_break (0); - fail_union_operation (op); return GNUNET_SYSERR; } - len = ntohs (mh->size) - sizeof (struct StrataEstimatorMessage); + is_compressed = (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC == htons (msg->header.type)); + len = ntohs (msg->header.size) - sizeof (struct StrataEstimatorMessage); if ( (GNUNET_NO == is_compressed) && (len != SE_STRATA_COUNT * SE_IBF_SIZE * IBF_BUCKET_SIZE) ) { - fail_union_operation (op); GNUNET_break (0); return GNUNET_SYSERR; } + return GNUNET_OK; +} + + +/** + * Handle a strata estimator from a remote peer + * + * @param cls the union operation + * @param msg the message + */ +void +handle_union_p2p_strata_estimator (void *cls, + const struct StrataEstimatorMessage *msg) +{ + struct Operation *op = cls; + struct StrataEstimator *remote_se; + unsigned int diff; + uint64_t other_size; + size_t len; + int is_compressed; + + is_compressed = (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC == htons (msg->header.type)); + GNUNET_STATISTICS_update (_GSS_statistics, + "# bytes of SE received", + ntohs (msg->header.size), + GNUNET_NO); + len = ntohs (msg->header.size) - sizeof (struct StrataEstimatorMessage); other_size = GNUNET_ntohll (msg->set_size); remote_se = strata_estimator_create (SE_STRATA_COUNT, SE_IBF_SIZE, @@ -857,7 +882,7 @@ handle_p2p_strata_estimator (void *cls, { /* insufficient resources, fail */ fail_union_operation (op); - return GNUNET_SYSERR; + return; } if (GNUNET_OK != strata_estimator_read (&msg[1], @@ -866,18 +891,16 @@ handle_p2p_strata_estimator (void *cls, remote_se)) { /* decompression failed */ - fail_union_operation (op); strata_estimator_destroy (remote_se); - return GNUNET_SYSERR; + fail_union_operation (op); + return; } GNUNET_assert (NULL != op->state->se); diff = strata_estimator_difference (remote_se, op->state->se); if (diff > 200) - diff = diff * 3 / 2; - - + diff = diff * 3 / 2; strata_estimator_destroy (remote_se); strata_estimator_destroy (op->state->se); @@ -885,12 +908,14 @@ handle_p2p_strata_estimator (void *cls, LOG (GNUNET_ERROR_TYPE_DEBUG, "got se diff=%d, using ibf size %d\n", diff, - 1<<get_order_from_difference (diff)); + 1U << get_order_from_difference (diff)); { char *set_debug; + set_debug = getenv ("GNUNET_SET_BENCHMARK"); - if ( (NULL != set_debug) && (0 == strcmp (set_debug, "1")) ) + if ( (NULL != set_debug) && + (0 == strcmp (set_debug, "1")) ) { FILE *f = fopen ("set.log", "a"); fprintf (f, "%llu\n", (unsigned long long) diff); @@ -898,34 +923,41 @@ handle_p2p_strata_estimator (void *cls, } } - if ((GNUNET_YES == op->spec->byzantine) && (other_size < op->spec->byzantine_lower_bound)) + if ( (GNUNET_YES == op->spec->byzantine) && + (other_size < op->spec->byzantine_lower_bound) ) { GNUNET_break (0); fail_union_operation (op); - return GNUNET_SYSERR; + return; } - - if ( (GNUNET_YES == op->spec->force_full) || (diff > op->state->initial_size / 4)) + if ( (GNUNET_YES == op->spec->force_full) || + (diff > op->state->initial_size / 4) || + (0 == other_size) ) { LOG (GNUNET_ERROR_TYPE_INFO, - "Sending full set (diff=%d, own set=%u)\n", + "Deciding to go for full set transmission (diff=%d, own set=%u)\n", diff, op->state->initial_size); GNUNET_STATISTICS_update (_GSS_statistics, "# of full sends", 1, GNUNET_NO); - if (op->state->initial_size <= other_size) + if ( (op->state->initial_size <= other_size) || + (0 == other_size) ) { send_full_set (op); } else { struct GNUNET_MQ_Envelope *ev; + + LOG (GNUNET_ERROR_TYPE_INFO, + "Telling other peer that we expect its full set\n"); op->state->phase = PHASE_EXPECT_IBF; ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_REQUEST_FULL); - GNUNET_MQ_send (op->mq, ev); + GNUNET_MQ_send (op->mq, + ev); } } else @@ -942,11 +974,10 @@ handle_p2p_strata_estimator (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to send IBF, closing connection\n"); fail_union_operation (op); - return GNUNET_SYSERR; + return; } } - - return GNUNET_OK; + GNUNET_CADET_receive_done (op->channel); } @@ -1164,99 +1195,116 @@ decode_and_send (struct Operation *op) /** - * Handle an IBF message from a remote peer. + * Check an IBF message from a remote peer. * * Reassemble the IBF from multiple pieces, and * process the whole IBF once possible. * * @param cls the union operation - * @param mh the header of the message - * @return #GNUNET_SYSERR if the tunnel should be disconnected, - * #GNUNET_OK otherwise + * @param msg the header of the message + * @return #GNUNET_OK if @a msg is well-formed */ -static int -handle_p2p_ibf (void *cls, - const struct GNUNET_MessageHeader *mh) +int +check_union_p2p_ibf (void *cls, + const struct IBFMessage *msg) { struct Operation *op = cls; - const struct IBFMessage *msg; unsigned int buckets_in_message; - if (ntohs (mh->size) < sizeof (struct IBFMessage)) + if (OT_UNION != op->type) { GNUNET_break_op (0); - fail_union_operation (op); return GNUNET_SYSERR; } - msg = (const struct IBFMessage *) mh; - if ( (op->state->phase == PHASE_INVENTORY_PASSIVE) || - (op->state->phase == PHASE_EXPECT_IBF) ) + buckets_in_message = (ntohs (msg->header.size) - sizeof *msg) / IBF_BUCKET_SIZE; + if (0 == buckets_in_message) { - op->state->phase = PHASE_EXPECT_IBF_CONT; - GNUNET_assert (NULL == op->state->remote_ibf); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Creating new ibf of size %u\n", - 1 << msg->order); - op->state->remote_ibf = ibf_create (1<<msg->order, SE_IBF_HASH_NUM); - op->state->salt_receive = ntohl (msg->salt); - LOG (GNUNET_ERROR_TYPE_DEBUG, "Receiving new IBF with salt %u\n", op->state->salt_receive); - if (NULL == op->state->remote_ibf) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse remote IBF, closing connection\n"); - fail_union_operation (op); - return GNUNET_SYSERR; - } - op->state->ibf_buckets_received = 0; - if (0 != ntohl (msg->offset)) - { - GNUNET_break_op (0); - fail_union_operation (op); - return GNUNET_SYSERR; - } + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if ((ntohs (msg->header.size) - sizeof *msg) != buckets_in_message * IBF_BUCKET_SIZE) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; } - else if (op->state->phase == PHASE_EXPECT_IBF_CONT) + if (op->state->phase == PHASE_EXPECT_IBF_CONT) { if (ntohl (msg->offset) != op->state->ibf_buckets_received) { GNUNET_break_op (0); - fail_union_operation (op); return GNUNET_SYSERR; } if (1<<msg->order != op->state->remote_ibf->size) { GNUNET_break_op (0); - fail_union_operation (op); return GNUNET_SYSERR; } if (ntohl (msg->salt) != op->state->salt_receive) { GNUNET_break_op (0); - fail_union_operation (op); return GNUNET_SYSERR; } } - else + else if ( (op->state->phase != PHASE_INVENTORY_PASSIVE) && + (op->state->phase != PHASE_EXPECT_IBF) ) { - GNUNET_assert (0); + GNUNET_break_op (0); + return GNUNET_SYSERR; } - buckets_in_message = (ntohs (msg->header.size) - sizeof *msg) / IBF_BUCKET_SIZE; + return GNUNET_OK; +} - if (0 == buckets_in_message) + +/** + * Handle an IBF message from a remote peer. + * + * Reassemble the IBF from multiple pieces, and + * process the whole IBF once possible. + * + * @param cls the union operation + * @param msg the header of the message + */ +void +handle_union_p2p_ibf (void *cls, + const struct IBFMessage *msg) +{ + struct Operation *op = cls; + unsigned int buckets_in_message; + + buckets_in_message = (ntohs (msg->header.size) - sizeof *msg) / IBF_BUCKET_SIZE; + if ( (op->state->phase == PHASE_INVENTORY_PASSIVE) || + (op->state->phase == PHASE_EXPECT_IBF) ) { - GNUNET_break_op (0); - fail_union_operation (op); - return GNUNET_SYSERR; + op->state->phase = PHASE_EXPECT_IBF_CONT; + GNUNET_assert (NULL == op->state->remote_ibf); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Creating new ibf of size %u\n", + 1 << msg->order); + op->state->remote_ibf = ibf_create (1<<msg->order, SE_IBF_HASH_NUM); + op->state->salt_receive = ntohl (msg->salt); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Receiving new IBF with salt %u\n", + op->state->salt_receive); + if (NULL == op->state->remote_ibf) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse remote IBF, closing connection\n"); + fail_union_operation (op); + return; + } + op->state->ibf_buckets_received = 0; + if (0 != ntohl (msg->offset)) + { + GNUNET_break_op (0); + fail_union_operation (op); + return; + } } - - if ((ntohs (msg->header.size) - sizeof *msg) != buckets_in_message * IBF_BUCKET_SIZE) + else { - GNUNET_break_op (0); - fail_union_operation (op); - return GNUNET_SYSERR; + GNUNET_assert (op->state->phase == PHASE_EXPECT_IBF_CONT); } - GNUNET_assert (NULL != op->state->remote_ibf); ibf_read_slice (&msg[1], @@ -1276,10 +1324,11 @@ handle_p2p_ibf (void *cls, /* Internal error, best we can do is shut down */ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to decode IBF, closing connection\n"); - return GNUNET_SYSERR; + fail_union_operation (op); + return; } } - return GNUNET_OK; + GNUNET_CADET_receive_done (op->channel); } @@ -1343,6 +1392,11 @@ send_done_and_destroy (void *cls) } +/** + * Tests if the operation is finished, and if so notify. + * + * @param op operation to check + */ static void maybe_finish (struct Operation *op) { @@ -1382,46 +1436,59 @@ maybe_finish (struct Operation *op) /** - * Handle an element message from a remote peer. - * Sent by the other peer either because we decoded an IBF and placed a demand, - * or because the other peer switched to full set transmission. + * Check an element message from a remote peer. * * @param cls the union operation - * @param mh the message + * @param emsg the message */ -static void -handle_p2p_elements (void *cls, - const struct GNUNET_MessageHeader *mh) +int +check_union_p2p_elements (void *cls, + const struct GNUNET_SET_ElementMessage *emsg) { struct Operation *op = cls; - struct ElementEntry *ee; - const struct GNUNET_SET_ElementMessage *emsg; - uint16_t element_size; - if (0 == GNUNET_CONTAINER_multihashmap_size (op->state->demanded_hashes)) + if (OT_UNION != op->type) { GNUNET_break_op (0); - fail_union_operation (op); - return; + return GNUNET_SYSERR; } - if (ntohs (mh->size) < sizeof (struct GNUNET_SET_ElementMessage)) + if (0 == GNUNET_CONTAINER_multihashmap_size (op->state->demanded_hashes)) { GNUNET_break_op (0); - fail_union_operation (op); - return; + return GNUNET_SYSERR; } + return GNUNET_OK; +} + - emsg = (const struct GNUNET_SET_ElementMessage *) mh; +/** + * Handle an element message from a remote peer. + * Sent by the other peer either because we decoded an IBF and placed a demand, + * or because the other peer switched to full set transmission. + * + * @param cls the union operation + * @param emsg the message + */ +void +handle_union_p2p_elements (void *cls, + const struct GNUNET_SET_ElementMessage *emsg) +{ + struct Operation *op = cls; + struct ElementEntry *ee; + struct KeyEntry *ke; + uint16_t element_size; - element_size = ntohs (mh->size) - sizeof (struct GNUNET_SET_ElementMessage); + element_size = ntohs (emsg->header.size) - sizeof (struct GNUNET_SET_ElementMessage); ee = GNUNET_malloc (sizeof (struct ElementEntry) + element_size); - GNUNET_memcpy (&ee[1], &emsg[1], element_size); + GNUNET_memcpy (&ee[1], + &emsg[1], + element_size); ee->element.size = element_size; ee->element.data = &ee[1]; ee->element.element_type = ntohs (emsg->element_type); ee->remote = GNUNET_YES; - GNUNET_SET_element_hash (&ee->element, &ee->element_hash); - + GNUNET_SET_element_hash (&ee->element, + &ee->element_hash); if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_remove (op->state->demanded_hashes, &ee->element_hash, @@ -1429,7 +1496,6 @@ handle_p2p_elements (void *cls, { /* We got something we didn't demand, since it's not in our map. */ GNUNET_break_op (0); - GNUNET_free (ee); fail_union_operation (op); return; } @@ -1448,10 +1514,9 @@ handle_p2p_elements (void *cls, 1, GNUNET_NO); - op->state->received_total += 1; - - struct KeyEntry *ke = op_get_element (op, &ee->element_hash); + op->state->received_total++; + ke = op_get_element (op, &ee->element_hash); if (NULL != ke) { /* Got repeated element. Should not happen since @@ -1467,7 +1532,7 @@ handle_p2p_elements (void *cls, { LOG (GNUNET_ERROR_TYPE_DEBUG, "Registering new element from remote peer\n"); - op->state->received_fresh += 1; + op->state->received_fresh++; op_register_element (op, ee, GNUNET_YES); /* only send results immediately if the client wants it */ switch (op->spec->result_mode) @@ -1485,43 +1550,57 @@ handle_p2p_elements (void *cls, } } - if (op->state->received_total > 8 && op->state->received_fresh < op->state->received_total / 3) + if ( (op->state->received_total > 8) && + (op->state->received_fresh < op->state->received_total / 3) ) { /* The other peer gave us lots of old elements, there's something wrong. */ GNUNET_break_op (0); fail_union_operation (op); return; } - + GNUNET_CADET_receive_done (op->channel); maybe_finish (op); } /** - * Handle an element message from a remote peer. + * Check a full element message from a remote peer. * * @param cls the union operation - * @param mh the message + * @param emsg the message */ -static void -handle_p2p_full_element (void *cls, - const struct GNUNET_MessageHeader *mh) +int +check_union_p2p_full_element (void *cls, + const struct GNUNET_SET_ElementMessage *emsg) { struct Operation *op = cls; - struct ElementEntry *ee; - const struct GNUNET_SET_ElementMessage *emsg; - uint16_t element_size; - if (ntohs (mh->size) < sizeof (struct GNUNET_SET_ElementMessage)) + if (OT_UNION != op->type) { GNUNET_break_op (0); - fail_union_operation (op); - return; + return GNUNET_SYSERR; } + // FIXME: check that we expect full elements here? + return GNUNET_OK; +} - emsg = (const struct GNUNET_SET_ElementMessage *) mh; - element_size = ntohs (mh->size) - sizeof (struct GNUNET_SET_ElementMessage); +/** + * Handle an element message from a remote peer. + * + * @param cls the union operation + * @param emsg the message + */ +void +handle_union_p2p_full_element (void *cls, + const struct GNUNET_SET_ElementMessage *emsg) +{ + struct Operation *op = cls; + struct ElementEntry *ee; + struct KeyEntry *ke; + uint16_t element_size; + + element_size = ntohs (emsg->header.size) - sizeof (struct GNUNET_SET_ElementMessage); ee = GNUNET_malloc (sizeof (struct ElementEntry) + element_size); GNUNET_memcpy (&ee[1], &emsg[1], element_size); ee->element.size = element_size; @@ -1544,10 +1623,9 @@ handle_p2p_full_element (void *cls, 1, GNUNET_NO); - op->state->received_total += 1; - - struct KeyEntry *ke = op_get_element (op, &ee->element_hash); + op->state->received_total++; + ke = op_get_element (op, &ee->element_hash); if (NULL != ke) { /* Got repeated element. Should not happen since @@ -1563,7 +1641,7 @@ handle_p2p_full_element (void *cls, { LOG (GNUNET_ERROR_TYPE_DEBUG, "Registering new element from remote peer\n"); - op->state->received_fresh += 1; + op->state->received_fresh++; op_register_element (op, ee, GNUNET_YES); /* only send results immediately if the client wants it */ switch (op->spec->result_mode) @@ -1581,8 +1659,8 @@ handle_p2p_full_element (void *cls, } } - if ( (GNUNET_YES == op->spec->byzantine) && - (op->state->received_total > 384 + op->state->received_fresh * 4) && + if ( (GNUNET_YES == op->spec->byzantine) && + (op->state->received_total > 384 + op->state->received_fresh * 4) && (op->state->received_fresh < op->state->received_total / 6) ) { /* The other peer gave us lots of old elements, there's something wrong. */ @@ -1594,51 +1672,73 @@ handle_p2p_full_element (void *cls, fail_union_operation (op); return; } + GNUNET_CADET_receive_done (op->channel); } + /** * Send offers (for GNUNET_Hash-es) in response * to inquiries (for IBF_Key-s). * * @param cls the union operation - * @param mh the message + * @param msg the message */ -static void -handle_p2p_inquiry (void *cls, - const struct GNUNET_MessageHeader *mh) +int +check_union_p2p_inquiry (void *cls, + const struct InquiryMessage *msg) { struct Operation *op = cls; - const struct IBF_Key *ibf_key; unsigned int num_keys; - struct InquiryMessage *msg; - /* look up elements and send them */ + if (OT_UNION != op->type) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } if (op->state->phase != PHASE_INVENTORY_PASSIVE) { GNUNET_break_op (0); - fail_union_operation (op); - return; + return GNUNET_SYSERR; } - num_keys = (ntohs (mh->size) - sizeof (struct InquiryMessage)) - / sizeof (struct IBF_Key); - if ((ntohs (mh->size) - sizeof (struct InquiryMessage)) + num_keys = (ntohs (msg->header.size) - sizeof (struct InquiryMessage)) + / sizeof (struct IBF_Key); + if ((ntohs (msg->header.size) - sizeof (struct InquiryMessage)) != num_keys * sizeof (struct IBF_Key)) { GNUNET_break_op (0); - fail_union_operation (op); - return; + return GNUNET_SYSERR; } + return GNUNET_OK; +} + - msg = (struct InquiryMessage *) mh; +/** + * Send offers (for GNUNET_Hash-es) in response + * to inquiries (for IBF_Key-s). + * + * @param cls the union operation + * @param msg the message + */ +void +handle_union_p2p_inquiry (void *cls, + const struct InquiryMessage *msg) +{ + struct Operation *op = cls; + const struct IBF_Key *ibf_key; + unsigned int num_keys; + num_keys = (ntohs (msg->header.size) - sizeof (struct InquiryMessage)) + / sizeof (struct IBF_Key); ibf_key = (const struct IBF_Key *) &msg[1]; while (0 != num_keys--) { struct IBF_Key unsalted_key; + unsalt_key (ibf_key, ntohl (msg->salt), &unsalted_key); send_offers_for_key (op, unsalted_key); ibf_key++; } + GNUNET_CADET_receive_done (op->channel); } @@ -1677,27 +1777,36 @@ send_missing_elements_iter (void *cls, /** - * Handle a + * Handle a request for full set transmission. * * @parem cls closure, a set union operation * @param mh the demand message */ -static void -handle_p2p_request_full (void *cls, - const struct GNUNET_MessageHeader *mh) +void +handle_union_p2p_request_full (void *cls, + const struct GNUNET_MessageHeader *mh) { struct Operation *op = cls; - if (PHASE_EXPECT_IBF != op->state->phase) + LOG (GNUNET_ERROR_TYPE_INFO, + "Received request for full set transmission\n"); + if (OT_UNION != op->type) { + GNUNET_break_op (0); fail_union_operation (op); + return; + } + if (PHASE_EXPECT_IBF != op->state->phase) + { GNUNET_break_op (0); + fail_union_operation (op); return; } // FIXME: we need to check that our set is larger than the // byzantine_lower_bound by some threshold send_full_set (op); + GNUNET_CADET_receive_done (op->channel); } @@ -1707,56 +1816,97 @@ handle_p2p_request_full (void *cls, * @parem cls closure, a set union operation * @param mh the demand message */ -static void -handle_p2p_full_done (void *cls, - const struct GNUNET_MessageHeader *mh) +void +handle_union_p2p_full_done (void *cls, + const struct GNUNET_MessageHeader *mh) { struct Operation *op = cls; - if (PHASE_EXPECT_IBF == op->state->phase) + switch (op->state->phase) { - struct GNUNET_MQ_Envelope *ev; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "got FULL DONE, sending elements that other peer is missing\n"); + case PHASE_EXPECT_IBF: + { + struct GNUNET_MQ_Envelope *ev; - /* send all the elements that did not come from the remote peer */ - GNUNET_CONTAINER_multihashmap32_iterate (op->state->key_to_element, - &send_missing_elements_iter, - op); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "got FULL DONE, sending elements that other peer is missing\n"); - ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE); - GNUNET_MQ_send (op->mq, ev); - op->state->phase = PHASE_DONE; + /* send all the elements that did not come from the remote peer */ + GNUNET_CONTAINER_multihashmap32_iterate (op->state->key_to_element, + &send_missing_elements_iter, + op); - /* we now wait until the other peer shuts the tunnel down*/ + ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE); + GNUNET_MQ_send (op->mq, ev); + op->state->phase = PHASE_DONE; + /* we now wait until the other peer shuts the tunnel down*/ + } + break; + case PHASE_FULL_SENDING: + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "got FULL DONE, finishing\n"); + /* We sent the full set, and got the response for that. We're done. */ + op->state->phase = PHASE_DONE; + GNUNET_CADET_receive_done (op->channel); + send_done_and_destroy (op); + return; + } + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Handle full done phase is %u\n", + (unsigned) op->state->phase); + GNUNET_break_op (0); + fail_union_operation (op); + return; } - else if (PHASE_FULL_SENDING == op->state->phase) + GNUNET_CADET_receive_done (op->channel); +} + + +/** + * Check a demand by the other peer for elements based on a list + * of `struct GNUNET_HashCode`s. + * + * @parem cls closure, a set union operation + * @param mh the demand message + * @return #GNUNET_OK if @a mh is well-formed + */ +int +check_union_p2p_demand (void *cls, + const struct GNUNET_MessageHeader *mh) +{ + struct Operation *op = cls; + unsigned int num_hashes; + + if (OT_UNION != op->type) { - LOG (GNUNET_ERROR_TYPE_DEBUG, "got FULL DONE, finishing\n"); - /* We sent the full set, and got the response for that. We're done. */ - op->state->phase = PHASE_DONE; - send_done_and_destroy (op); + GNUNET_break_op (0); + return GNUNET_SYSERR; } - else + num_hashes = (ntohs (mh->size) - sizeof (struct GNUNET_MessageHeader)) + / sizeof (struct GNUNET_HashCode); + if ((ntohs (mh->size) - sizeof (struct GNUNET_MessageHeader)) + != num_hashes * sizeof (struct GNUNET_HashCode)) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "handle full done phase is %u\n", (unsigned) op->state->phase); GNUNET_break_op (0); - fail_union_operation (op); - return; + return GNUNET_SYSERR; } + return GNUNET_OK; } /** * Handle a demand by the other peer for elements based on a list - * of GNUNET_HashCode-s. + * of `struct GNUNET_HashCode`s. * * @parem cls closure, a set union operation * @param mh the demand message */ -static void -handle_p2p_demand (void *cls, - const struct GNUNET_MessageHeader *mh) +void +handle_union_p2p_demand (void *cls, + const struct GNUNET_MessageHeader *mh) { struct Operation *op = cls; struct ElementEntry *ee; @@ -1767,19 +1917,12 @@ handle_p2p_demand (void *cls, num_hashes = (ntohs (mh->size) - sizeof (struct GNUNET_MessageHeader)) / sizeof (struct GNUNET_HashCode); - if ((ntohs (mh->size) - sizeof (struct GNUNET_MessageHeader)) - != num_hashes * sizeof (struct GNUNET_HashCode)) - { - GNUNET_break_op (0); - fail_union_operation (op); - return; - } - for (hash = (const struct GNUNET_HashCode *) &mh[1]; num_hashes > 0; hash++, num_hashes--) { - ee = GNUNET_CONTAINER_multihashmap_get (op->spec->set->content->elements, hash); + ee = GNUNET_CONTAINER_multihashmap_get (op->spec->set->content->elements, + hash); if (NULL == ee) { /* Demand for non-existing element. */ @@ -1823,31 +1966,35 @@ handle_p2p_demand (void *cls, break; } } + GNUNET_CADET_receive_done (op->channel); } /** - * Handle offers (of GNUNET_HashCode-s) and - * respond with demands (of GNUNET_HashCode-s). + * Check offer (of `struct GNUNET_HashCode`s). * * @param cls the union operation * @param mh the message + * @return #GNUNET_OK if @a mh is well-formed */ -static void -handle_p2p_offer (void *cls, - const struct GNUNET_MessageHeader *mh) +int +check_union_p2p_offer (void *cls, + const struct GNUNET_MessageHeader *mh) { struct Operation *op = cls; - const struct GNUNET_HashCode *hash; unsigned int num_hashes; + if (OT_UNION != op->type) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } /* look up elements and send them */ if ( (op->state->phase != PHASE_INVENTORY_PASSIVE) && (op->state->phase != PHASE_INVENTORY_ACTIVE)) { GNUNET_break_op (0); - fail_union_operation (op); - return; + return GNUNET_SYSERR; } num_hashes = (ntohs (mh->size) - sizeof (struct GNUNET_MessageHeader)) / sizeof (struct GNUNET_HashCode); @@ -1855,10 +2002,29 @@ handle_p2p_offer (void *cls, != num_hashes * sizeof (struct GNUNET_HashCode)) { GNUNET_break_op (0); - fail_union_operation (op); - return; + return GNUNET_SYSERR; } + return GNUNET_OK; +} + + +/** + * Handle offers (of `struct GNUNET_HashCode`s) and + * respond with demands (of `struct GNUNET_HashCode`s). + * + * @param cls the union operation + * @param mh the message + */ +void +handle_union_p2p_offer (void *cls, + const struct GNUNET_MessageHeader *mh) +{ + struct Operation *op = cls; + const struct GNUNET_HashCode *hash; + unsigned int num_hashes; + num_hashes = (ntohs (mh->size) - sizeof (struct GNUNET_MessageHeader)) + / sizeof (struct GNUNET_HashCode); for (hash = (const struct GNUNET_HashCode *) &mh[1]; num_hashes > 0; hash++, num_hashes--) @@ -1897,6 +2063,7 @@ handle_p2p_offer (void *cls, *(struct GNUNET_HashCode *) &demands[1] = *hash; GNUNET_MQ_send (op->mq, ev); } + GNUNET_CADET_receive_done (op->channel); } @@ -1906,16 +2073,22 @@ handle_p2p_offer (void *cls, * @param cls the union operation * @param mh the message */ -static void -handle_p2p_done (void *cls, - const struct GNUNET_MessageHeader *mh) +void +handle_union_p2p_done (void *cls, + const struct GNUNET_MessageHeader *mh) { struct Operation *op = cls; - if (op->state->phase == PHASE_INVENTORY_PASSIVE) + if (OT_UNION != op->type) { + GNUNET_break_op (0); + fail_union_operation (op); + return; + } + switch (op->state->phase) + { + case PHASE_INVENTORY_PASSIVE: /* We got all requests, but still have to send our elements in response. */ - op->state->phase = PHASE_FINISH_WAITING; LOG (GNUNET_ERROR_TYPE_DEBUG, @@ -1929,11 +2102,10 @@ handle_p2p_done (void *cls, * all our demands are satisfied, so that the active * peer can quit if we gave him everything. */ + GNUNET_CADET_receive_done (op->channel); maybe_finish (op); return; - } - if (op->state->phase == PHASE_INVENTORY_ACTIVE) - { + case PHASE_INVENTORY_ACTIVE: LOG (GNUNET_ERROR_TYPE_DEBUG, "got DONE (as active partner), waiting to finish\n"); /* All demands of the other peer are satisfied, @@ -1944,11 +2116,14 @@ handle_p2p_done (void *cls, * to the other peer once our demands are met. */ op->state->phase = PHASE_FINISH_CLOSING; + GNUNET_CADET_receive_done (op->channel); maybe_finish (op); return; + default: + GNUNET_break_op (0); + fail_union_operation (op); + return; } - GNUNET_break_op (0); - fail_union_operation (op); } @@ -2119,62 +2294,6 @@ union_set_destroy (struct SetState *set_state) /** - * Dispatch messages for a union operation. - * - * @param op the state of the union evaluate operation - * @param mh the received message - * @return #GNUNET_SYSERR if the tunnel should be disconnected, - * #GNUNET_OK otherwise - */ -int -union_handle_p2p_message (struct Operation *op, - const struct GNUNET_MessageHeader *mh) -{ - //LOG (GNUNET_ERROR_TYPE_DEBUG, - // "received p2p message (t: %u, s: %u)\n", - // ntohs (mh->type), - // ntohs (mh->size)); - switch (ntohs (mh->type)) - { - case GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF: - return handle_p2p_ibf (op, mh); - case GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE: - return handle_p2p_strata_estimator (op, mh, GNUNET_NO); - case GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC: - return handle_p2p_strata_estimator (op, mh, GNUNET_YES); - case GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS: - handle_p2p_elements (op, mh); - break; - case GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT: - handle_p2p_full_element (op, mh); - break; - case GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY: - handle_p2p_inquiry (op, mh); - break; - case GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE: - handle_p2p_done (op, mh); - break; - case GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER: - handle_p2p_offer (op, mh); - break; - case GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND: - handle_p2p_demand (op, mh); - break; - case GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE: - handle_p2p_full_done (op, mh); - break; - case GNUNET_MESSAGE_TYPE_SET_UNION_P2P_REQUEST_FULL: - handle_p2p_request_full (op, mh); - break; - default: - /* Something wrong with cadet's message handlers? */ - GNUNET_assert (0); - } - return GNUNET_OK; -} - - -/** * Handler for peer-disconnects, notifies the client * about the aborted operation in case the op was not concluded. * @@ -2240,7 +2359,6 @@ _GSS_union_vt () { static const struct SetVT union_vt = { .create = &union_set_create, - .msg_handler = &union_handle_p2p_message, .add = &union_add, .remove = &union_remove, .destroy_set = &union_set_destroy, diff --git a/src/set/set_api.c b/src/set/set_api.c index 04a4e49108..bc428f9f6d 100644 --- a/src/set/set_api.c +++ b/src/set/set_api.c @@ -76,6 +76,8 @@ struct GNUNET_SET_Handle /** * Should the set be destroyed once all operations are gone? + * #GNUNET_SYSERR if #GNUNET_SET_destroy() must raise this flag, + * #GNUNET_YES if #GNUNET_SET_destroy() did raise this flag. */ int destroy_requested; @@ -345,11 +347,13 @@ handle_iter_done (void *cls, if (NULL == iter) return; + set->destroy_requested = GNUNET_SYSERR; set->iterator = NULL; set->iteration_id++; iter (set->iterator_cls, NULL); - + if (GNUNET_SYSERR == set->destroy_requested) + set->destroy_requested = GNUNET_NO; if (GNUNET_YES == set->destroy_requested) GNUNET_SET_destroy (set); } @@ -736,7 +740,9 @@ GNUNET_SET_destroy (struct GNUNET_SET_Handle *set) /* destroying set while iterator is active is currently not supported; we should expand the API to allow clients to explicitly cancel the iteration! */ - if ( (NULL != set->ops_head) || (NULL != set->iterator) ) + if ( (NULL != set->ops_head) || + (NULL != set->iterator) || + (GNUNET_SYSERR == set->destroy_requested) ) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Set operations are pending, delaying set destruction\n"); @@ -809,7 +815,7 @@ GNUNET_SET_prepare (const struct GNUNET_PeerIdentity *other_peer, msg->force_delta = GNUNET_YES; break; default: - LOG (GNUNET_ERROR_TYPE_ERROR, + LOG (GNUNET_ERROR_TYPE_ERROR, "Option with type %d not recognized\n", (int) opt->type); } } diff --git a/src/set/test_set_union_copy.c b/src/set/test_set_union_copy.c index c887a89588..a1eba63115 100644 --- a/src/set/test_set_union_copy.c +++ b/src/set/test_set_union_copy.c @@ -122,6 +122,7 @@ check_count_iter (void *cls, return GNUNET_NO; } ci_cls->cont (ci_cls->cont_cls); + GNUNET_free (ci_cls); return GNUNET_NO; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, diff --git a/src/sq/sq.c b/src/sq/sq.c index 114de2d88f..089ebf0ffc 100644 --- a/src/sq/sq.c +++ b/src/sq/sq.c @@ -90,7 +90,12 @@ GNUNET_SQ_extract_result (sqlite3_stmt *result, j, rs[i].result_size, rs[i].dst)) + { + for (unsigned int k=0;k<i;k++) + if (NULL != rs[k].cleaner) + rs[k].cleaner (rs[k].cls); return GNUNET_SYSERR; + } GNUNET_assert (0 != rs[i].num_params); j += rs[i].num_params; } @@ -112,4 +117,24 @@ GNUNET_SQ_cleanup_result (struct GNUNET_SQ_ResultSpec *rs) rs[i].cleaner (rs[i].cls); } + +/** + * Reset @a stmt and log error. + * + * @param dbh database handle + * @param stmt statement to reset + */ +void +GNUNET_SQ_reset (sqlite3 *dbh, + sqlite3_stmt *stmt) +{ + if (SQLITE_OK != + sqlite3_reset (stmt)) + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, + "sqlite", + _("Failed to reset sqlite statement with error: %s\n"), + sqlite3_errmsg (dbh)); +} + + /* end of sq.c */ diff --git a/src/sq/sq_query_helper.c b/src/sq/sq_query_helper.c index 5529c5e6c2..a04b4ced45 100644 --- a/src/sq/sq_query_helper.c +++ b/src/sq/sq_query_helper.c @@ -235,6 +235,40 @@ GNUNET_SQ_query_param_rsa_signature (const struct GNUNET_CRYPTO_RsaSignature *x) /** + * Function called to convert input argument into SQL parameters. + * + * @param cls closure + * @param data pointer to input argument + * @param data_len number of bytes in @a data (if applicable) + * @param stmt sqlite statement to bind parameters for + * @param off offset of the argument to bind in @a stmt, numbered from 1, + * so immediately suitable for passing to `sqlite3_bind`-functions. + * @return #GNUNET_SYSERR on error, #GNUNET_OK on success + */ +static int +bind_abstime (void *cls, + const void *data, + size_t data_len, + sqlite3_stmt *stmt, + unsigned int off) +{ + const struct GNUNET_TIME_Absolute *u = data; + struct GNUNET_TIME_Absolute abs; + + abs = *u; + if (abs.abs_value_us > INT64_MAX) + abs.abs_value_us = INT64_MAX; + GNUNET_assert (sizeof (uint64_t) == data_len); + if (SQLITE_OK != + sqlite3_bind_int64 (stmt, + (int) off, + (sqlite3_int64) abs.abs_value_us)) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** * Generate query parameter for an absolute time value. * The database must store a 64-bit integer. * @@ -243,7 +277,13 @@ GNUNET_SQ_query_param_rsa_signature (const struct GNUNET_CRYPTO_RsaSignature *x) struct GNUNET_SQ_QueryParam GNUNET_SQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x) { - return GNUNET_SQ_query_param_uint64 (&x->abs_value_us); + struct GNUNET_SQ_QueryParam qp = { + .conv = &bind_abstime, + .data = x, + .size = sizeof (struct GNUNET_TIME_Absolute), + .num_params = 1 + }; + return qp; } @@ -269,6 +309,8 @@ bind_nbotime (void *cls, struct GNUNET_TIME_Absolute abs; abs = GNUNET_TIME_absolute_ntoh (*u); + if (abs.abs_value_us > INT64_MAX) + abs.abs_value_us = INT64_MAX; GNUNET_assert (sizeof (uint64_t) == data_len); if (SQLITE_OK != sqlite3_bind_int64 (stmt, diff --git a/src/sq/sq_result_helper.c b/src/sq/sq_result_helper.c index eaf606aa44..fad3f3c8d4 100644 --- a/src/sq/sq_result_helper.c +++ b/src/sq/sq_result_helper.c @@ -46,6 +46,15 @@ extract_var_blob (void *cls, const void *ret; void **rdst = (void **) dst; + if (SQLITE_NULL == + sqlite3_column_type (result, + column)) + { + *rdst = NULL; + *dst_size = 0; + return GNUNET_YES; + } + if (SQLITE_BLOB != sqlite3_column_type (result, column)) @@ -142,6 +151,14 @@ extract_fixed_blob (void *cls, int have; const void *ret; + if ( (0 == *dst_size) && + (SQLITE_NULL == + sqlite3_column_type (result, + column)) ) + { + return GNUNET_YES; + } + if (SQLITE_BLOB != sqlite3_column_type (result, column)) @@ -459,6 +476,45 @@ GNUNET_SQ_result_spec_rsa_signature (struct GNUNET_CRYPTO_RsaSignature **sig) /** + * Extract absolute time value from a Postgres database @a result at row @a row. + * + * @param cls closure + * @param result where to extract data from + * @param column column to extract data from + * @param[in,out] dst_size where to store size of result, may be NULL + * @param[out] dst where to store the result + * @return + * #GNUNET_YES if all results could be extracted + * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL) + */ +static int +extract_abs_time (void *cls, + sqlite3_stmt *result, + unsigned int column, + size_t *dst_size, + void *dst) +{ + struct GNUNET_TIME_Absolute *u = dst; + struct GNUNET_TIME_Absolute t; + + GNUNET_assert (sizeof (uint64_t) == *dst_size); + if (SQLITE_INTEGER != + sqlite3_column_type (result, + column)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + t.abs_value_us = (uint64_t) sqlite3_column_int64 (result, + column); + if (INT64_MAX == t.abs_value_us) + t = GNUNET_TIME_UNIT_FOREVER_ABS; + *u = t; + return GNUNET_OK; +} + + +/** * Absolute time expected. * * @param[out] at where to store the result @@ -467,7 +523,14 @@ GNUNET_SQ_result_spec_rsa_signature (struct GNUNET_CRYPTO_RsaSignature **sig) struct GNUNET_SQ_ResultSpec GNUNET_SQ_result_spec_absolute_time (struct GNUNET_TIME_Absolute *at) { - return GNUNET_SQ_result_spec_uint64 (&at->abs_value_us); + struct GNUNET_SQ_ResultSpec rs = { + .conv = &extract_abs_time, + .dst = at, + .dst_size = sizeof (struct GNUNET_TIME_Absolute), + .num_params = 1 + }; + + return rs; } @@ -503,6 +566,8 @@ extract_abs_time_nbo (void *cls, } t.abs_value_us = (uint64_t) sqlite3_column_int64 (result, column); + if (INT64_MAX == t.abs_value_us) + t = GNUNET_TIME_UNIT_FOREVER_ABS; *u = GNUNET_TIME_absolute_hton (t); return GNUNET_OK; } |