AM_CLFAGS = -g
libexec_PROGRAMS = \
- gnunet-service-cadet-new \
+ gnunet-service-cadet \
$(EXP_LIBEXEC)
bin_PROGRAMS = \
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 = \
+gnunet_service_cadet_SOURCES = \
+ gnunet-service-cadet.c gnunet-service-cadet.h \
+ gnunet-service-cadet_channel.c gnunet-service-cadet_channel.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 \
+ 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/ats/libgnunetats.la \
$(top_builddir)/src/core/libgnunetcore.la \
if HAVE_TESTING
noinst_LTLIBRARIES = libgnunetcadettest.la $(noinst_LIB_EXP)
- noinst_PROGRAMS = gnunet-cadet-profiler
+# noinst_PROGRAMS = gnunet-cadet-profiler
endif
if HAVE_TESTING
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)
+#gnunet_cadet_profiler_SOURCES = \
+# gnunet-cadet-profiler.c
+#gnunet_cadet_profiler_LDADD = $(ld_cadet_test_lib)
test_cadet_local_mq_SOURCES = \
libgnunetcadettest_la_SOURCES = \
- cadet_test_lib_new.c cadet_test_lib_new.h
+ cadet_test_lib.c cadet_test_lib.h
libgnunetcadettest_la_LIBADD = \
$(top_builddir)/src/util/libgnunetutil.la \
$(top_builddir)/src/testbed/libgnunettestbed.la \
$(top_builddir)/src/statistics/libgnunetstatistics.la
test_cadet_2_forward_SOURCES = \
- test_cadet_new.c
+ test_cadet.c
test_cadet_2_forward_LDADD = $(ld_cadet_test_lib)
test_cadet_2_signal_SOURCES = \
- test_cadet_new.c
+ test_cadet.c
test_cadet_2_signal_LDADD = $(ld_cadet_test_lib)
test_cadet_2_keepalive_SOURCES = \
- test_cadet_new.c
+ test_cadet.c
test_cadet_2_keepalive_LDADD = $(ld_cadet_test_lib)
test_cadet_2_speed_SOURCES = \
- test_cadet_new.c
+ test_cadet.c
test_cadet_2_speed_LDADD = $(ld_cadet_test_lib)
test_cadet_2_speed_ack_SOURCES = \
- test_cadet_new.c
+ test_cadet.c
test_cadet_2_speed_ack_LDADD = $(ld_cadet_test_lib)
test_cadet_2_speed_backwards_SOURCES = \
- test_cadet_new.c
+ test_cadet.c
test_cadet_2_speed_backwards_LDADD = $(ld_cadet_test_lib)
test_cadet_2_speed_reliable_SOURCES = \
- test_cadet_new.c
+ test_cadet.c
test_cadet_2_speed_reliable_LDADD = $(ld_cadet_test_lib)
test_cadet_2_speed_reliable_backwards_SOURCES = \
- test_cadet_new.c
+ test_cadet.c
test_cadet_2_speed_reliable_backwards_LDADD = $(ld_cadet_test_lib)
-
test_cadet_5_forward_SOURCES = \
- test_cadet_new.c
+ test_cadet.c
test_cadet_5_forward_LDADD = $(ld_cadet_test_lib)
test_cadet_5_signal_SOURCES = \
- test_cadet_new.c
+ test_cadet.c
test_cadet_5_signal_LDADD = $(ld_cadet_test_lib)
test_cadet_5_keepalive_SOURCES = \
- test_cadet_new.c
+ test_cadet.c
test_cadet_5_keepalive_LDADD = $(ld_cadet_test_lib)
test_cadet_5_speed_SOURCES = \
- test_cadet_new.c
+ test_cadet.c
test_cadet_5_speed_LDADD = $(ld_cadet_test_lib)
test_cadet_5_speed_ack_SOURCES = \
- test_cadet_new.c
+ test_cadet.c
test_cadet_5_speed_ack_LDADD = $(ld_cadet_test_lib)
test_cadet_5_speed_backwards_SOURCES = \
- test_cadet_new.c
+ test_cadet.c
test_cadet_5_speed_backwards_LDADD = $(ld_cadet_test_lib)
test_cadet_5_speed_reliable_SOURCES = \
- test_cadet_new.c
+ test_cadet.c
test_cadet_5_speed_reliable_LDADD = $(ld_cadet_test_lib)
test_cadet_5_speed_reliable_backwards_SOURCES = \
- test_cadet_new.c
+ test_cadet.c
test_cadet_5_speed_reliable_backwards_LDADD = $(ld_cadet_test_lib)
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;
#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 ******************************/
/*
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
*/
/**
- * @author Bartlomiej Polot
* @file cadet/cadet_protocol.h
+ * @brief P2P messages used by CADET
+ * @author Bartlomiej Polot
+ * @author Christian Grothoff
*/
#ifndef CADET_PROTOCOL_H_
*/
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.
*/
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 ***********************************/
/******************************************************************************/
*/
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.
*/
};
-#endif
-
GNUNET_NETWORK_STRUCT_END
#if 0 /* keep Emacsens' auto-indent happy */
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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
+++ /dev/null
-/*
- 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 */
+++ /dev/null
-/*
- 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
+++ /dev/null
-/*
- 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 */
+++ /dev/null
-
-/*
- 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.h
- * @brief Information we track per peer.
- * @author Bartlomiej Polot
- * @author Christian Grothoff
- */
-#ifndef GNUNET_SERVICE_CADET_H
-#define GNUNET_SERVICE_CADET_H
-
-#include "gnunet_util_lib.h"
-#define NEW_CADET 1
-#include "cadet_protocol.h"
-
-/**
- * A client to the CADET service. Each client gets a unique handle.
- */
-struct CadetClient;
-
-/**
- * A peer in the GNUnet network. Each peer we care about must have one globally
- * unique such handle within this process.
- */
-struct CadetPeer;
-
-/**
- * Tunnel from us to another peer. There can only be at most one
- * tunnel per peer.
- */
-struct CadetTunnel;
-
-/**
- * Entry in the message queue of a `struct CadetTunnel`.
- */
-struct CadetTunnelQueueEntry;
-
-/**
- * A path of peer in the GNUnet network. There must only be at most
- * once such path. Paths may share disjoint prefixes, but must all
- * end at a unique suffix. Paths must also not be proper subsets of
- * other existing paths.
- */
-struct CadetPeerPath;
-
-/**
- * Entry in a peer path.
- */
-struct CadetPeerPathEntry
-{
- /**
- * DLL of paths where the same @e peer is at the same offset.
- */
- struct CadetPeerPathEntry *next;
-
- /**
- * DLL of paths where the same @e peer is at the same offset.
- */
- struct CadetPeerPathEntry *prev;
-
- /**
- * The peer at this offset of the path.
- */
- struct CadetPeer *peer;
-
- /**
- * Path this entry belongs to.
- */
- struct CadetPeerPath *path;
-
- /**
- * Connection using this path, or NULL for none.
- */
- struct CadetConnection *cc;
-
- /**
- * Path's historic score up to this point. Basically, how often did
- * we succeed or fail to use the path up to this entry in a
- * connection. Positive values indicate good experiences, negative
- * values bad experiences. Code updating the score must guard
- * against overflows.
- */
- int score;
-
-};
-
-/**
- * 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 *cc;
-
- /**
- * Tunnel this connection belongs to.
- */
- struct CadetTunnel *t;
-
- /**
- * Creation time, to keep oldest connection alive.
- */
- struct GNUNET_TIME_Absolute created;
-
- /**
- * Connection throughput, to keep fastest connection alive.
- */
- uint32_t throughput;
-
- /**
- * Is the connection currently ready for transmission?
- */
- int is_ready;
-};
-
-
-/**
- * Active path through the network (used by a tunnel). There may
- * be at most one connection per path.
- */
-struct CadetConnection;
-
-/**
- * Description of a segment of a `struct CadetConnection` at the
- * intermediate peers. Routes are basically entries in a peer's
- * routing table for forwarding traffic. At both endpoints, the
- * routes are terminated by a `struct CadetConnection`, which knows
- * the complete `struct CadetPath` that is formed by the individual
- * routes.
- */
-struct CadetRoute;
-
-/**
- * Logical end-to-end conenction between clients. There can be
- * any number of channels between clients.
- */
-struct CadetChannel;
-
-/**
- * Handle to our configuration.
- */
-extern const struct GNUNET_CONFIGURATION_Handle *cfg;
-
-/**
- * Handle to the statistics service.
- */
-extern struct GNUNET_STATISTICS_Handle *stats;
-
-/**
- * Handle to communicate with ATS.
- */
-extern struct GNUNET_ATS_ConnectivityHandle *ats_ch;
-
-/**
- * Local peer own ID.
- */
-extern struct GNUNET_PeerIdentity my_full_id;
-
-/**
- * Own private key.
- */
-extern struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
-
-/**
- * All ports clients of this peer have opened.
- */
-extern struct GNUNET_CONTAINER_MultiHashMap *open_ports;
-
-/**
- * Map from `struct GNUNET_CADET_ConnectionTunnelIdentifier`
- * hash codes to `struct CadetConnection` objects.
- */
-extern struct GNUNET_CONTAINER_MultiShortmap *connections;
-
-/**
- * Map from ports to channels where the ports were closed at the
- * time we got the inbound connection.
- * Indexed by port, contains `struct CadetChannel`.
- */
-extern struct GNUNET_CONTAINER_MultiHashMap *loose_channels;
-
-/**
- * Map from PIDs to `struct CadetPeer` entries.
- */
-extern struct GNUNET_CONTAINER_MultiPeerMap *peers;
-
-/**
- * How many messages are needed to trigger an AXOLOTL ratchet advance.
- */
-extern unsigned long long ratchet_messages;
-
-/**
- * How long until we trigger a ratched advance due to time.
- */
-extern struct GNUNET_TIME_Relative ratchet_time;
-
-/**
- * How frequently do we send KEEPALIVE messages on idle connections?
- */
-extern struct GNUNET_TIME_Relative keepalive_period;
-
-/**
- * Signal that shutdown is happening: prevent recovery measures.
- */
-extern int shutting_down;
-
-/**
- * Set to non-zero values to create random drops to test retransmissions.
- */
-extern 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);
-
-
-/**
- * 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);
-
-/**
- * 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);
-
-
-/**
- * Bind incoming channel to this client, and notify client
- * about incoming connection.
- *
- * @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);
-
-
-/**
- * Return identifier for a client as a string.
- *
- * @param c client to identify
- * @return string for debugging
- */
-const char *
-GSC_2s (struct CadetClient *c);
-
-
-#endif
+++ /dev/null
-/*
- 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 */
+++ /dev/null
-
-/*
- 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
+++ /dev/null
-/*
- 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 */
+++ /dev/null
-
-/*
- 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
+++ /dev/null
-/*
- This file is part of GNUnet.
- Copyright (C) 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_core.c
- * @brief cadet service; interaction with CORE service
- * @author Bartlomiej Polot
- * @author Christian Grothoff
- *
- * All functions in this file should use the prefix GCO (Gnunet Cadet cOre (bottom))
- *
- * TODO:
- * - 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_core_service.h"
-#include "gnunet_statistics_service.h"
-#include "cadet_protocol.h"
-
-
-#define LOG(level, ...) GNUNET_log_from(level,"cadet-cor",__VA_ARGS__)
-
-/**
- * Information we keep per direction for a route.
- */
-struct RouteDirection;
-
-
-/**
- * Set of CadetRoutes that have exactly the same number of messages
- * in their buffer. Used so we can efficiently find all of those
- * routes that have the current maximum of messages in the buffer (in
- * case we have to purge).
- */
-struct Rung
-{
-
- /**
- * Rung of RouteDirections with one more buffer entry each.
- */
- struct Rung *next;
-
- /**
- * Rung of RouteDirections with one less buffer entry each.
- */
- struct Rung *prev;
-
- /**
- * DLL of route directions with a number of buffer entries matching this rung.
- */
- struct RouteDirection *rd_head;
-
- /**
- * DLL of route directions with a number of buffer entries matching this rung.
- */
- struct RouteDirection *rd_tail;
-
- /**
- * Total number of route directions in this rung.
- */
- unsigned int num_routes;
-
- /**
- * Number of messages route directions at this rung have
- * in their buffer.
- */
- unsigned int rung_off;
-};
-
-
-/**
- * Information we keep per direction for a route.
- */
-struct RouteDirection
-{
-
- /**
- * DLL of other route directions within the same `struct Rung`.
- */
- struct RouteDirection *prev;
-
- /**
- * DLL of other route directions within the same `struct Rung`.
- */
- struct RouteDirection *next;
-
- /**
- * Rung of this route direction (matches length of the buffer DLL).
- */
- struct Rung *rung;
-
- /**
- * Head of DLL of envelopes we have in the buffer for this direction.
- */
- struct GNUNET_MQ_Envelope *env_head;
-
- /**
- * Tail of DLL of envelopes we have in the buffer for this direction.
- */
- struct GNUNET_MQ_Envelope *env_tail;
-
- /**
- * Target peer.
- */
- struct CadetPeer *hop;
-
- /**
- * Route this direction is part of.
- */
- struct CadetRoute *my_route;
-
- /**
- * Message queue manager for @e hop.
- */
- struct GCP_MessageQueueManager *mqm;
-
- /**
- * Is @e mqm currently ready for transmission?
- */
- int is_ready;
-
-};
-
-
-/**
- * Description of a segment of a `struct CadetConnection` at the
- * intermediate peers. Routes are basically entries in a peer's
- * routing table for forwarding traffic. At both endpoints, the
- * routes are terminated by a `struct CadetConnection`, which knows
- * the complete `struct CadetPath` that is formed by the individual
- * routes.
- */
-struct CadetRoute
-{
-
- /**
- * Information about the next hop on this route.
- */
- struct RouteDirection next;
-
- /**
- * Information about the previous hop on this route.
- */
- struct RouteDirection prev;
-
- /**
- * Unique identifier for the connection that uses this route.
- */
- struct GNUNET_CADET_ConnectionTunnelIdentifier cid;
-
- /**
- * When was this route last in use?
- */
- struct GNUNET_TIME_Absolute last_use;
-
- /**
- * Position of this route in the #route_heap.
- */
- struct GNUNET_CONTAINER_HeapNode *hn;
-
- /**
- * Options for the route, control buffering.
- */
- enum GNUNET_CADET_ChannelOption options;
-};
-
-
-/**
- * Handle to the CORE service.
- */
-static struct GNUNET_CORE_Handle *core;
-
-/**
- * Routes on which this peer is an intermediate.
- */
-static struct GNUNET_CONTAINER_MultiShortmap *routes;
-
-/**
- * Heap of routes, MIN-sorted by last activity.
- */
-static struct GNUNET_CONTAINER_Heap *route_heap;
-
-/**
- * Rung zero (always pointed to by #rung_head).
- */
-static struct Rung rung_zero;
-
-/**
- * DLL of rungs, with the head always point to a rung of
- * route directions with no messages in the queue.
- */
-static struct Rung *rung_head = &rung_zero;
-
-/**
- * Tail of the #rung_head DLL.
- */
-static struct Rung *rung_tail = &rung_zero;
-
-/**
- * Maximum number of concurrent routes this peer will support.
- */
-static unsigned long long max_routes;
-
-/**
- * Maximum number of envelopes we will buffer at this peer.
- */
-static unsigned long long max_buffers;
-
-/**
- * Current number of envelopes we have buffered at this peer.
- */
-static unsigned long long cur_buffers;
-
-/**
- * Task to timeout routes.
- */
-static struct GNUNET_SCHEDULER_Task *timeout_task;
-
-
-/**
- * Get the route corresponding to a hash.
- *
- * @param cid hash generated from the connection identifier
- */
-static struct CadetRoute *
-get_route (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid)
-{
- return GNUNET_CONTAINER_multishortmap_get (routes,
- &cid->connection_of_tunnel);
-}
-
-
-/**
- * Lower the rung in which @a dir is by 1.
- *
- * @param dir direction to lower in rung.
- */
-static void
-lower_rung (struct RouteDirection *dir)
-{
- struct Rung *rung = dir->rung;
- struct Rung *prev;
-
- GNUNET_CONTAINER_DLL_remove (rung->rd_head,
- rung->rd_tail,
- dir);
- prev = rung->prev;
- GNUNET_assert (NULL != prev);
- if (prev->rung_off != rung->rung_off - 1)
- {
- prev = GNUNET_new (struct Rung);
- prev->rung_off = rung->rung_off - 1;
- GNUNET_CONTAINER_DLL_insert_after (rung_head,
- rung_tail,
- rung->prev,
- prev);
- }
- GNUNET_assert (NULL != prev);
- GNUNET_CONTAINER_DLL_insert (prev->rd_head,
- prev->rd_tail,
- dir);
- dir->rung = prev;
-}
-
-
-/**
- * Discard the buffer @a env from the route direction @a dir and
- * move @a dir down a rung.
- *
- * @param dir direction that contains the @a env in the buffer
- * @param env envelope to discard
- */
-static void
-discard_buffer (struct RouteDirection *dir,
- struct GNUNET_MQ_Envelope *env)
-{
- GNUNET_MQ_dll_remove (&dir->env_head,
- &dir->env_tail,
- env);
- cur_buffers--;
- GNUNET_MQ_discard (env);
- lower_rung (dir);
- GNUNET_STATISTICS_set (stats,
- "# buffer use",
- cur_buffers,
- GNUNET_NO);
-}
-
-
-/**
- * Discard all messages from the highest rung, to make space.
- */
-static void
-discard_all_from_rung_tail ()
-{
- struct Rung *tail = rung_tail;
- struct RouteDirection *dir;
-
- while (NULL != (dir = tail->rd_head))
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Queue full due new message %s on connection %s, dropping old message\n",
- GNUNET_sh2s (&dir->my_route->cid.connection_of_tunnel));
- GNUNET_STATISTICS_update (stats,
- "# messages dropped due to full buffer",
- 1,
- GNUNET_NO);
- discard_buffer (dir,
- dir->env_head);
- }
- GNUNET_CONTAINER_DLL_remove (rung_head,
- rung_tail,
- tail);
- GNUNET_free (tail);
-}
-
-
-/**
- * We message @a msg from @a prev. Find its route by @a cid and
- * forward to the next hop. Drop and signal broken route if we do not
- * have a route.
- *
- * @param prev previous hop (sender)
- * @param cid connection identifier, tells us which route to use
- * @param msg the message to forward
- */
-static void
-route_message (struct CadetPeer *prev,
- const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
- const struct GNUNET_MessageHeader *msg)
-{
- struct CadetRoute *route;
- struct RouteDirection *dir;
- struct Rung *rung;
- struct Rung *nxt;
- struct GNUNET_MQ_Envelope *env;
-
- route = get_route (cid);
- if (NULL == route)
- {
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_CADET_ConnectionBrokenMessage *bm;
-
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Failed to route message of type %u from %s on connection %s: no route\n",
- ntohs (msg->type),
- GCP_2s (prev),
- GNUNET_sh2s (&cid->connection_of_tunnel));
- switch (ntohs (msg->type))
- {
- case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY:
- case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN:
- /* No need to respond to these! */
- return;
- }
- env = GNUNET_MQ_msg (bm,
- GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN);
- bm->cid = *cid;
- bm->peer1 = my_full_id;
- GCP_send_ooo (prev,
- env);
- return;
- }
- route->last_use = GNUNET_TIME_absolute_get ();
- GNUNET_CONTAINER_heap_update_cost (route->hn,
- route->last_use.abs_value_us);
- dir = (prev == route->prev.hop) ? &route->next : &route->prev;
- if (GNUNET_YES == dir->is_ready)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Routing message of type %u from %s to %s on connection %s\n",
- ntohs (msg->type),
- GCP_2s (prev),
- GNUNET_i2s (GCP_get_id (dir->hop)),
- GNUNET_sh2s (&cid->connection_of_tunnel));
- dir->is_ready = GNUNET_NO;
- GCP_send (dir->mqm,
- GNUNET_MQ_msg_copy (msg));
- return;
- }
- /* Check if buffering is disallowed, and if so, make sure we only queue
- one message per direction. */
- if ( (0 != (route->options & GNUNET_CADET_OPTION_NOBUFFER)) &&
- (NULL != dir->env_head) )
- discard_buffer (dir,
- dir->env_head);
- rung = dir->rung;
- if (cur_buffers == max_buffers)
- {
- /* Need to make room. */
- if (NULL != rung->next)
- {
- /* Easy case, drop messages from route directions in highest rung */
- discard_all_from_rung_tail ();
- }
- else
- {
- /* We are in the highest rung, drop our own! */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Queue full due new message %s on connection %s, dropping old message\n",
- GNUNET_sh2s (&dir->my_route->cid.connection_of_tunnel));
- GNUNET_STATISTICS_update (stats,
- "# messages dropped due to full buffer",
- 1,
- GNUNET_NO);
- discard_buffer (dir,
- dir->env_head);
- rung = dir->rung;
- }
- }
- /* remove 'dir' from current rung */
- GNUNET_CONTAINER_DLL_remove (rung->rd_head,
- rung->rd_tail,
- dir);
- /* make 'nxt' point to the next higher rung, creat if necessary */
- nxt = rung->next;
- if ( (NULL == nxt) ||
- (rung->rung_off + 1 != nxt->rung_off) )
- {
- nxt = GNUNET_new (struct Rung);
- nxt->rung_off = rung->rung_off + 1;
- GNUNET_CONTAINER_DLL_insert_after (rung_head,
- rung_tail,
- rung,
- nxt);
- }
- /* insert 'dir' into next higher rung */
- GNUNET_CONTAINER_DLL_insert (nxt->rd_head,
- nxt->rd_tail,
- dir);
- dir->rung = nxt;
-
- /* add message into 'dir' buffer */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Queueing new message of type %u from %s to %s on connection %s\n",
- ntohs (msg->type),
- GCP_2s (prev),
- GNUNET_i2s (GCP_get_id (dir->hop)),
- GNUNET_sh2s (&cid->connection_of_tunnel));
- env = GNUNET_MQ_msg_copy (msg);
- GNUNET_MQ_dll_insert_tail (&dir->env_head,
- &dir->env_tail,
- env);
- cur_buffers++;
- GNUNET_STATISTICS_set (stats,
- "# buffer use",
- cur_buffers,
- GNUNET_NO);
- /* Clean up 'rung' if now empty (and not head) */
- if ( (NULL == rung->rd_head) &&
- (rung != rung_head) )
- {
- GNUNET_CONTAINER_DLL_remove (rung_head,
- rung_tail,
- rung);
- GNUNET_free (rung);
- }
-}
-
-
-/**
- * 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_connection_create (void *cls,
- const struct GNUNET_CADET_ConnectionCreateMessage *msg)
-{
- uint16_t size = ntohs (msg->header.size) - sizeof (*msg);
-
- if (0 != (size % sizeof (struct GNUNET_PeerIdentity)))
- {
- GNUNET_break_op (0);
- return GNUNET_NO;
- }
- return GNUNET_YES;
-}
-
-
-/**
- * Free internal data of a route direction.
- *
- * @param dir direction to destroy (do NOT free memory of 'dir' itself)
- */
-static void
-destroy_direction (struct RouteDirection *dir)
-{
- struct GNUNET_MQ_Envelope *env;
-
- while (NULL != (env = dir->env_head))
- {
- GNUNET_STATISTICS_update (stats,
- "# messages dropped due to route destruction",
- 1,
- GNUNET_NO);
- discard_buffer (dir,
- env);
- }
- if (NULL != dir->mqm)
- {
- GCP_request_mq_cancel (dir->mqm,
- NULL);
- dir->mqm = NULL;
- }
- GNUNET_CONTAINER_DLL_remove (rung_head->rd_head,
- rung_head->rd_tail,
- dir);
-}
-
-
-/**
- * Destroy our state for @a route.
- *
- * @param route route to destroy
- */
-static void
-destroy_route (struct CadetRoute *route)
-{
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Destroying route from %s to %s of connection %s\n",
- GNUNET_i2s (GCP_get_id (route->prev.hop)),
- GNUNET_i2s2 (GCP_get_id (route->next.hop)),
- GNUNET_sh2s (&route->cid.connection_of_tunnel));
- GNUNET_assert (route ==
- GNUNET_CONTAINER_heap_remove_node (route->hn));
- GNUNET_assert (GNUNET_YES ==
- GNUNET_CONTAINER_multishortmap_remove (routes,
- &route->cid.connection_of_tunnel,
- route));
- GNUNET_STATISTICS_set (stats,
- "# routes",
- GNUNET_CONTAINER_multishortmap_size (routes),
- GNUNET_NO);
- destroy_direction (&route->prev);
- destroy_direction (&route->next);
- GNUNET_free (route);
-}
-
-
-/**
- * Send message that a route is broken between @a peer1 and @a peer2.
- *
- * @param target where to send the message
- * @param cid connection identifier to use
- * @param peer1 one of the peers where a link is broken
- * @param peer2 another one of the peers where a link is broken
- */
-static void
-send_broken (struct RouteDirection *target,
- const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
- const struct GNUNET_PeerIdentity *peer1,
- const struct GNUNET_PeerIdentity *peer2)
-{
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_CADET_ConnectionBrokenMessage *bm;
-
- if (NULL == target->mqm)
- return; /* Can't send notification, connection is down! */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Notifying %s about BROKEN route at %s-%s of connection %s\n",
- GCP_2s (target->hop),
- GNUNET_i2s (peer1),
- GNUNET_i2s2 (peer2),
- GNUNET_sh2s (&cid->connection_of_tunnel));
-
- env = GNUNET_MQ_msg (bm,
- GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN);
- bm->cid = *cid;
- if (NULL != peer1)
- bm->peer1 = *peer1;
- if (NULL != peer2)
- bm->peer2 = *peer2;
- GCP_request_mq_cancel (target->mqm,
- env);
- target->mqm = NULL;
-}
-
-
-/**
- * Function called to check if any routes have timed out, and if
- * so, to clean them up. Finally, schedules itself again at the
- * earliest time where there might be more work.
- *
- * @param cls NULL
- */
-static void
-timeout_cb (void *cls)
-{
- struct CadetRoute *r;
- struct GNUNET_TIME_Relative linger;
- struct GNUNET_TIME_Absolute exp;
-
- timeout_task = NULL;
- linger = GNUNET_TIME_relative_multiply (keepalive_period,
- 3);
- while (NULL != (r = GNUNET_CONTAINER_heap_peek (route_heap)))
- {
- exp = GNUNET_TIME_absolute_add (r->last_use,
- linger);
- if (0 != GNUNET_TIME_absolute_get_duration (exp).rel_value_us)
- {
- /* Route not yet timed out, wait until it does. */
- timeout_task = GNUNET_SCHEDULER_add_at (exp,
- &timeout_cb,
- NULL);
- return;
- }
- send_broken (&r->prev,
- &r->cid,
- NULL,
- NULL);
- send_broken (&r->next,
- &r->cid,
- NULL,
- NULL);
- destroy_route (r);
- }
- /* No more routes left, so no need for a #timeout_task */
-}
-
-
-/**
- * Function called when the message queue to the previous hop
- * becomes available/unavailable. We expect this function to
- * be called immediately when we register, and then again
- * later if the connection ever goes down.
- *
- * @param cls the `struct RouteDirection`
- * @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
-dir_ready_cb (void *cls,
- int ready)
-{
- struct RouteDirection *dir = cls;
- struct CadetRoute *route = dir->my_route;
- struct RouteDirection *odir;
-
- if (GNUNET_YES == ready)
- {
- struct GNUNET_MQ_Envelope *env;
-
- dir->is_ready = GNUNET_YES;
- if (NULL != (env = dir->env_head))
- {
- GNUNET_MQ_dll_remove (&dir->env_head,
- &dir->env_tail,
- env);
- cur_buffers--;
- GNUNET_STATISTICS_set (stats,
- "# buffer use",
- cur_buffers,
- GNUNET_NO);
- lower_rung (dir);
- dir->is_ready = GNUNET_NO;
- GCP_send (dir->mqm,
- env);
- }
- return;
- }
- odir = (dir == &route->next) ? &route->prev : &route->next;
- send_broken (&route->next,
- &route->cid,
- GCP_get_id (odir->hop),
- &my_full_id);
- destroy_route (route);
-}
-
-
-/**
- * Initialize one of the directions of a route.
- *
- * @param route route the direction belongs to
- * @param dir direction to initialize
- * @param hop next hop on in the @a dir
- */
-static void
-dir_init (struct RouteDirection *dir,
- struct CadetRoute *route,
- struct CadetPeer *hop)
-{
- dir->hop = hop;
- dir->my_route = route;
- dir->mqm = GCP_request_mq (hop,
- &dir_ready_cb,
- dir);
- GNUNET_CONTAINER_DLL_insert (rung_head->rd_head,
- rung_head->rd_tail,
- dir);
- dir->rung = rung_head;
- GNUNET_assert (GNUNET_YES == dir->is_ready);
-}
-
-
-/**
- * We could not create the desired route. Send a
- * #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN
- * message to @a target.
- *
- * @param target who should receive the message
- * @param cid identifier of the connection/route that failed
- * @param failure_at neighbour with which we failed to route,
- * or NULL.
- */
-static void
-send_broken_without_mqm (struct CadetPeer *target,
- const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
- const struct GNUNET_PeerIdentity *failure_at)
-{
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_CADET_ConnectionBrokenMessage *bm;
-
- env = GNUNET_MQ_msg (bm,
- GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN);
- bm->cid = *cid;
- bm->peer1 = my_full_id;
- if (NULL != failure_at)
- bm->peer2 = *failure_at;
- GCP_send_ooo (target,
- env);
-}
-
-
-/**
- * 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_connection_create (void *cls,
- const struct GNUNET_CADET_ConnectionCreateMessage *msg)
-{
- struct CadetPeer *sender = cls;
- struct CadetPeer *next;
- const struct GNUNET_PeerIdentity *pids = (const struct GNUNET_PeerIdentity *) &msg[1];
- struct CadetRoute *route;
- uint16_t size = ntohs (msg->header.size) - sizeof (*msg);
- unsigned int path_length;
- unsigned int off;
- enum GNUNET_CADET_ChannelOption options;
-
- options = (enum GNUNET_CADET_ChannelOption) ntohl (msg->options);
- path_length = size / sizeof (struct GNUNET_PeerIdentity);
- /* Initiator is at offset 0. */
- for (off=1;off<path_length;off++)
- if (0 == memcmp (&my_full_id,
- &pids[off],
- sizeof (struct GNUNET_PeerIdentity)))
- break;
- if (off == path_length)
- {
- /* We are not on the path, bogus request */
- GNUNET_break_op (0);
- return;
- }
- /* Check previous hop */
- if (sender != GCP_get (&pids[off - 1],
- GNUNET_NO))
- {
- /* sender is not on the path, not allowed */
- GNUNET_break_op (0);
- return;
- }
- if (NULL !=
- get_route (&msg->cid))
- {
- /* Duplicate CREATE, pass it on, previous one might have been lost! */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Passing on duplicate CADET_CONNECTION_CREATE message on connection %s\n",
- GNUNET_sh2s (&msg->cid.connection_of_tunnel));
- route_message (sender,
- &msg->cid,
- &msg->header);
- return;
- }
- if (off == path_length - 1)
- {
- /* We are the destination, create connection */
- struct CadetConnection *cc;
- struct CadetPeerPath *path;
- struct CadetPeer *origin;
-
- cc = GCC_lookup (&msg->cid);
- if (NULL != cc)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received duplicate CADET_CONNECTION_CREATE message on connection %s\n",
- GNUNET_sh2s (&msg->cid.connection_of_tunnel));
- GCC_handle_duplicate_create (cc);
- return;
- }
-
- origin = GCP_get (&pids[0],
- GNUNET_YES);
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received CADET_CONNECTION_CREATE message from %s for connection %s, building inverse path\n",
- GCP_2s (origin),
- GNUNET_sh2s (&msg->cid.connection_of_tunnel));
- path = GCPP_get_path_from_route (path_length - 1,
- pids);
- if (GNUNET_OK !=
- GCT_add_inbound_connection (GCP_get_tunnel (origin,
- GNUNET_YES),
- &msg->cid,
- (enum GNUNET_CADET_ChannelOption) ntohl (msg->options),
- path))
- {
- /* Send back BROKEN: duplicate connection on the same path,
- we will use the other one. */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received CADET_CONNECTION_CREATE from %s for %s, but %s already has a connection. Sending BROKEN\n",
- GCP_2s (sender),
- GNUNET_sh2s (&msg->cid.connection_of_tunnel),
- GCPP_2s (path));
- send_broken_without_mqm (sender,
- &msg->cid,
- NULL);
- return;
- }
- return;
- }
- /* We are merely a hop on the way, check if we can support the route */
- next = GCP_get (&pids[off + 1],
- GNUNET_NO);
- if ( (NULL == next) ||
- (GNUNET_NO == GCP_has_core_connection (next)) )
- {
- /* unworkable, send back BROKEN notification */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received CADET_CONNECTION_CREATE from %s for %s. Next hop %s:%u is down. Sending BROKEN\n",
- GCP_2s (sender),
- GNUNET_sh2s (&msg->cid.connection_of_tunnel),
- GNUNET_i2s (&pids[off + 1]),
- off + 1);
- send_broken_without_mqm (sender,
- &msg->cid,
- &pids[off + 1]);
- return;
- }
- if (max_routes <= GNUNET_CONTAINER_multishortmap_size (routes))
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received CADET_CONNECTION_CREATE from %s for %s. We have reached our route limit. Sending BROKEN\n",
- GCP_2s (sender),
- GNUNET_sh2s (&msg->cid.connection_of_tunnel));
- send_broken_without_mqm (sender,
- &msg->cid,
- &pids[off - 1]);
- return;
- }
-
- /* Workable route, create routing entry */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received CADET_CONNECTION_CREATE from %s for %s. Next hop %s:%u is up. Creating route\n",
- GCP_2s (sender),
- GNUNET_sh2s (&msg->cid.connection_of_tunnel),
- GNUNET_i2s (&pids[off + 1]),
- off + 1);
- route = GNUNET_new (struct CadetRoute);
- route->options = options;
- route->cid = msg->cid;
- route->last_use = GNUNET_TIME_absolute_get ();
- dir_init (&route->prev,
- route,
- sender);
- dir_init (&route->next,
- route,
- next);
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multishortmap_put (routes,
- &route->cid.connection_of_tunnel,
- route,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- GNUNET_STATISTICS_set (stats,
- "# routes",
- GNUNET_CONTAINER_multishortmap_size (routes),
- GNUNET_NO);
- route->hn = GNUNET_CONTAINER_heap_insert (route_heap,
- route,
- route->last_use.abs_value_us);
- if (NULL == timeout_task)
- timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (keepalive_period,
- 3),
- &timeout_cb,
- NULL);
-}
-
-
-/**
- * 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_connection_create_ack (void *cls,
- const struct GNUNET_CADET_ConnectionCreateAckMessage *msg)
-{
- struct CadetPeer *peer = cls;
- struct CadetConnection *cc;
-
- /* First, check if ACK belongs to a connection that ends here. */
- cc = GCC_lookup (&msg->cid);
- if (NULL != cc)
- {
- /* verify ACK came from the right direction */
- struct CadetPeerPath *path = GCC_get_path (cc);
-
- if (peer !=
- GCPP_get_peer_at_offset (path,
- 0))
- {
- /* received ACK from unexpected direction, ignore! */
- GNUNET_break_op (0);
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received CONNECTION_CREATE_ACK for connection %s.\n",
- GNUNET_sh2s (&msg->cid.connection_of_tunnel));
- GCC_handle_connection_create_ack (cc);
- return;
- }
-
- /* We're just an intermediary peer, route the message along its path */
- route_message (peer,
- &msg->cid,
- &msg->header);
-}
-
-
-/**
- * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN
- *
- * @param cls Closure (CadetPeer for neighbor that sent the message).
- * @param msg Message itself.
- * @deprecated duplicate logic with #handle_destroy(); dedup!
- */
-static void
-handle_connection_broken (void *cls,
- const struct GNUNET_CADET_ConnectionBrokenMessage *msg)
-{
- struct CadetPeer *peer = cls;
- struct CadetConnection *cc;
- struct CadetRoute *route;
-
- /* First, check if message belongs to a connection that ends here. */
- cc = GCC_lookup (&msg->cid);
- if (NULL != cc)
- {
- /* verify message came from the right direction */
- struct CadetPeerPath *path = GCC_get_path (cc);
-
- if (peer !=
- GCPP_get_peer_at_offset (path,
- 0))
- {
- /* received message from unexpected direction, ignore! */
- GNUNET_break_op (0);
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received CONNECTION_BROKEN for connection %s. Destroying it.\n",
- GNUNET_sh2s (&msg->cid.connection_of_tunnel));
- GCC_destroy_without_core (cc);
-
- /* FIXME: also destroy the path up to the specified link! */
- return;
- }
-
- /* We're just an intermediary peer, route the message along its path */
- route_message (peer,
- &msg->cid,
- &msg->header);
- route = get_route (&msg->cid);
- if (NULL != route)
- destroy_route (route);
- /* FIXME: also destroy paths we MAY have up to the specified link! */
-}
-
-
-/**
- * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY
- *
- * @param cls Closure (CadetPeer for neighbor that sent the message).
- * @param msg Message itself.
- */
-static void
-handle_connection_destroy (void *cls,
- const struct GNUNET_CADET_ConnectionDestroyMessage *msg)
-{
- struct CadetPeer *peer = cls;
- struct CadetConnection *cc;
- struct CadetRoute *route;
-
- /* First, check if message belongs to a connection that ends here. */
- cc = GCC_lookup (&msg->cid);
- if (NULL != cc)
- {
- /* verify message came from the right direction */
- struct CadetPeerPath *path = GCC_get_path (cc);
-
- if (peer !=
- GCPP_get_peer_at_offset (path,
- 0))
- {
- /* received message from unexpected direction, ignore! */
- GNUNET_break_op (0);
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received CONNECTION_DESTROY for connection %s. Destroying connection.\n",
- GNUNET_sh2s (&msg->cid.connection_of_tunnel));
-
- GCC_destroy_without_core (cc);
- return;
- }
-
- /* We're just an intermediary peer, route the message along its path */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received CONNECTION_DESTROY for connection %s. Destroying route.\n",
- GNUNET_sh2s (&msg->cid.connection_of_tunnel));
- route_message (peer,
- &msg->cid,
- &msg->header);
- route = get_route (&msg->cid);
- if (NULL != route)
- destroy_route (route);
-}
-
-
-/**
- * Handle for #GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX
- *
- * @param cls Closure (CadetPeer for neighbor that sent the message).
- * @param msg Message itself.
- */
-static void
-handle_tunnel_kx (void *cls,
- const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg)
-{
- struct CadetPeer *peer = cls;
- struct CadetConnection *cc;
-
- /* First, check if message belongs to a connection that ends here. */
- cc = GCC_lookup (&msg->cid);
- if (NULL != cc)
- {
- /* verify message came from the right direction */
- struct CadetPeerPath *path = GCC_get_path (cc);
-
- if (peer !=
- GCPP_get_peer_at_offset (path,
- 0))
- {
- /* received message from unexpected direction, ignore! */
- GNUNET_break_op (0);
- return;
- }
- GCC_handle_kx (cc,
- msg);
- return;
- }
-
- /* We're just an intermediary peer, route the message along its path */
- route_message (peer,
- &msg->cid,
- &msg->header);
-}
-
-
-/**
- * Handle for #GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX_AUTH
- *
- * @param cls Closure (CadetPeer for neighbor that sent the message).
- * @param msg Message itself.
- */
-static void
-handle_tunnel_kx_auth (void *cls,
- const struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg)
-{
- struct CadetPeer *peer = cls;
- struct CadetConnection *cc;
-
- /* First, check if message belongs to a connection that ends here. */
- cc = GCC_lookup (&msg->kx.cid);
- if (NULL != cc)
- {
- /* verify message came from the right direction */
- struct CadetPeerPath *path = GCC_get_path (cc);
-
- if (peer !=
- GCPP_get_peer_at_offset (path,
- 0))
- {
- /* received message from unexpected direction, ignore! */
- GNUNET_break_op (0);
- return;
- }
- GCC_handle_kx_auth (cc,
- msg);
- return;
- }
-
- /* We're just an intermediary peer, route the message along its path */
- route_message (peer,
- &msg->kx.cid,
- &msg->kx.header);
-}
-
-
-/**
- * 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_tunnel_encrypted (void *cls,
- const struct GNUNET_CADET_TunnelEncryptedMessage *msg)
-{
- return GNUNET_YES;
-}
-
-
-/**
- * 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_tunnel_encrypted (void *cls,
- const struct GNUNET_CADET_TunnelEncryptedMessage *msg)
-{
- struct CadetPeer *peer = cls;
- struct CadetConnection *cc;
-
- /* First, check if message belongs to a connection that ends here. */
- cc = GCC_lookup (&msg->cid);
- if (NULL != cc)
- {
- /* verify message came from the right direction */
- struct CadetPeerPath *path = GCC_get_path (cc);
-
- if (peer !=
- GCPP_get_peer_at_offset (path,
- 0))
- {
- /* received message from unexpected direction, ignore! */
- GNUNET_break_op (0);
- return;
- }
- GCC_handle_encrypted (cc,
- msg);
- return;
- }
- /* We're just an intermediary peer, route the message along its path */
- route_message (peer,
- &msg->cid,
- &msg->header);
-}
-
-
-/**
- * Function called after #GNUNET_CORE_connect has succeeded (or failed
- * for good). Note that the private key of the peer is intentionally
- * not exposed here; if you need it, your process should try to read
- * the private key file directly (which should work if you are
- * authorized...). Implementations of this function must not call
- * #GNUNET_CORE_disconnect (other than by scheduling a new task to
- * do this later).
- *
- * @param cls closure
- * @param my_identity ID of this peer, NULL if we failed
- */
-static void
-core_init_cb (void *cls,
- const struct GNUNET_PeerIdentity *my_identity)
-{
- if (NULL == my_identity)
- {
- GNUNET_break (0);
- return;
- }
- GNUNET_break (0 ==
- memcmp (my_identity,
- &my_full_id,
- sizeof (struct GNUNET_PeerIdentity)));
-}
-
-
-/**
- * Method called whenever a given peer connects.
- *
- * @param cls closure
- * @param peer peer identity this notification is about
- */
-static void *
-core_connect_cb (void *cls,
- const struct GNUNET_PeerIdentity *peer,
- struct GNUNET_MQ_Handle *mq)
-{
- struct CadetPeer *cp;
-
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "CORE connection to peer %s was established.\n",
- GNUNET_i2s (peer));
- cp = GCP_get (peer,
- GNUNET_YES);
- GCP_set_mq (cp,
- mq);
- return cp;
-}
-
-
-/**
- * Method called whenever a peer disconnects.
- *
- * @param cls closure
- * @param peer peer identity this notification is about
- */
-static void
-core_disconnect_cb (void *cls,
- const struct GNUNET_PeerIdentity *peer,
- void *peer_cls)
-{
- struct CadetPeer *cp = peer_cls;
-
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "CORE connection to peer %s went down.\n",
- GNUNET_i2s (peer));
- GCP_set_mq (cp,
- NULL);
-}
-
-
-/**
- * Initialize the CORE subsystem.
- *
- * @param c Configuration.
- */
-void
-GCO_init (const struct GNUNET_CONFIGURATION_Handle *c)
-{
- struct GNUNET_MQ_MessageHandler handlers[] = {
- GNUNET_MQ_hd_var_size (connection_create,
- GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE,
- struct GNUNET_CADET_ConnectionCreateMessage,
- NULL),
- GNUNET_MQ_hd_fixed_size (connection_create_ack,
- GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK,
- struct GNUNET_CADET_ConnectionCreateAckMessage,
- NULL),
- GNUNET_MQ_hd_fixed_size (connection_broken,
- GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN,
- struct GNUNET_CADET_ConnectionBrokenMessage,
- NULL),
- GNUNET_MQ_hd_fixed_size (connection_destroy,
- GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY,
- struct GNUNET_CADET_ConnectionDestroyMessage,
- NULL),
- GNUNET_MQ_hd_fixed_size (tunnel_kx,
- GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX,
- struct GNUNET_CADET_TunnelKeyExchangeMessage,
- NULL),
- GNUNET_MQ_hd_fixed_size (tunnel_kx_auth,
- GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX_AUTH,
- struct GNUNET_CADET_TunnelKeyExchangeAuthMessage,
- NULL),
- GNUNET_MQ_hd_var_size (tunnel_encrypted,
- GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED,
- struct GNUNET_CADET_TunnelEncryptedMessage,
- NULL),
- GNUNET_MQ_handler_end ()
- };
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_number (c,
- "CADET",
- "MAX_ROUTES",
- &max_routes))
- max_routes = 5000;
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_number (c,
- "CADET",
- "MAX_MSGS_QUEUE",
- &max_buffers))
- max_buffers = 10000;
- routes = GNUNET_CONTAINER_multishortmap_create (1024,
- GNUNET_NO);
- route_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
- core = GNUNET_CORE_connect (c,
- NULL,
- &core_init_cb,
- &core_connect_cb,
- &core_disconnect_cb,
- handlers);
-}
-
-
-/**
- * Shut down the CORE subsystem.
- */
-void
-GCO_shutdown ()
-{
- if (NULL != core)
- {
- GNUNET_CORE_disconnect (core);
- core = NULL;
- }
- GNUNET_assert (0 == GNUNET_CONTAINER_multishortmap_size (routes));
- GNUNET_CONTAINER_multishortmap_destroy (routes);
- routes = NULL;
- GNUNET_CONTAINER_heap_destroy (route_heap);
- route_heap = NULL;
- if (NULL != timeout_task)
- {
- GNUNET_SCHEDULER_cancel (timeout_task);
- timeout_task = NULL;
- }
-}
-
-/* end of gnunet-cadet-service_core.c */
+++ /dev/null
-/*
- This file is part of GNUnet.
- Copyright (C) 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_core.h
- * @brief cadet service; interaction with CORE service
- * @author Bartlomiej Polot
- * @author Christian Grothoff
- *
- * All functions in this file should use the prefix GCO (Gnunet Cadet cOre (bottom))
- */
-
-#ifndef GNUNET_SERVICE_CADET_CORE_H
-#define GNUNET_SERVICE_CADET_CORE_H
-
-#ifdef __cplusplus
-extern "C"
-{
-#if 0 /* keep Emacsens' auto-indent happy */
-}
-#endif
-#endif
-
-#include "gnunet_util_lib.h"
-
-
-/**
- * Initialize the CORE subsystem.
- *
- * @param c Configuration.
- */
-void
-GCO_init (const struct GNUNET_CONFIGURATION_Handle *c);
-
-
-/**
- * Shut down the CORE subsystem.
- */
-void
-GCO_shutdown (void);
-
-
-#if 0 /* keep Emacsens' auto-indent happy */
-{
-#endif
-#ifdef __cplusplus
-}
-#endif
-
-/* ifndef GNUNET_CADET_SERVICE_CORE_H */
-#endif
-/* end of gnunet-cadet-service_core.h */
+++ /dev/null
-/*
- 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 */
+++ /dev/null
-/*
- 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 */
+++ /dev/null
-/*
- 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 */
+++ /dev/null
-/*
- 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 */
+++ /dev/null
-/*
- 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_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"
-
-
-#define LOG(level, ...) GNUNET_log_from(level,"cadet-pat",__VA_ARGS__)
-
-
-/**
- * Information regarding a possible path to reach a peer.
- */
-struct CadetPeerPath
-{
-
- /**
- * Array of all the peers on the path. If @e hn is non-NULL, the
- * last one is our owner.
- */
- struct CadetPeerPathEntry **entries;
-
- /**
- * Node of this path in the owner's heap. Used to update our position
- * in the heap whenever our @e desirability changes.
- */
- struct GNUNET_CONTAINER_HeapNode *hn;
-
- /**
- * Desirability of the path. How unique is it for the various peers
- * on it?
- */
- GNUNET_CONTAINER_HeapCostType desirability;
-
- /**
- * Length of the @e entries array.
- */
- unsigned int entries_length;
-
-};
-
-
-/**
- * Calculate the path's desirability score.
- *
- * @param path path to calculate the score for
- */
-static void
-recalculate_path_desirability (struct CadetPeerPath *path)
-{
- double result = 0.0;
-
- for (unsigned int i=0;i<path->entries_length;i++)
- {
- struct CadetPeer *cp = path->entries[i]->peer;
-
- result += GCP_get_desirability_of_path (cp,
- i);
- }
- path->desirability = (GNUNET_CONTAINER_HeapCostType) result;
-}
-
-
-/**
- * Return how much we like keeping the path. This is an aggregate
- * score based on various factors, including the age of the path
- * (older == better), and the value of this path to all of its ajacent
- * peers. For example, long paths that end at a peer that we have no
- * shorter way to reach are very desirable, while long paths that end
- * at a peer for which we have a shorter way as well are much less
- * desirable. Higher values indicate more valuable paths. The
- * returned value should be used to decide which paths to remember.
- *
- * @param path path to return the length for
- * @return desirability of the path, larger is more desirable
- */
-GNUNET_CONTAINER_HeapCostType
-GCPP_get_desirability (const struct CadetPeerPath *path)
-{
- return path->desirability;
-}
-
-
-/**
- * Return connection to @a destination using @a path, or return
- * NULL if no such connection exists.
- *
- * @param path path to traverse
- * @param destination destination node to get to, must be on path
- * @param off offset of @a destination on @a path
- * @return NULL if we have no existing connection
- * otherwise connection from us to @a destination via @a path
- */
-struct CadetConnection *
-GCPP_get_connection (struct CadetPeerPath *path,
- struct CadetPeer *destination,
- unsigned int off)
-{
- struct CadetPeerPathEntry *entry;
-
- GNUNET_assert (off < path->entries_length);
- entry = path->entries[off];
- GNUNET_assert (entry->peer == destination);
- return entry->cc;
-}
-
-
-/**
- * Notify @a path that it is used for connection @a cc
- * which ends at the path's offset @a off.
- *
- * @param path the path to remember the @a cc
- * @param off the offset where the @a cc ends
- * @param cc the connection to remember
- */
-void
-GCPP_add_connection (struct CadetPeerPath *path,
- unsigned int off,
- struct CadetConnection *cc)
-{
- struct CadetPeerPathEntry *entry;
-
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Adding connection %s to path %s at offset %u\n",
- GCC_2s (cc),
- GCPP_2s (path),
- off);
- GNUNET_assert (off < path->entries_length);
- entry = path->entries[off];
- GNUNET_assert (NULL == entry->cc);
- GNUNET_assert (NULL != cc);
- entry->cc = cc;
-}
-
-
-
-/**
- * Notify @a path that it is no longer used for connection @a cc which
- * ended at the path's offset @a off.
- *
- * @param path the path to forget the @a cc
- * @param off the offset where the @a cc ended
- * @param cc the connection to forget
- */
-void
-GCPP_del_connection (struct CadetPeerPath *path,
- unsigned int off,
- struct CadetConnection *cc)
-{
- struct CadetPeerPathEntry *entry;
-
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Removing connection %s to path %s at offset %u\n",
- GCC_2s (cc),
- GCPP_2s (path),
- off);
- GNUNET_assert (off < path->entries_length);
- entry = path->entries[off];
- GNUNET_assert (cc == entry->cc);
- entry->cc = NULL;
-}
-
-
-/**
- * This path is no longer needed, free resources.
- *
- * @param path path resources to free
- */
-static void
-path_destroy (struct CadetPeerPath *path)
-{
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Destroying path %s\n",
- GCPP_2s (path));
- for (unsigned int i=0;i<path->entries_length;i++)
- {
- struct CadetPeerPathEntry *entry = path->entries[i];
-
- if (NULL != entry->cc)
- {
- struct CadetTConnection *ct;
-
- ct = GCC_get_ct (entry->cc);
- if (NULL != ct)
- GCT_connection_lost (ct);
- GCC_destroy_without_tunnel (entry->cc);
- }
- GNUNET_free (entry);
- }
- GNUNET_free (path->entries);
- GNUNET_free (path);
-}
-
-
-/**
- * The owning peer of this path is no longer interested in maintaining
- * it, so the path should be discarded or shortened (in case a
- * previous peer on the path finds the path desirable).
- *
- * @param path the path that is being released
- */
-void
-GCPP_release (struct CadetPeerPath *path)
-{
- struct CadetPeerPathEntry *entry;
- int force;
-
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Owner releases path %s\n",
- GCPP_2s (path));
- path->hn = NULL;
- entry = path->entries[path->entries_length - 1];
- GNUNET_assert (path == entry->path);
- while (1)
- {
- /* cut 'off' end of path */
- GNUNET_assert (NULL == entry->cc);
- GCP_path_entry_remove (entry->peer,
- entry,
- path->entries_length - 1);
- path->entries_length--; /* We don't bother shrinking the 'entries' array,
- as it's probably not worth it. */
- GNUNET_free (entry);
- if (0 == path->entries_length)
- break; /* the end */
-
- /* see if new peer at the end likes this path any better */
- entry = path->entries[path->entries_length - 1];
- GNUNET_assert (path == entry->path);
- force = (NULL == entry->cc) ? GNUNET_NO : GNUNET_YES;
- path->hn = GCP_attach_path (entry->peer,
- path,
- path->entries_length - 1,
- force);
- if (NULL != path->hn)
- return; /* yep, got attached, we are done. */
- GNUNET_assert (GNUNET_NO == force);
- }
-
- /* nobody wants us, discard the path */
- path_destroy (path);
-}
-
-
-/**
- * Updates the score for an entry on the path based
- * on our experiences with using @a path.
- *
- * @param path the path to update
- * @param off offset of the entry to update
- * @param delta change in the score to apply
- */
-void
-GCPP_update_score (struct CadetPeerPath *path,
- unsigned int off,
- int delta)
-{
- struct CadetPeerPathEntry *entry;
-
- GNUNET_assert (off < path->entries_length);
- entry = path->entries[off];
-
- /* Add delta, with checks for overflows */
- if (delta >= 0)
- {
- if (delta + entry->score < entry->score)
- entry->score = INT_MAX;
- else
- entry->score += delta;
- }
- else
- {
- if (delta + entry->score > entry->score)
- entry->score = INT_MIN;
- else
- entry->score += delta;
- }
- recalculate_path_desirability (path);
-}
-
-
-/**
- * Closure for #find_peer_at() and #check_match().
- */
-struct CheckMatchContext
-{
-
- /**
- * Set to a matching path, if any.
- */
- struct CadetPeerPath *match;
-
- /**
- * Array the combined paths.
- */
- struct CadetPeer **cpath;
-
- /**
- * How long is the @e cpath array?
- */
- unsigned int cpath_length;
-
-};
-
-
-/**
- * Check if the given path is identical on all of the
- * hops until @a off, and not longer than @a off. If the
- * @a path matches, store it in `match`.
- *
- * @param cls the `struct CheckMatchContext` to check against
- * @param path the path to check
- * @param off offset to check at
- * @return #GNUNET_YES (continue to iterate), or if found #GNUNET_NO
- */
-static int
-check_match (void *cls,
- struct CadetPeerPath *path,
- unsigned int off)
-{
- struct CheckMatchContext *cm_ctx = cls;
-
- GNUNET_assert (path->entries_length > off);
- if ( (path->entries_length != off + 1) &&
- (off + 1 != cm_ctx->cpath_length) )
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "check_match missmatch because path %s is too long (%u vs. %u vs. %u)\n",
- GCPP_2s (path),
- path->entries_length,
- off + 1,
- cm_ctx->cpath_length);
- return GNUNET_YES; /* too long, goes somewhere else already, thus cannot be useful */
- }
- for (unsigned int i=0;i<off;i++)
- if (cm_ctx->cpath[i] !=
- GCPP_get_peer_at_offset (path,
- i))
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "check_match path %s missmatches at offset %u\n",
- GCPP_2s (path),
- i);
- return GNUNET_YES; /* missmatch, ignore */
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "check_match found match with path %s\n",
- GCPP_2s (path));
- cm_ctx->match = path;
- return GNUNET_NO; /* match, we are done! */
-}
-
-
-/**
- * Extend path @a path by the @a num_peers from the @a peers
- * array, assuming the owners past the current owner want it.
- *
- * @param path path to extend
- * @param peers list of peers beyond the end of @a path
- * @param num_peers length of the @a peers array
- * @param force force attachment, even if we have other
- * paths already
- */
-static void
-extend_path (struct CadetPeerPath *path,
- struct CadetPeer **peers,
- unsigned int num_peers,
- int force)
-{
- unsigned int old_len = path->entries_length;
- int i;
-
- /* Expand path */
- GNUNET_array_grow (path->entries,
- path->entries_length,
- old_len + num_peers);
- for (i=num_peers-1;i >= 0;i--)
- {
- struct CadetPeerPathEntry *entry = GNUNET_new (struct CadetPeerPathEntry);
-
- path->entries[old_len + i] = entry;
- entry->peer = peers[i];
- entry->path = path;
- }
- for (i=num_peers-1;i >= 0;i--)
- {
- struct CadetPeerPathEntry *entry = path->entries[old_len + i];
-
- GCP_path_entry_add (entry->peer,
- entry,
- old_len + i);
- }
-
- /* If we extend an existing path, detach it from the
- old owner and re-attach to the new one */
- GCP_detach_path (path->entries[old_len-1]->peer,
- path,
- path->hn);
- path->hn = NULL;
- for (i=num_peers-1;i>=0;i--)
- {
- struct CadetPeerPathEntry *entry = path->entries[old_len + i];
-
- path->entries_length = old_len + i + 1;
- recalculate_path_desirability (path);
- path->hn = GCP_attach_path (peers[i],
- path,
- old_len + (unsigned int) i,
- force);
- if (NULL != path->hn)
- break;
- GNUNET_assert (NULL == entry->cc);
- GCP_path_entry_remove (entry->peer,
- entry,
- old_len + i);
- GNUNET_free (entry);
- path->entries[old_len + i] = NULL;
- }
- if (NULL == path->hn)
- {
- /* none of the peers is interested in this path;
- shrink path back and re-attach. */
- GNUNET_array_grow (path->entries,
- path->entries_length,
- old_len);
- path->hn = GCP_attach_path (path->entries[old_len - 1]->peer,
- path,
- old_len - 1,
- GNUNET_YES);
- GNUNET_assert (NULL != path->hn);
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Extended path %s\n",
- GCPP_2s (path));
-}
-
-
-/**
- * Create a peer path based on the result of a DHT lookup. If we
- * already know this path, or one that is longer, simply return NULL.
- * Otherwise, we try to extend an existing path, or create a new one
- * if applicable.
- *
- * @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
- * @return a path through the network
- */
-void
-GCPP_try_path_from_dht (const struct GNUNET_PeerIdentity *get_path,
- unsigned int get_path_length,
- const struct GNUNET_PeerIdentity *put_path,
- unsigned int put_path_length)
-{
- struct CadetPeer *cpath[get_path_length + put_path_length];
- struct CheckMatchContext cm_ctx;
- struct CadetPeerPath *path;
- struct GNUNET_CONTAINER_HeapNode *hn;
- int i;
- unsigned int skip;
- unsigned int total_len;
-
- /* precompute 'cpath' so we can avoid doing the lookups lots of times */
- skip = 0;
- memset (cpath,
- 0,
- sizeof (cpath)); /* Just to trigger harder errors later. */
- total_len = get_path_length + put_path_length;
- for (unsigned int off=0;off<total_len;off++)
- {
- const struct GNUNET_PeerIdentity *pid;
-
- pid = (off < get_path_length)
- ? &get_path[get_path_length - off]
- : &put_path[get_path_length + put_path_length - off];
- cpath[off - skip] = GCP_get (pid,
- GNUNET_YES);
- /* Check that no peer is twice on the path */
- for (unsigned int i=0;i<off - skip;i++)
- {
- if (cpath[i] == cpath[off - skip])
- {
- skip = off - i;
- break;
- }
- }
- }
- total_len -= skip;
-
- /* First figure out if this path is a subset of an existing path, an
- extension of an existing path, or a new path. */
- cm_ctx.cpath_length = total_len;
- cm_ctx.cpath = cpath;
- cm_ctx.match = NULL;
- for (i=total_len-1;i>=0;i--)
- {
- GCP_iterate_paths_at (cpath[i],
- (unsigned int) i,
- &check_match,
- &cm_ctx);
- if (NULL != cm_ctx.match)
- {
- if (i == total_len - 1)
- {
- /* Existing path includes this one, nothing to do! */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Path discovered from DHT is already known\n");
- return;
- }
- if (cm_ctx.match->entries_length == i + 1)
- {
- /* Existing path ends in the middle of new path, extend it! */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Trying to extend existing path %s by additional links discovered from DHT\n",
- GCPP_2s (cm_ctx.match));
- extend_path (cm_ctx.match,
- &cpath[i + 1],
- total_len - i - 1,
- GNUNET_NO);
- return;
- }
- }
- }
-
- /* No match at all, create completely new path */
- path = GNUNET_new (struct CadetPeerPath);
- path->entries_length = total_len;
- path->entries = GNUNET_new_array (path->entries_length,
- struct CadetPeerPathEntry *);
- for (i=path->entries_length-1;i>=0;i--)
- {
- struct CadetPeerPathEntry *entry = GNUNET_new (struct CadetPeerPathEntry);
-
- path->entries[i] = entry;
- entry->peer = cpath[i];
- entry->path = path;
- }
- for (i=path->entries_length-1;i>=0;i--)
- {
- struct CadetPeerPathEntry *entry = path->entries[i];
-
- GCP_path_entry_add (entry->peer,
- entry,
- i);
- }
-
- /* Finally, try to attach it */
- hn = NULL;
- for (i=total_len-1;i>=0;i--)
- {
- struct CadetPeerPathEntry *entry = path->entries[i];
-
- path->entries_length = i + 1;
- recalculate_path_desirability (path);
- hn = GCP_attach_path (cpath[i],
- path,
- (unsigned int) i,
- GNUNET_NO);
- if (NULL != hn)
- break;
- GCP_path_entry_remove (entry->peer,
- entry,
- i);
- GNUNET_free (entry);
- path->entries[i] = NULL;
- }
- if (NULL == hn)
- {
- /* None of the peers on the path care about it. */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Path discovered from DHT is not interesting to us\n");
- GNUNET_free (path->entries);
- GNUNET_free (path);
- return;
- }
- path->hn = hn;
- /* Shrink path to actual useful length */
- GNUNET_array_grow (path->entries,
- path->entries_length,
- i + 1);
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Created new path %s based on information from DHT\n",
- GCPP_2s (path));
-}
-
-
-/**
- * We got an incoming connection, obtain the corresponding path.
- *
- * @param path_length number of segments on the @a path
- * @param pids path through the network, in reverse order (we are at the end at index @a path_length)
- * @return corresponding path object
- */
-struct CadetPeerPath *
-GCPP_get_path_from_route (unsigned int path_length,
- const struct GNUNET_PeerIdentity *pids)
-{
- struct CheckMatchContext cm_ctx;
- struct CadetPeer *cpath[path_length];
- struct CadetPeerPath *path;
-
- /* precompute inverted 'cpath' so we can avoid doing the lookups and
- have the correct order */
- for (unsigned int off=0;off<path_length;off++)
- cpath[off] = GCP_get (&pids[path_length - 1 - off],
- GNUNET_YES);
-
- /* First figure out if this path is a subset of an existing path, an
- extension of an existing path, or a new path. */
- cm_ctx.cpath = cpath;
- cm_ctx.cpath_length = path_length;
- cm_ctx.match = NULL;
- for (int i=path_length-1;i>=0;i--)
- {
- GCP_iterate_paths_at (cpath[i],
- (unsigned int) i,
- &check_match,
- &cm_ctx);
- if (NULL != cm_ctx.match)
- {
- if (i == path_length - 1)
- {
- /* Existing path includes this one, return the match! */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Returning existing path %s as inverse for incoming connection\n",
- GCPP_2s (cm_ctx.match));
- return cm_ctx.match;
- }
- if (cm_ctx.match->entries_length == i + 1)
- {
- /* Existing path ends in the middle of new path, extend it! */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Extending existing path %s to create inverse for incoming connection\n",
- GCPP_2s (cm_ctx.match));
- extend_path (cm_ctx.match,
- &cpath[i + 1],
- path_length - i - 1,
- GNUNET_YES);
- /* Check that extension was successful */
- GNUNET_assert (cm_ctx.match->entries_length == path_length);
- return cm_ctx.match;
- }
- /* Eh, we found a match but couldn't use it? Something is wrong. */
- GNUNET_break (0);
- }
- }
-
- /* No match at all, create completely new path */
- path = GNUNET_new (struct CadetPeerPath);
- path->entries_length = path_length;
- path->entries = GNUNET_new_array (path->entries_length,
- struct CadetPeerPathEntry *);
- for (int i=path_length-1;i>=0;i--)
- {
- struct CadetPeerPathEntry *entry = GNUNET_new (struct CadetPeerPathEntry);
-
- path->entries[i] = entry;
- entry->peer = cpath[i];
- entry->path = path;
- }
- for (int i=path_length-1;i>=0;i--)
- {
- struct CadetPeerPathEntry *entry = path->entries[i];
-
- GCP_path_entry_add (entry->peer,
- entry,
- i);
- }
- recalculate_path_desirability (path);
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Created new path %s to create inverse for incoming connection\n",
- GCPP_2s (path));
- path->hn = GCP_attach_path (cpath[path_length - 1],
- path,
- path_length - 1,
- GNUNET_YES);
- return path;
-}
-
-
-/**
- * Return the length of the path. Excludes one end of the
- * path, so the loopback path has length 0.
- *
- * @param path path to return the length for
- * @return number of peers on the path
- */
-unsigned int
-GCPP_get_length (struct CadetPeerPath *path)
-{
- return path->entries_length;
-}
-
-
-/**
- * Find peer's offset on path.
- *
- * @param path path to search
- * @param cp peer to look for
- * @return offset of @a cp on @a path, or UINT_MAX if not found
- */
-unsigned int
-GCPP_find_peer (struct CadetPeerPath *path,
- struct CadetPeer *cp)
-{
- for (unsigned int off = 0;
- off < path->entries_length;
- off++)
- if (cp == GCPP_get_peer_at_offset (path,
- off))
- return off;
- return UINT_MAX;
-}
-
-
-/**
- * Obtain the peer at offset @a off in @a path.
- *
- * @param path peer path to inspect
- * @param off offset to return, must be smaller than path length
- * @return the peer at offset @a off
- */
-struct CadetPeer *
-GCPP_get_peer_at_offset (struct CadetPeerPath *path,
- unsigned int off)
-{
- GNUNET_assert (off < path->entries_length);
- return path->entries[off]->peer;
-}
-
-
-/**
- * Convert a path to a human-readable string.
- *
- * @param path path to convert
- * @return string, to be freed by caller (unlike other *_2s APIs!)
- */
-const char *
-GCPP_2s (struct CadetPeerPath *path)
-{
- static char buf[2048];
- size_t off;
- const unsigned int max_plen = (sizeof(buf) - 16) / 5 - 2; /* 5 characters per entry */
-
- off = 0;
- for (unsigned int i = 0;
- i < path->entries_length;
- i++)
- {
- if ( (path->entries_length > max_plen) &&
- (i == max_plen / 2) )
- off += GNUNET_snprintf (&buf[off],
- sizeof (buf) - off,
- "...-");
- if ( (path->entries_length > max_plen) &&
- (i > max_plen / 2) &&
- (i < path->entries_length - max_plen / 2) )
- continue;
- off += GNUNET_snprintf (&buf[off],
- sizeof (buf) - off,
- "%s%s",
- GNUNET_i2s (GCP_get_id (GCPP_get_peer_at_offset (path,
- i))),
- (i == path->entries_length -1) ? "" : "-");
- }
- GNUNET_snprintf (&buf[off],
- sizeof (buf) - off,
- "(%p)",
- path);
- return buf;
-}
-
-
-/* end of gnunet-service-cadet-new_paths.c */
+++ /dev/null
-
-/*
- 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_paths.h
- * @brief Information we track per path.
- * @author Bartlomiej Polot
- * @author Christian Grothoff
- */
-#ifndef GNUNET_SERVICE_CADET_PATHS_H
-#define GNUNET_SERVICE_CADET_PATHS_H
-
-#include "gnunet_util_lib.h"
-#include "gnunet-service-cadet-new.h"
-
-/**
- * Create a peer path based on the result of a DHT lookup. If we
- * already know this path, or one that is longer, simply return NULL.
- * Otherwise, we try to extend an existing path, or create a new one
- * if applicable.
- *
- * @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
- */
-void
-GCPP_try_path_from_dht (const struct GNUNET_PeerIdentity *get_path,
- unsigned int get_path_length,
- const struct GNUNET_PeerIdentity *put_path,
- unsigned int put_path_length);
-
-
-/**
- * We got an incoming connection, obtain the corresponding path.
- *
- * @param path_length number of segments on the @a path
- * @param path through the network, in reverse order (we are at the end!)
- * @return corresponding path object
- */
-struct CadetPeerPath *
-GCPP_get_path_from_route (unsigned int path_length,
- const struct GNUNET_PeerIdentity *pids);
-
-
-/**
- * Return the length of the path. Excludes one end of the
- * path, so the loopback path has length 0.
- *
- * @param path path to return the length for
- * @return number of peers on the path
- */
-unsigned int
-GCPP_get_length (struct CadetPeerPath *path);
-
-
-/**
- * Return connection to @a destination using @a path, or return
- * NULL if no such connection exists.
- *
- * @param path path to traverse
- * @param destination destination node to get to, must be on path
- * @param off offset of @a destination on @a path
- * @return NULL if we have no existing connection
- * otherwise connection from us to @a destination via @a path
- */
-struct CadetConnection *
-GCPP_get_connection (struct CadetPeerPath *path,
- struct CadetPeer *destination,
- unsigned int off);
-
-
-/**
- * Notify @a path that it is used for connection @a cc
- * which ends at the path's offset @a off.
- *
- * @param path the path to remember the @a cc
- * @param off the offset where the @a cc ends
- * @param cc the connection to remember
- */
-void
-GCPP_add_connection (struct CadetPeerPath *path,
- unsigned int off,
- struct CadetConnection *cc);
-
-
-/**
- * Notify @a path that it is no longer used for connection @a cc which
- * ended at the path's offset @a off.
- *
- * @param path the path to forget the @a cc
- * @param off the offset where the @a cc ended
- * @param cc the connection to forget
- */
-void
-GCPP_del_connection (struct CadetPeerPath *path,
- unsigned int off,
- struct CadetConnection *cc);
-
-
-/**
- * Find peer's offset on path.
- *
- * @param path path to search
- * @param cp peer to look for
- * @return offset of @a cp on @a path, or UINT_MAX if not found
- */
-unsigned int
-GCPP_find_peer (struct CadetPeerPath *path,
- struct CadetPeer *cp);
-
-
-/**
- * Return how much we like keeping the path. This is an aggregate
- * score based on various factors, including the age of the path
- * (older == better), and the value of this path to all of its ajacent
- * peers. For example, long paths that end at a peer that we have no
- * shorter way to reach are very desirable, while long paths that end
- * at a peer for which we have a shorter way as well are much less
- * desirable. Higher values indicate more valuable paths. The
- * returned value should be used to decide which paths to remember.
- *
- * @param path path to return the length for
- * @return desirability of the path, larger is more desirable
- */
-GNUNET_CONTAINER_HeapCostType
-GCPP_get_desirability (const struct CadetPeerPath *path);
-
-
-/**
- * The given peer @a cp used to own this @a path. However, it is no
- * longer interested in maintaining it, so the path should be
- * discarded or shortened (in case a previous peer on the path finds
- * the path desirable).
- *
- * @param path the path that is being released
- */
-void
-GCPP_release (struct CadetPeerPath *path);
-
-
-/**
- * Obtain the peer at offset @a off in @a path.
- *
- * @param path peer path to inspect
- * @param off offset to return, must be smaller than path length
- * @return peer at offset @a off
- */
-struct CadetPeer *
-GCPP_get_peer_at_offset (struct CadetPeerPath *path,
- unsigned int off);
-
-
-/**
- * Convert a path to a human-readable string.
- *
- * @param path path to convert
- * @return string, statically allocated
- */
-const char *
-GCPP_2s (struct CadetPeerPath *p);
-
-
-#endif
+++ /dev/null
-/*
- 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 */
+++ /dev/null
-
-/*
- 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
+++ /dev/null
-/*
- 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_tunnels.c
- * @brief Information we track per tunnel.
- * @author Bartlomiej Polot
- * @author Christian Grothoff
- *
- * FIXME:
- * - proper connection evaluation during connection management:
- * + consider quality (or quality spread?) of current connection set
- * when deciding how often to do maintenance
- * + interact with PEER to drive DHT GET/PUT operations based
- * on how much we like our connections
- */
-#include "platform.h"
-#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"
-
-
-#define LOG(level, ...) GNUNET_log_from(level,"cadet-tun",__VA_ARGS__)
-
-/**
- * How often do we try to decrypt payload with unverified key
- * material? Used to limit CPU increase upon receiving bogus
- * KX.
- */
-#define MAX_UNVERIFIED_ATTEMPTS 16
-
-/**
- * How long do we wait until tearing down an idle tunnel?
- */
-#define IDLE_DESTROY_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 90)
-
-/**
- * How long do we wait initially before retransmitting the KX?
- * TODO: replace by 2 RTT if/once we have connection-level RTT data!
- */
-#define INITIAL_KX_RETRY_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 250)
-
-/**
- * Maximum number of skipped keys we keep in memory per tunnel.
- */
-#define MAX_SKIPPED_KEYS 64
-
-/**
- * Maximum number of keys (and thus ratchet steps) we are willing to
- * skip before we decide this is either a bogus packet or a DoS-attempt.
- */
-#define MAX_KEY_GAP 256
-
-
-/**
- * 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;
-
- /**
- * 32-byte root key which gets updated by DH ratchet.
- */
- struct GNUNET_CRYPTO_SymmetricSessionKey RK;
-
- /**
- * 32-byte header key (currently used for sending).
- */
- struct GNUNET_CRYPTO_SymmetricSessionKey HKs;
-
- /**
- * 32-byte header key (currently used for receiving)
- */
- struct GNUNET_CRYPTO_SymmetricSessionKey HKr;
-
- /**
- * 32-byte next header key (for sending), used once the
- * ratchet advances. We are sure that the sender has this
- * key as well only after @e ratchet_allowed is #GNUNET_YES.
- */
- struct GNUNET_CRYPTO_SymmetricSessionKey NHKs;
-
- /**
- * 32-byte next header key (for receiving). To be tried
- * when decrypting with @e HKr fails and thus the sender
- * may have advanced the ratchet.
- */
- struct GNUNET_CRYPTO_SymmetricSessionKey NHKr;
-
- /**
- * 32-byte chain keys (used for forward-secrecy) for
- * sending messages. Updated for every message.
- */
- struct GNUNET_CRYPTO_SymmetricSessionKey CKs;
-
- /**
- * 32-byte chain keys (used for forward-secrecy) for
- * receiving messages. Updated for every message. If
- * messages are skipped, the respective derived MKs
- * (and the current @HKr) are kept in the @e skipped_head DLL.
- */
- struct GNUNET_CRYPTO_SymmetricSessionKey CKr;
-
- /**
- * ECDH for key exchange (A0 / B0).
- */
- struct GNUNET_CRYPTO_EcdhePrivateKey kx_0;
-
- /**
- * ECDH Ratchet key (our private key in the current DH).
- */
- struct GNUNET_CRYPTO_EcdhePrivateKey DHRs;
-
- /**
- * ECDH Ratchet key (other peer's public key in the current DH).
- */
- struct GNUNET_CRYPTO_EcdhePublicKey DHRr;
-
- /**
- * Time when the current ratchet expires and a new one is triggered
- * (if @e ratchet_allowed is #GNUNET_YES).
- */
- struct GNUNET_TIME_Absolute ratchet_expiration;
-
- /**
- * Number of elements in @a skipped_head <-> @a skipped_tail.
- */
- unsigned int skipped;
-
- /**
- * 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;
-
- /**
- * True (#GNUNET_YES) if we have received a message from the
- * other peer that uses the keys from our last ratchet step.
- * This implies that we are again allowed to advance the ratchet,
- * otherwise we have to wait until the other peer sees our current
- * ephemeral key and advances first.
- *
- * #GNUNET_NO if we have advanced the ratched but lack any evidence
- * that the other peer has noticed this.
- */
- int ratchet_allowed;
-
- /**
- * Number of messages recieved since our last ratchet advance.
- *
- * If this counter = 0, we cannot send a new ratchet key in the next
- * message.
- *
- * If this counter > 0, we could (but don't have to) send a new key.
- *
- * Once the @e ratchet_counter is larger than
- * #ratchet_messages (or @e ratchet_expiration time has past), and
- * @e ratchet_allowed is #GNUNET_YES, we advance the ratchet.
- */
- unsigned int ratchet_counter;
-
-};
-
-
-/**
- * Struct used to save messages in a non-ready tunnel to send once connected.
- */
-struct CadetTunnelQueueEntry
-{
- /**
- * We are entries in a DLL
- */
- struct CadetTunnelQueueEntry *next;
-
- /**
- * We are entries in a DLL
- */
- struct CadetTunnelQueueEntry *prev;
-
- /**
- * Tunnel these messages belong in.
- */
- struct CadetTunnel *t;
-
- /**
- * Continuation to call once sent (on the channel layer).
- */
- GCT_SendContinuation cont;
-
- /**
- * Closure for @c cont.
- */
- void *cont_cls;
-
- /**
- * Envelope of message to send follows.
- */
- struct GNUNET_MQ_Envelope *env;
-
- /**
- * Where to put the connection identifier into the payload
- * of the message in @e env once we have it?
- */
- struct GNUNET_CADET_ConnectionTunnelIdentifier *cid;
-};
-
-
-/**
- * Struct containing all information regarding a tunnel to a peer.
- */
-struct CadetTunnel
-{
- /**
- * Destination of the tunnel.
- */
- struct CadetPeer *destination;
-
- /**
- * 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;
-
- /**
- * Axolotl info.
- */
- struct CadetTunnelAxolotl ax;
-
- /**
- * Unverified Axolotl info, used only if we got a fresh KX (not a
- * KX_AUTH) while our end of the tunnel was still up. In this case,
- * we keep the fresh KX around but do not put it into action until
- * we got encrypted payload that assures us of the authenticity of
- * the KX.
- */
- struct CadetTunnelAxolotl *unverified_ax;
-
- /**
- * Task scheduled if there are no more channels using the tunnel.
- */
- struct GNUNET_SCHEDULER_Task *destroy_task;
-
- /**
- * Task to trim connections if too many are present.
- */
- struct GNUNET_SCHEDULER_Task *maintain_connections_task;
-
- /**
- * Task to send messages from queue (if possible).
- */
- struct GNUNET_SCHEDULER_Task *send_task;
-
- /**
- * Task to trigger KX.
- */
- struct GNUNET_SCHEDULER_Task *kx_task;
-
- /**
- * Tokenizer for decrypted messages.
- */
- struct GNUNET_MessageStreamTokenizer *mst;
-
- /**
- * Dispatcher for decrypted messages only (do NOT use for sending!).
- */
- struct GNUNET_MQ_Handle *mq;
-
- /**
- * DLL of ready connections that are actively used to reach the destination peer.
- */
- struct CadetTConnection *connection_ready_head;
-
- /**
- * DLL of ready connections that are actively used to reach the destination peer.
- */
- struct CadetTConnection *connection_ready_tail;
-
- /**
- * DLL of connections that we maintain that might be used to reach the destination peer.
- */
- struct CadetTConnection *connection_busy_head;
-
- /**
- * DLL of connections that we maintain that might be used to reach the destination peer.
- */
- struct CadetTConnection *connection_busy_tail;
-
- /**
- * Channels inside this tunnel. Maps
- * `struct GNUNET_CADET_ChannelTunnelNumber` to a `struct CadetChannel`.
- */
- struct GNUNET_CONTAINER_MultiHashMap32 *channels;
-
- /**
- * Channel ID for the next created channel in this tunnel.
- */
- struct GNUNET_CADET_ChannelTunnelNumber next_ctn;
-
- /**
- * Queued messages, to transmit once tunnel gets connected.
- */
- struct CadetTunnelQueueEntry *tq_head;
-
- /**
- * Queued messages, to transmit once tunnel gets connected.
- */
- struct CadetTunnelQueueEntry *tq_tail;
-
- /**
- * Identification of the connection from which we are currently processing
- * a message. Only valid (non-NULL) during #handle_decrypted() and the
- * handle-*()-functions called from our @e mq during that function.
- */
- struct CadetTConnection *current_ct;
-
- /**
- * How long do we wait until we retry the KX?
- */
- struct GNUNET_TIME_Relative kx_retry_delay;
-
- /**
- * When do we try the next KX?
- */
- struct GNUNET_TIME_Absolute next_kx_attempt;
-
- /**
- * Number of connections in the @e connection_ready_head DLL.
- */
- unsigned int num_ready_connections;
-
- /**
- * Number of connections in the @e connection_busy_head DLL.
- */
- unsigned int num_busy_connections;
-
- /**
- * How often have we tried and failed to decrypt a message using
- * the unverified KX material from @e unverified_ax? Used to
- * stop trying after #MAX_UNVERIFIED_ATTEMPTS.
- */
- unsigned int unverified_attempts;
-
- /**
- * Number of entries in the @e tq_head DLL.
- */
- unsigned int tq_len;
-
- /**
- * State of the tunnel encryption.
- */
- enum CadetTunnelEState estate;
-
- /**
- * Force triggering KX_AUTH independent of @e estate.
- */
- int kx_auth_requested;
-
-};
-
-
-/**
- * Connection @a ct is now unready, clear it's ready flag
- * and move it from the ready DLL to the busy DLL.
- *
- * @param ct connection to move to unready status
- */
-static void
-mark_connection_unready (struct CadetTConnection *ct)
-{
- struct CadetTunnel *t = ct->t;
-
- GNUNET_assert (GNUNET_YES == ct->is_ready);
- GNUNET_CONTAINER_DLL_remove (t->connection_ready_head,
- t->connection_ready_tail,
- ct);
- GNUNET_assert (0 < t->num_ready_connections);
- t->num_ready_connections--;
- ct->is_ready = GNUNET_NO;
- GNUNET_CONTAINER_DLL_insert (t->connection_busy_head,
- t->connection_busy_tail,
- ct);
- t->num_busy_connections++;
-}
-
-
-/**
- * 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)
-{
- static char buf[64];
-
- if (NULL == t)
- return "Tunnel(NULL)";
- GNUNET_snprintf (buf,
- sizeof (buf),
- "Tunnel %s",
- GNUNET_i2s (GCP_get_id (t->destination)));
- return buf;
-}
-
-
-/**
- * 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_RECV:
- return "CADET_TUNNEL_KEY_AX_RECV";
- case CADET_TUNNEL_KEY_AX_SENT:
- return "CADET_TUNNEL_KEY_AX_SENT";
- case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
- return "CADET_TUNNEL_KEY_AX_SENT_AND_RECV";
- case CADET_TUNNEL_KEY_AX_AUTH_SENT:
- return "CADET_TUNNEL_KEY_AX_AUTH_SENT";
- case CADET_TUNNEL_KEY_OK:
- return "CADET_TUNNEL_KEY_OK";
- default:
- GNUNET_snprintf (buf,
- sizeof (buf),
- "%u (UNKNOWN STATE)",
- es);
- return buf;
- }
-}
-
-
-/**
- * Return the peer to which this tunnel goes.
- *
- * @param t a tunnel
- * @return the destination of the tunnel
- */
-struct CadetPeer *
-GCT_get_destination (struct CadetTunnel *t)
-{
- return t->destination;
-}
-
-
-/**
- * Count channels of a tunnel.
- *
- * @param t Tunnel on which to count.
- *
- * @return Number of channels.
- */
-unsigned int
-GCT_count_channels (struct CadetTunnel *t)
-{
- return GNUNET_CONTAINER_multihashmap32_size (t->channels);
-}
-
-
-/**
- * Lookup a channel by its @a ctn.
- *
- * @param t tunnel to look in
- * @param ctn number of channel to find
- * @return NULL if channel does not exist
- */
-struct CadetChannel *
-lookup_channel (struct CadetTunnel *t,
- struct GNUNET_CADET_ChannelTunnelNumber ctn)
-{
- return GNUNET_CONTAINER_multihashmap32_get (t->channels,
- ntohl (ctn.cn));
-}
-
-
-/**
- * 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 (const struct CadetTunnel *t)
-{
- return t->num_ready_connections + t->num_busy_connections;
-}
-
-
-/**
- * Find first connection that is ready in the list of
- * our connections. Picks ready connections round-robin.
- *
- * @param t tunnel to search
- * @return NULL if we have no connection that is ready
- */
-static struct CadetTConnection *
-get_ready_connection (struct CadetTunnel *t)
-{
- struct CadetTConnection *hd = t->connection_ready_head;
-
- GNUNET_assert ( (NULL == hd) ||
- (GNUNET_YES == hd->is_ready) );
- return hd;
-}
-
-
-/**
- * Get the encryption state of a tunnel.
- *
- * @param t Tunnel.
- *
- * @return Tunnel's encryption state.
- */
-enum CadetTunnelEState
-GCT_get_estate (struct CadetTunnel *t)
-{
- return t->estate;
-}
-
-
-/**
- * Called when either we have a new connection, or a new message in the
- * queue, or some existing connection has transmission capacity. Looks
- * at our message queue and if there is a message, picks a connection
- * to send it on.
- *
- * @param cls the `struct CadetTunnel` to process messages on
- */
-static void
-trigger_transmissions (void *cls);
-
-
-/* ************************************** start core crypto ***************************** */
-
-
-/**
- * Create a new Axolotl ephemeral (ratchet) key.
- *
- * @param ax key material to update
- */
-static void
-new_ephemeral (struct CadetTunnelAxolotl *ax)
-{
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Creating new ephemeral ratchet key (DHRs)\n");
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CRYPTO_ecdhe_key_create2 (&ax->DHRs));
-}
-
-
-/**
- * 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;
-
- GNUNET_CRYPTO_hmac_derive_key (&auth_key,
- key,
- &iv, sizeof (iv),
- key, sizeof (*key),
- ctx, sizeof (ctx),
- NULL);
- /* Two step: GNUNET_ShortHash is only 256 bits,
- GNUNET_HashCode is 512, so we truncate. */
- GNUNET_CRYPTO_hmac (&auth_key,
- plaintext,
- size,
- &hash);
- GNUNET_memcpy (hmac,
- &hash,
- sizeof (*hmac));
-}
-
-
-/**
- * Perform a HMAC.
- *
- * @param key Key to use.
- * @param[out] hash Resulting HMAC.
- * @param source Source key material (data to HMAC).
- * @param len Length of @a source.
- */
-static void
-t_ax_hmac_hash (const struct GNUNET_CRYPTO_SymmetricSessionKey *key,
- struct GNUNET_HashCode *hash,
- const 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 symmetric encryption key from an HMAC-HASH.
- *
- * @param key Key to use for the HMAC.
- * @param[out] out Key to generate.
- * @param source Source key material (data to HMAC).
- * @param len Length of @a source.
- */
-static void
-t_hmac_derive_key (const struct GNUNET_CRYPTO_SymmetricSessionKey *key,
- struct GNUNET_CRYPTO_SymmetricSessionKey *out,
- const 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 ax key material to use.
- * @param dst Destination with @a size bytes for the encrypted data.
- * @param src Source of the plaintext. Can overlap with @c dst, must contain @a size bytes
- * @param size Size of the buffers at @a src and @a dst
- */
-static void
-t_ax_encrypt (struct CadetTunnelAxolotl *ax,
- void *dst,
- const void *src,
- size_t size)
-{
- struct GNUNET_CRYPTO_SymmetricSessionKey MK;
- struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
- size_t out_size;
-
- 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 (ax);
- 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);
-
- out_size = GNUNET_CRYPTO_symmetric_encrypt (src,
- size,
- &MK,
- &iv,
- dst);
- GNUNET_assert (size == out_size);
- t_hmac_derive_key (&ax->CKs,
- &ax->CKs,
- "1",
- 1);
-}
-
-
-/**
- * Decrypt data with the axolotl tunnel key.
- *
- * @param ax key material to use.
- * @param dst Destination for the decrypted data, must contain @a size bytes.
- * @param src Source of the ciphertext. Can overlap with @c dst, must contain @a size bytes.
- * @param size Size of the @a src and @a dst buffers
- */
-static void
-t_ax_decrypt (struct CadetTunnelAxolotl *ax,
- void *dst,
- const void *src,
- size_t size)
-{
- struct GNUNET_CRYPTO_SymmetricSessionKey MK;
- struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
- size_t out_size;
-
- t_hmac_derive_key (&ax->CKr,
- &MK,
- "0",
- 1);
- GNUNET_CRYPTO_symmetric_derive_iv (&iv,
- &MK,
- NULL, 0,
- NULL);
- 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);
-}
-
-
-/**
- * Encrypt header with the axolotl header key.
- *
- * @param ax key material to use.
- * @param[in|out] msg Message whose header to encrypt.
- */
-static void
-t_h_encrypt (struct CadetTunnelAxolotl *ax,
- struct GNUNET_CADET_TunnelEncryptedMessage *msg)
-{
- struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
- size_t out_size;
-
- GNUNET_CRYPTO_symmetric_derive_iv (&iv,
- &ax->HKs,
- NULL, 0,
- NULL);
- out_size = GNUNET_CRYPTO_symmetric_encrypt (&msg->ax_header,
- sizeof (struct GNUNET_CADET_AxHeader),
- &ax->HKs,
- &iv,
- &msg->ax_header);
- GNUNET_assert (sizeof (struct GNUNET_CADET_AxHeader) == out_size);
-}
-
-
-/**
- * Decrypt header with the current axolotl header key.
- *
- * @param ax key material to use.
- * @param src Message whose header to decrypt.
- * @param dst Where to decrypt header to.
- */
-static void
-t_h_decrypt (struct CadetTunnelAxolotl *ax,
- const struct GNUNET_CADET_TunnelEncryptedMessage *src,
- struct GNUNET_CADET_TunnelEncryptedMessage *dst)
-{
- struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
- size_t out_size;
-
- GNUNET_CRYPTO_symmetric_derive_iv (&iv,
- &ax->HKr,
- NULL, 0,
- NULL);
- out_size = GNUNET_CRYPTO_symmetric_decrypt (&src->ax_header.Ns,
- sizeof (struct GNUNET_CADET_AxHeader),
- &ax->HKr,
- &iv,
- &dst->ax_header.Ns);
- GNUNET_assert (sizeof (struct GNUNET_CADET_AxHeader) == out_size);
-}
-
-
-/**
- * Delete a key from the list of skipped keys.
- *
- * @param ax key material to delete @a key from.
- * @param key Key to delete.
- */
-static void
-delete_skipped_key (struct CadetTunnelAxolotl *ax,
- struct CadetTunnelSkippedKey *key)
-{
- GNUNET_CONTAINER_DLL_remove (ax->skipped_head,
- ax->skipped_tail,
- key);
- GNUNET_free (key);
- ax->skipped--;
-}
-
-
-/**
- * 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 ax key material 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 ssize_t
-try_old_ax_keys (struct CadetTunnelAxolotl *ax,
- 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 skipped keys\n");
- hmac = &plaintext_header.hmac;
- esize = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage);
-
- /* Find a correct Header Key */
- valid_HK = NULL;
- for (key = ax->skipped_head; NULL != key; key = key->next)
- {
- t_hmac (&src->ax_header,
- sizeof (struct GNUNET_CADET_AxHeader) + esize,
- 0,
- &key->HK,
- hmac);
- if (0 == memcmp (hmac,
- &src->hmac,
- sizeof (*hmac)))
- {
- 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->ax_header.Ns,
- sizeof (struct GNUNET_CADET_AxHeader),
- &key->HK,
- &iv,
- &plaintext_header.ax_header.Ns);
- GNUNET_assert (sizeof (struct GNUNET_CADET_AxHeader) == res);
-
- /* Find the correct message key */
- N = ntohl (plaintext_header.ax_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;
-
- /* 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);
- delete_skipped_key (ax,
- key);
- return res;
-}
-
-
-/**
- * Delete a key from the list of skipped keys.
- *
- * @param ax key material to delete from.
- * @param HKr Header Key to use.
- */
-static void
-store_skipped_key (struct CadetTunnelAxolotl *ax,
- const struct GNUNET_CRYPTO_SymmetricSessionKey *HKr)
-{
- struct CadetTunnelSkippedKey *key;
-
- key = GNUNET_new (struct CadetTunnelSkippedKey);
- key->timestamp = GNUNET_TIME_absolute_get ();
- key->Kn = ax->Nr;
- key->HK = ax->HKr;
- t_hmac_derive_key (&ax->CKr,
- &key->MK,
- "0",
- 1);
- t_hmac_derive_key (&ax->CKr,
- &ax->CKr,
- "1",
- 1);
- GNUNET_CONTAINER_DLL_insert (ax->skipped_head,
- ax->skipped_tail,
- key);
- ax->skipped++;
- ax->Nr++;
-}
-
-
-/**
- * Stage skipped AX keys and calculate the message key.
- * Stores each HK and MK for skipped messages.
- *
- * @param ax key material to use
- * @param HKr Header key.
- * @param Np Received meesage number.
- * @return #GNUNET_OK if keys were stored.
- * #GNUNET_SYSERR if an error ocurred (@a Np not expected).
- */
-static int
-store_ax_keys (struct CadetTunnelAxolotl *ax,
- const struct GNUNET_CRYPTO_SymmetricSessionKey *HKr,
- uint32_t Np)
-{
- int gap;
-
- gap = Np - ax->Nr;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Storing skipped keys [%u, %u)\n",
- ax->Nr,
- Np);
- if (MAX_KEY_GAP < gap)
- {
- /* Avoid DoS (forcing peer to do more than #MAX_KEY_GAP 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,
- ax->Nr);
- return GNUNET_SYSERR;
- }
- if (0 > gap)
- {
- /* Delayed message: don't store keys, flag to try old keys. */
- return GNUNET_SYSERR;
- }
-
- while (ax->Nr < Np)
- store_skipped_key (ax,
- HKr);
-
- while (ax->skipped > MAX_SKIPPED_KEYS)
- delete_skipped_key (ax,
- 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 ax key material 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 ssize_t
-t_ax_decrypt_and_validate (struct CadetTunnelAxolotl *ax,
- void *dst,
- const struct GNUNET_CADET_TunnelEncryptedMessage *src,
- size_t size)
-{
- 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 */
-
- esize = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage);
-
- /* Try current HK */
- t_hmac (&src->ax_header,
- sizeof (struct GNUNET_CADET_AxHeader) + 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 */
- t_hmac (&src->ax_header,
- sizeof (struct GNUNET_CADET_AxHeader) + 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 (ax,
- dst,
- src,
- size);
- }
- HK = ax->HKr;
- ax->HKr = ax->NHKr;
- t_h_decrypt (ax,
- src,
- &plaintext_header);
- Np = ntohl (plaintext_header.ax_header.Ns);
- PNp = ntohl (plaintext_header.ax_header.PNs);
- DHRp = &plaintext_header.ax_header.DHRs;
- store_ax_keys (ax,
- &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
- {
- t_h_decrypt (ax,
- src,
- &plaintext_header);
- Np = ntohl (plaintext_header.ax_header.Ns);
- PNp = ntohl (plaintext_header.ax_header.PNs);
- }
- if ( (Np != ax->Nr) &&
- (GNUNET_OK != store_ax_keys (ax,
- &ax->HKr,
- Np)) )
- {
- /* Try the skipped keys, if that fails, we're out of luck. */
- return try_old_ax_keys (ax,
- dst,
- src,
- size);
- }
-
- t_ax_decrypt (ax,
- dst,
- &src[1],
- esize);
- ax->Nr = Np + 1;
- return esize;
-}
-
-
-/**
- * Our tunnel became ready for the first time, notify channels
- * that have been waiting.
- *
- * @param cls our tunnel, not used
- * @param key unique ID of the channel, not used
- * @param value the `struct CadetChannel` to notify
- * @return #GNUNET_OK (continue to iterate)
- */
-static int
-notify_tunnel_up_cb (void *cls,
- uint32_t key,
- void *value)
-{
- struct CadetChannel *ch = value;
-
- GCCH_tunnel_up (ch);
- return GNUNET_OK;
-}
-
-
-/**
- * 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 = t->estate;
-
- t->estate = state;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "%s estate changed from %s to %s\n",
- GCT_2s (t),
- estate2s (old),
- estate2s (state));
-
- if ( (CADET_TUNNEL_KEY_OK != old) &&
- (CADET_TUNNEL_KEY_OK == t->estate) )
- {
- if (NULL != t->kx_task)
- {
- GNUNET_SCHEDULER_cancel (t->kx_task);
- t->kx_task = NULL;
- }
- /* notify all channels that have been waiting */
- GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
- ¬ify_tunnel_up_cb,
- t);
- if (NULL != t->send_task)
- GNUNET_SCHEDULER_cancel (t->send_task);
- t->send_task = GNUNET_SCHEDULER_add_now (&trigger_transmissions,
- t);
- }
-}
-
-
-/**
- * Send a KX message.
- *
- * @param t tunnel on which to send the KX_AUTH
- * @param ct Tunnel and connection on which to send the KX_AUTH, NULL if
- * we are to find one that is ready.
- * @param ax axolotl key context to use
- */
-static void
-send_kx (struct CadetTunnel *t,
- struct CadetTConnection *ct,
- struct CadetTunnelAxolotl *ax)
-{
- struct CadetConnection *cc;
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_CADET_TunnelKeyExchangeMessage *msg;
- enum GNUNET_CADET_KX_Flags flags;
-
- if ( (NULL == ct) ||
- (GNUNET_NO == ct->is_ready) )
- ct = get_ready_connection (t);
- if (NULL == ct)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Wanted to send %s in state %s, but no connection is ready, deferring\n",
- GCT_2s (t),
- estate2s (t->estate));
- t->next_kx_attempt = GNUNET_TIME_absolute_get ();
- return;
- }
- cc = ct->cc;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Sending KX on %s via %s in state %s\n",
- GCT_2s (t),
- GCC_2s (cc),
- estate2s (t->estate));
- env = GNUNET_MQ_msg (msg,
- GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX);
- flags = GNUNET_CADET_KX_FLAG_FORCE_REPLY; /* always for KX */
- msg->flags = htonl (flags);
- msg->cid = *GCC_get_id (cc);
- GNUNET_CRYPTO_ecdhe_key_get_public (&ax->kx_0,
- &msg->ephemeral_key);
- GNUNET_CRYPTO_ecdhe_key_get_public (&ax->DHRs,
- &msg->ratchet_key);
- mark_connection_unready (ct);
- t->kx_retry_delay = GNUNET_TIME_STD_BACKOFF (t->kx_retry_delay);
- t->next_kx_attempt = GNUNET_TIME_relative_to_absolute (t->kx_retry_delay);
- if (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate)
- GCT_change_estate (t,
- CADET_TUNNEL_KEY_AX_SENT);
- else if (CADET_TUNNEL_KEY_AX_RECV == t->estate)
- GCT_change_estate (t,
- CADET_TUNNEL_KEY_AX_SENT_AND_RECV);
- GCC_transmit (cc,
- env);
-}
-
-
-/**
- * Send a KX_AUTH message.
- *
- * @param t tunnel on which to send the KX_AUTH
- * @param ct Tunnel and connection on which to send the KX_AUTH, NULL if
- * we are to find one that is ready.
- * @param ax axolotl key context to use
- * @param force_reply Force the other peer to reply with a KX_AUTH message
- * (set if we would like to transmit right now, but cannot)
- */
-static void
-send_kx_auth (struct CadetTunnel *t,
- struct CadetTConnection *ct,
- struct CadetTunnelAxolotl *ax,
- int force_reply)
-{
- struct CadetConnection *cc;
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg;
- enum GNUNET_CADET_KX_Flags flags;
-
- if ( (NULL == ct) ||
- (GNUNET_NO == ct->is_ready) )
- ct = get_ready_connection (t);
- if (NULL == ct)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Wanted to send KX_AUTH on %s, but no connection is ready, deferring\n",
- GCT_2s (t));
- t->next_kx_attempt = GNUNET_TIME_absolute_get ();
- t->kx_auth_requested = GNUNET_YES; /* queue KX_AUTH independent of estate */
- return;
- }
- t->kx_auth_requested = GNUNET_NO; /* clear flag */
- cc = ct->cc;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Sending KX_AUTH on %s using %s\n",
- GCT_2s (t),
- GCC_2s (ct->cc));
-
- env = GNUNET_MQ_msg (msg,
- GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX_AUTH);
- flags = GNUNET_CADET_KX_FLAG_NONE;
- if (GNUNET_YES == force_reply)
- flags |= GNUNET_CADET_KX_FLAG_FORCE_REPLY;
- msg->kx.flags = htonl (flags);
- msg->kx.cid = *GCC_get_id (cc);
- GNUNET_CRYPTO_ecdhe_key_get_public (&ax->kx_0,
- &msg->kx.ephemeral_key);
- GNUNET_CRYPTO_ecdhe_key_get_public (&ax->DHRs,
- &msg->kx.ratchet_key);
- /* Compute authenticator (this is the main difference to #send_kx()) */
- GNUNET_CRYPTO_hash (&ax->RK,
- sizeof (ax->RK),
- &msg->auth);
-
- /* Compute when to be triggered again; actual job will
- be scheduled via #connection_ready_cb() */
- t->kx_retry_delay
- = GNUNET_TIME_STD_BACKOFF (t->kx_retry_delay);
- t->next_kx_attempt
- = GNUNET_TIME_relative_to_absolute (t->kx_retry_delay);
-
- /* Send via cc, mark it as unready */
- mark_connection_unready (ct);
-
- /* Update state machine, unless we are already OK */
- if (CADET_TUNNEL_KEY_OK != t->estate)
- GCT_change_estate (t,
- CADET_TUNNEL_KEY_AX_AUTH_SENT);
-
- GCC_transmit (cc,
- env);
-}
-
-
-/**
- * Cleanup state used by @a ax.
- *
- * @param ax state to free, but not memory of @a ax itself
- */
-static void
-cleanup_ax (struct CadetTunnelAxolotl *ax)
-{
- while (NULL != ax->skipped_head)
- delete_skipped_key (ax,
- ax->skipped_head);
- GNUNET_assert (0 == ax->skipped);
- GNUNET_CRYPTO_ecdhe_key_clear (&ax->kx_0);
- GNUNET_CRYPTO_ecdhe_key_clear (&ax->DHRs);
-}
-
-
-/**
- * Update our Axolotl key state based on the KX data we received.
- * Computes the new chain keys, and root keys, etc, and also checks
- * wether this is a replay of the current chain.
- *
- * @param[in|out] axolotl chain key state to recompute
- * @param pid peer identity of the other peer
- * @param ephemeral_key ephemeral public key of the other peer
- * @param ratchet_key senders next ephemeral public key
- * @return #GNUNET_OK on success, #GNUNET_NO if the resulting
- * root key is already in @a ax and thus the KX is useless;
- * #GNUNET_SYSERR on hard errors (i.e. @a pid is #my_full_id)
- */
-static int
-update_ax_by_kx (struct CadetTunnelAxolotl *ax,
- const struct GNUNET_PeerIdentity *pid,
- const struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral_key,
- const struct GNUNET_CRYPTO_EcdhePublicKey *ratchet_key)
-{
- struct GNUNET_HashCode key_material[3];
- struct GNUNET_CRYPTO_SymmetricSessionKey keys[5];
- const char salt[] = "CADET Axolotl salt";
- int am_I_alice;
-
- 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 GNUNET_SYSERR;
- }
-
- if (0 == memcmp (&ax->DHRr,
- ratchet_key,
- sizeof (*ratchet_key)))
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Ratchet key already known. Ignoring KX.\n");
- return GNUNET_NO;
- }
-
- ax->DHRr = *ratchet_key;
-
- /* ECDH A B0 */
- if (GNUNET_YES == am_I_alice)
- {
- GNUNET_CRYPTO_eddsa_ecdh (my_private_key, /* A */
- 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 (my_private_key, /* A */
- 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 */
- ephemeral_key, /* B0 or A0 */
- &key_material[2]);
-
- /* 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_DEBUG,
- "Root key of handshake already known. Ignoring KX.\n");
- return GNUNET_NO;
- }
-
- 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_expiration
- = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(),
- ratchet_time);
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Try to redo the KX or KX_AUTH handshake, if we can.
- *
- * @param cls the `struct CadetTunnel` to do KX for.
- */
-static void
-retry_kx (void *cls)
-{
- struct CadetTunnel *t = cls;
- struct CadetTunnelAxolotl *ax;
-
- t->kx_task = NULL;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Trying to make KX progress on %s in state %s\n",
- GCT_2s (t),
- estate2s (t->estate));
- switch (t->estate)
- {
- case CADET_TUNNEL_KEY_UNINITIALIZED: /* first attempt */
- case CADET_TUNNEL_KEY_AX_SENT: /* trying again */
- send_kx (t,
- NULL,
- &t->ax);
- break;
- case CADET_TUNNEL_KEY_AX_RECV:
- case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
- /* We are responding, so only require reply
- if WE have a channel waiting. */
- if (NULL != t->unverified_ax)
- {
- /* Send AX_AUTH so we might get this one verified */
- ax = t->unverified_ax;
- }
- else
- {
- /* How can this be? */
- GNUNET_break (0);
- ax = &t->ax;
- }
- send_kx_auth (t,
- NULL,
- ax,
- (0 == GCT_count_channels (t))
- ? GNUNET_NO
- : GNUNET_YES);
- break;
- case CADET_TUNNEL_KEY_AX_AUTH_SENT:
- /* We are responding, so only require reply
- if WE have a channel waiting. */
- if (NULL != t->unverified_ax)
- {
- /* Send AX_AUTH so we might get this one verified */
- ax = t->unverified_ax;
- }
- else
- {
- /* How can this be? */
- GNUNET_break (0);
- ax = &t->ax;
- }
- send_kx_auth (t,
- NULL,
- ax,
- (0 == GCT_count_channels (t))
- ? GNUNET_NO
- : GNUNET_YES);
- break;
- case CADET_TUNNEL_KEY_OK:
- /* Must have been the *other* peer asking us to
- respond with a KX_AUTH. */
- if (NULL != t->unverified_ax)
- {
- /* Sending AX_AUTH in response to AX so we might get this one verified */
- ax = t->unverified_ax;
- }
- else
- {
- /* Sending AX_AUTH in response to AX_AUTH */
- ax = &t->ax;
- }
- send_kx_auth (t,
- NULL,
- ax,
- GNUNET_NO);
- break;
- }
-}
-
-
-/**
- * Handle KX message that lacks authentication (and which will thus
- * only be considered authenticated after we respond with our own
- * KX_AUTH and finally successfully decrypt payload).
- *
- * @param ct connection/tunnel combo that received encrypted message
- * @param msg the key exchange message
- */
-void
-GCT_handle_kx (struct CadetTConnection *ct,
- const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg)
-{
- struct CadetTunnel *t = ct->t;
- struct CadetTunnelAxolotl *ax;
- int ret;
-
- if (0 ==
- memcmp (&t->ax.DHRr,
- &msg->ratchet_key,
- sizeof (msg->ratchet_key)))
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Got duplicate KX. Firing back KX_AUTH.\n");
- send_kx_auth (t,
- ct,
- &t->ax,
- GNUNET_NO);
- return;
- }
-
- /* We only keep ONE unverified KX around, so if there is an existing one,
- clean it up. */
- if (NULL != t->unverified_ax)
- {
- if (0 ==
- memcmp (&t->unverified_ax->DHRr,
- &msg->ratchet_key,
- sizeof (msg->ratchet_key)))
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Got duplicate unverified KX on %s. Fire back KX_AUTH again.\n",
- GCT_2s (t));
- send_kx_auth (t,
- ct,
- t->unverified_ax,
- GNUNET_NO);
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Dropping old unverified KX state. Got a fresh KX for %s.\n",
- GCT_2s (t));
- memset (t->unverified_ax,
- 0,
- sizeof (struct CadetTunnelAxolotl));
- t->unverified_ax->DHRs = t->ax.DHRs;
- t->unverified_ax->kx_0 = t->ax.kx_0;
- }
- else
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Creating fresh unverified KX for %s.\n",
- GCT_2s (t));
- t->unverified_ax = GNUNET_new (struct CadetTunnelAxolotl);
- t->unverified_ax->DHRs = t->ax.DHRs;
- t->unverified_ax->kx_0 = t->ax.kx_0;
- }
- /* Set as the 'current' RK/DHRr the one we are currently using,
- so that the duplicate-detection logic of
- #update_ax_by_kx can work. */
- t->unverified_ax->RK = t->ax.RK;
- t->unverified_ax->DHRr = t->ax.DHRr;
- t->unverified_attempts = 0;
- ax = t->unverified_ax;
-
- /* Update 'ax' by the new key material */
- ret = update_ax_by_kx (ax,
- GCP_get_id (t->destination),
- &msg->ephemeral_key,
- &msg->ratchet_key);
- GNUNET_break (GNUNET_SYSERR != ret);
- if (GNUNET_OK != ret)
- return; /* duplicate KX, nothing to do */
-
- /* move ahead in our state machine */
- if (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate)
- GCT_change_estate (t,
- CADET_TUNNEL_KEY_AX_RECV);
- else if (CADET_TUNNEL_KEY_AX_SENT == t->estate)
- GCT_change_estate (t,
- CADET_TUNNEL_KEY_AX_SENT_AND_RECV);
-
- /* KX is still not done, try again our end. */
- if (CADET_TUNNEL_KEY_OK != t->estate)
- {
- if (NULL != t->kx_task)
- GNUNET_SCHEDULER_cancel (t->kx_task);
- t->kx_task
- = GNUNET_SCHEDULER_add_now (&retry_kx,
- t);
- }
-}
-
-
-/**
- * Handle KX_AUTH message.
- *
- * @param ct connection/tunnel combo that received encrypted message
- * @param msg the key exchange message
- */
-void
-GCT_handle_kx_auth (struct CadetTConnection *ct,
- const struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg)
-{
- struct CadetTunnel *t = ct->t;
- struct CadetTunnelAxolotl ax_tmp;
- struct GNUNET_HashCode kx_auth;
- int ret;
-
- if ( (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate) ||
- (CADET_TUNNEL_KEY_AX_RECV == t->estate) )
- {
- /* Confusing, we got a KX_AUTH before we even send our own
- KX. This should not happen. We'll send our own KX ASAP anyway,
- so let's ignore this here. */
- GNUNET_break_op (0);
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Handling KX_AUTH message for %s\n",
- GCT_2s (t));
-
- /* We do everything in ax_tmp until we've checked the authentication
- so we don't clobber anything we care about by accident. */
- ax_tmp = t->ax;
-
- /* Update 'ax' by the new key material */
- ret = update_ax_by_kx (&ax_tmp,
- GCP_get_id (t->destination),
- &msg->kx.ephemeral_key,
- &msg->kx.ratchet_key);
- if (GNUNET_OK != ret)
- {
- if (GNUNET_NO == ret)
- GNUNET_STATISTICS_update (stats,
- "# redundant KX_AUTH received",
- 1,
- GNUNET_NO);
- else
- GNUNET_break (0); /* connect to self!? */
- return;
- }
- GNUNET_CRYPTO_hash (&ax_tmp.RK,
- sizeof (ax_tmp.RK),
- &kx_auth);
- if (0 != memcmp (&kx_auth,
- &msg->auth,
- sizeof (kx_auth)))
- {
- /* This KX_AUTH is not using the latest KX/KX_AUTH data
- we transmitted to the sender, refuse it, try KX again. */
- GNUNET_STATISTICS_update (stats,
- "# KX_AUTH not using our last KX received (auth failure)",
- 1,
- GNUNET_NO);
- send_kx (t,
- ct,
- &t->ax);
- return;
- }
- /* Yep, we're good. */
- t->ax = ax_tmp;
- if (NULL != t->unverified_ax)
- {
- /* We got some "stale" KX before, drop that. */
- cleanup_ax (t->unverified_ax);
- GNUNET_free (t->unverified_ax);
- t->unverified_ax = NULL;
- }
-
- /* move ahead in our state machine */
- switch (t->estate)
- {
- case CADET_TUNNEL_KEY_UNINITIALIZED:
- case CADET_TUNNEL_KEY_AX_RECV:
- /* Checked above, this is impossible. */
- GNUNET_assert (0);
- break;
- case CADET_TUNNEL_KEY_AX_SENT: /* This is the normal case */
- case CADET_TUNNEL_KEY_AX_SENT_AND_RECV: /* both peers started KX */
- case CADET_TUNNEL_KEY_AX_AUTH_SENT: /* both peers now did KX_AUTH */
- GCT_change_estate (t,
- CADET_TUNNEL_KEY_OK);
- break;
- case CADET_TUNNEL_KEY_OK:
- /* Did not expect another KX_AUTH, but so what, still acceptable.
- Nothing to do here. */
- break;
- }
-}
-
-
-
-/* ************************************** end core crypto ***************************** */
-
-
-/**
- * Compute the next free channel tunnel number for this tunnel.
- *
- * @param t the tunnel
- * @return unused number that can uniquely identify a channel in the tunnel
- */
-static struct GNUNET_CADET_ChannelTunnelNumber
-get_next_free_ctn (struct CadetTunnel *t)
-{
-#define HIGH_BIT 0x8000000
- struct GNUNET_CADET_ChannelTunnelNumber ret;
- uint32_t ctn;
- int cmp;
- uint32_t highbit;
-
- cmp = GNUNET_CRYPTO_cmp_peer_identity (&my_full_id,
- GCP_get_id (GCT_get_destination (t)));
- if (0 < cmp)
- highbit = HIGH_BIT;
- else if (0 > cmp)
- highbit = 0;
- else
- GNUNET_assert (0); // loopback must never go here!
- ctn = ntohl (t->next_ctn.cn);
- while (NULL !=
- GNUNET_CONTAINER_multihashmap32_get (t->channels,
- ctn | highbit))
- {
- ctn = ((ctn + 1) & (~ HIGH_BIT));
- }
- t->next_ctn.cn = htonl ((ctn + 1) & (~ HIGH_BIT));
- ret.cn = htonl (ctn | highbit);
- return ret;
-}
-
-
-/**
- * Add a channel to a tunnel, and notify channel that we are ready
- * for transmission if we are already up. Otherwise that notification
- * will be done later in #notify_tunnel_up_cb().
- *
- * @param t Tunnel.
- * @param ch Channel
- * @return unique number identifying @a ch within @a t
- */
-struct GNUNET_CADET_ChannelTunnelNumber
-GCT_add_channel (struct CadetTunnel *t,
- struct CadetChannel *ch)
-{
- struct GNUNET_CADET_ChannelTunnelNumber ctn;
-
- ctn = get_next_free_ctn (t);
- if (NULL != t->destroy_task)
- {
- GNUNET_SCHEDULER_cancel (t->destroy_task);
- t->destroy_task = NULL;
- }
- GNUNET_assert (GNUNET_YES ==
- GNUNET_CONTAINER_multihashmap32_put (t->channels,
- ntohl (ctn.cn),
- ch,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Adding %s to %s\n",
- GCCH_2s (ch),
- GCT_2s (t));
- switch (t->estate)
- {
- case CADET_TUNNEL_KEY_UNINITIALIZED:
- /* waiting for connection to start KX */
- break;
- case CADET_TUNNEL_KEY_AX_RECV:
- case CADET_TUNNEL_KEY_AX_SENT:
- case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
- /* we're currently waiting for KX to complete */
- break;
- case CADET_TUNNEL_KEY_AX_AUTH_SENT:
- /* waiting for OTHER peer to send us data,
- we might need to prompt more aggressively! */
- if (NULL == t->kx_task)
- t->kx_task
- = GNUNET_SCHEDULER_add_at (t->next_kx_attempt,
- &retry_kx,
- t);
- break;
- case CADET_TUNNEL_KEY_OK:
- /* We are ready. Tell the new channel that we are up. */
- GCCH_tunnel_up (ch);
- break;
- }
- return ctn;
-}
-
-
-/**
- * We lost a connection, remove it from our list and clean up
- * the connection object itself.
- *
- * @param ct binding of connection to tunnel of the connection that was lost.
- */
-void
-GCT_connection_lost (struct CadetTConnection *ct)
-{
- struct CadetTunnel *t = ct->t;
-
- if (GNUNET_YES == ct->is_ready)
- {
- GNUNET_CONTAINER_DLL_remove (t->connection_ready_head,
- t->connection_ready_tail,
- ct);
- t->num_ready_connections--;
- }
- else
- {
- GNUNET_CONTAINER_DLL_remove (t->connection_busy_head,
- t->connection_busy_tail,
- ct);
- t->num_busy_connections--;
- }
- GNUNET_free (ct);
-}
-
-
-/**
- * Clean up connection @a ct of a tunnel.
- *
- * @param cls the `struct CadetTunnel`
- * @param ct connection to clean up
- */
-static void
-destroy_t_connection (void *cls,
- struct CadetTConnection *ct)
-{
- struct CadetTunnel *t = cls;
- struct CadetConnection *cc = ct->cc;
-
- GNUNET_assert (ct->t == t);
- GCT_connection_lost (ct);
- GCC_destroy_without_tunnel (cc);
-}
-
-
-/**
- * This tunnel is no longer used, destroy it.
- *
- * @param cls the idle tunnel
- */
-static void
-destroy_tunnel (void *cls)
-{
- struct CadetTunnel *t = cls;
- struct CadetTunnelQueueEntry *tq;
-
- t->destroy_task = NULL;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Destroying idle %s\n",
- GCT_2s (t));
- GNUNET_assert (0 == GCT_count_channels (t));
- GCT_iterate_connections (t,
- &destroy_t_connection,
- t);
- GNUNET_assert (NULL == t->connection_ready_head);
- GNUNET_assert (NULL == t->connection_busy_head);
- while (NULL != (tq = t->tq_head))
- {
- if (NULL != tq->cont)
- tq->cont (tq->cont_cls,
- NULL);
- GCT_send_cancel (tq);
- }
- GCP_drop_tunnel (t->destination,
- t);
- GNUNET_CONTAINER_multihashmap32_destroy (t->channels);
- if (NULL != t->maintain_connections_task)
- {
- GNUNET_SCHEDULER_cancel (t->maintain_connections_task);
- t->maintain_connections_task = NULL;
- }
- if (NULL != t->send_task)
- {
- GNUNET_SCHEDULER_cancel (t->send_task);
- t->send_task = NULL;
- }
- if (NULL != t->kx_task)
- {
- GNUNET_SCHEDULER_cancel (t->kx_task);
- t->kx_task = NULL;
- }
- GNUNET_MST_destroy (t->mst);
- GNUNET_MQ_destroy (t->mq);
- if (NULL != t->unverified_ax)
- {
- cleanup_ax (t->unverified_ax);
- GNUNET_free (t->unverified_ax);
- }
- cleanup_ax (&t->ax);
- GNUNET_assert (NULL == t->destroy_task);
- GNUNET_free (t);
-}
-
-
-/**
- * Remove a channel from a tunnel.
- *
- * @param t Tunnel.
- * @param ch Channel
- * @param ctn unique number identifying @a ch within @a t
- */
-void
-GCT_remove_channel (struct CadetTunnel *t,
- struct CadetChannel *ch,
- struct GNUNET_CADET_ChannelTunnelNumber ctn)
-{
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Removing %s from %s\n",
- GCCH_2s (ch),
- GCT_2s (t));
- GNUNET_assert (GNUNET_YES ==
- GNUNET_CONTAINER_multihashmap32_remove (t->channels,
- ntohl (ctn.cn),
- ch));
- if ( (0 ==
- GCT_count_channels (t)) &&
- (NULL == t->destroy_task) )
- {
- t->destroy_task
- = GNUNET_SCHEDULER_add_delayed (IDLE_DESTROY_DELAY,
- &destroy_tunnel,
- t);
- }
-}
-
-
-/**
- * Destroy remaining channels during shutdown.
- *
- * @param cls the `struct CadetTunnel` of the channel
- * @param key key of the channel
- * @param value the `struct CadetChannel`
- * @return #GNUNET_OK (continue to iterate)
- */
-static int
-destroy_remaining_channels (void *cls,
- uint32_t key,
- void *value)
-{
- struct CadetChannel *ch = value;
-
- GCCH_handle_remote_destroy (ch,
- NULL);
- return GNUNET_OK;
-}
-
-
-/**
- * Destroys the tunnel @a t now, without delay. Used during shutdown.
- *
- * @param t tunnel to destroy
- */
-void
-GCT_destroy_tunnel_now (struct CadetTunnel *t)
-{
- GNUNET_assert (GNUNET_YES == shutting_down);
- GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
- &destroy_remaining_channels,
- t);
- GNUNET_assert (0 ==
- GCT_count_channels (t));
- if (NULL != t->destroy_task)
- {
- GNUNET_SCHEDULER_cancel (t->destroy_task);
- t->destroy_task = NULL;
- }
- destroy_tunnel (t);
-}
-
-
-/**
- * Send normal payload from queue in @a t via connection @a ct.
- * Does nothing if our payload queue is empty.
- *
- * @param t tunnel to send data from
- * @param ct connection to use for transmission (is ready)
- */
-static void
-try_send_normal_payload (struct CadetTunnel *t,
- struct CadetTConnection *ct)
-{
- struct CadetTunnelQueueEntry *tq;
-
- GNUNET_assert (GNUNET_YES == ct->is_ready);
- tq = t->tq_head;
- if (NULL == tq)
- {
- /* no messages pending right now */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Not sending payload of %s on ready %s (nothing pending)\n",
- GCT_2s (t),
- GCC_2s (ct->cc));
- return;
- }
- /* ready to send message 'tq' on tunnel 'ct' */
- GNUNET_assert (t == tq->t);
- GNUNET_CONTAINER_DLL_remove (t->tq_head,
- t->tq_tail,
- tq);
- if (NULL != tq->cid)
- *tq->cid = *GCC_get_id (ct->cc);
- mark_connection_unready (ct);
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Sending payload of %s on %s\n",
- GCT_2s (t),
- GCC_2s (ct->cc));
- GCC_transmit (ct->cc,
- tq->env);
- if (NULL != tq->cont)
- tq->cont (tq->cont_cls,
- GCC_get_id (ct->cc));
- GNUNET_free (tq);
-}
-
-
-/**
- * A connection is @a is_ready for transmission. Looks at our message
- * queue and if there is a message, sends it out via the connection.
- *
- * @param cls the `struct CadetTConnection` that is @a is_ready
- * @param is_ready #GNUNET_YES if connection are now ready,
- * #GNUNET_NO if connection are no longer ready
- */
-static void
-connection_ready_cb (void *cls,
- int is_ready)
-{
- struct CadetTConnection *ct = cls;
- struct CadetTunnel *t = ct->t;
-
- if (GNUNET_NO == is_ready)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "%s no longer ready for %s\n",
- GCC_2s (ct->cc),
- GCT_2s (t));
- mark_connection_unready (ct);
- return;
- }
- GNUNET_assert (GNUNET_NO == ct->is_ready);
- GNUNET_CONTAINER_DLL_remove (t->connection_busy_head,
- t->connection_busy_tail,
- ct);
- GNUNET_assert (0 < t->num_busy_connections);
- t->num_busy_connections--;
- ct->is_ready = GNUNET_YES;
- GNUNET_CONTAINER_DLL_insert_tail (t->connection_ready_head,
- t->connection_ready_tail,
- ct);
- t->num_ready_connections++;
-
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "%s now ready for %s in state %s\n",
- GCC_2s (ct->cc),
- GCT_2s (t),
- estate2s (t->estate));
- switch (t->estate)
- {
- case CADET_TUNNEL_KEY_UNINITIALIZED:
- /* Do not begin KX if WE have no channels waiting! */
- if (0 == GCT_count_channels (t))
- return;
- /* We are uninitialized, just transmit immediately,
- without undue delay. */
- if (NULL != t->kx_task)
- {
- GNUNET_SCHEDULER_cancel (t->kx_task);
- t->kx_task = NULL;
- }
- send_kx (t,
- ct,
- &t->ax);
- break;
- case CADET_TUNNEL_KEY_AX_RECV:
- case CADET_TUNNEL_KEY_AX_SENT:
- case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
- case CADET_TUNNEL_KEY_AX_AUTH_SENT:
- /* we're currently waiting for KX to complete, schedule job */
- if (NULL == t->kx_task)
- t->kx_task
- = GNUNET_SCHEDULER_add_at (t->next_kx_attempt,
- &retry_kx,
- t);
- break;
- case CADET_TUNNEL_KEY_OK:
- if (GNUNET_YES == t->kx_auth_requested)
- {
- if (NULL != t->kx_task)
- {
- GNUNET_SCHEDULER_cancel (t->kx_task);
- t->kx_task = NULL;
- }
- send_kx_auth (t,
- ct,
- &t->ax,
- GNUNET_NO);
- return;
- }
- try_send_normal_payload (t,
- ct);
- break;
- }
-}
-
-
-/**
- * Called when either we have a new connection, or a new message in the
- * queue, or some existing connection has transmission capacity. Looks
- * at our message queue and if there is a message, picks a connection
- * to send it on.
- *
- * @param cls the `struct CadetTunnel` to process messages on
- */
-static void
-trigger_transmissions (void *cls)
-{
- struct CadetTunnel *t = cls;
- struct CadetTConnection *ct;
-
- t->send_task = NULL;
- if (NULL == t->tq_head)
- return; /* no messages pending right now */
- ct = get_ready_connection (t);
- if (NULL == ct)
- return; /* no connections ready */
- try_send_normal_payload (t,
- ct);
-}
-
-
-/**
- * Closure for #evaluate_connection. Used to assemble summary information
- * about the existing connections so we can evaluate a new path.
- */
-struct EvaluationSummary
-{
-
- /**
- * Minimum length of any of our connections, `UINT_MAX` if we have none.
- */
- unsigned int min_length;
-
- /**
- * Maximum length of any of our connections, 0 if we have none.
- */
- unsigned int max_length;
-
- /**
- * Minimum desirability of any of our connections, UINT64_MAX if we have none.
- */
- GNUNET_CONTAINER_HeapCostType min_desire;
-
- /**
- * Maximum desirability of any of our connections, 0 if we have none.
- */
- GNUNET_CONTAINER_HeapCostType max_desire;
-
- /**
- * Path we are comparing against for #evaluate_connection, can be NULL.
- */
- struct CadetPeerPath *path;
-
- /**
- * Connection deemed the "worst" so far encountered by #evaluate_connection,
- * NULL if we did not yet encounter any connections.
- */
- struct CadetTConnection *worst;
-
- /**
- * Numeric score of @e worst, only set if @e worst is non-NULL.
- */
- double worst_score;
-
- /**
- * Set to #GNUNET_YES if we have a connection over @e path already.
- */
- int duplicate;
-
-};
-
-
-/**
- * Evaluate a connection, updating our summary information in @a cls about
- * what kinds of connections we have.
- *
- * @param cls the `struct EvaluationSummary *` to update
- * @param ct a connection to include in the summary
- */
-static void
-evaluate_connection (void *cls,
- struct CadetTConnection *ct)
-{
- struct EvaluationSummary *es = cls;
- struct CadetConnection *cc = ct->cc;
- struct CadetPeerPath *ps = GCC_get_path (cc);
- const struct CadetConnectionMetrics *metrics;
- GNUNET_CONTAINER_HeapCostType ct_desirability;
- struct GNUNET_TIME_Relative uptime;
- struct GNUNET_TIME_Relative last_use;
- uint32_t ct_length;
- double score;
- double success_rate;
-
- if (ps == es->path)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Ignoring duplicate path %s.\n",
- GCPP_2s (es->path));
- es->duplicate = GNUNET_YES;
- return;
- }
- ct_desirability = GCPP_get_desirability (ps);
- ct_length = GCPP_get_length (ps);
- metrics = GCC_get_metrics (cc);
- uptime = GNUNET_TIME_absolute_get_duration (metrics->age);
- last_use = GNUNET_TIME_absolute_get_duration (metrics->last_use);
- /* We add 1.0 here to avoid division by zero. */
- success_rate = (metrics->num_acked_transmissions + 1.0) / (metrics->num_successes + 1.0);
- score
- = ct_desirability
- + 100.0 / (1.0 + ct_length) /* longer paths = better */
- + sqrt (uptime.rel_value_us / 60000000LL) /* larger uptime = better */
- - last_use.rel_value_us / 1000L; /* longer idle = worse */
- score *= success_rate; /* weigh overall by success rate */
-
- if ( (NULL == es->worst) ||
- (score < es->worst_score) )
- {
- es->worst = ct;
- es->worst_score = score;
- }
- es->min_length = GNUNET_MIN (es->min_length,
- ct_length);
- es->max_length = GNUNET_MAX (es->max_length,
- ct_length);
- es->min_desire = GNUNET_MIN (es->min_desire,
- ct_desirability);
- es->max_desire = GNUNET_MAX (es->max_desire,
- ct_desirability);
-}
-
-
-/**
- * Consider using the path @a p for the tunnel @a t.
- * The tunnel destination is at offset @a off in path @a p.
- *
- * @param cls our tunnel
- * @param path a path to our destination
- * @param off offset of the destination on path @a path
- * @return #GNUNET_YES (should keep iterating)
- */
-static int
-consider_path_cb (void *cls,
- struct CadetPeerPath *path,
- unsigned int off)
-{
- struct CadetTunnel *t = cls;
- struct EvaluationSummary es;
- struct CadetTConnection *ct;
-
- GNUNET_assert (off < GCPP_get_length (path));
- es.min_length = UINT_MAX;
- es.max_length = 0;
- es.max_desire = 0;
- es.min_desire = UINT64_MAX;
- es.path = path;
- es.duplicate = GNUNET_NO;
- es.worst = NULL;
-
- /* Compute evaluation summary over existing connections. */
- GCT_iterate_connections (t,
- &evaluate_connection,
- &es);
- if (GNUNET_YES == es.duplicate)
- return GNUNET_YES;
-
- /* FIXME: not sure we should really just count
- 'num_connections' here, as they may all have
- consistently failed to connect. */
-
- /* We iterate by increasing path length; if we have enough paths and
- this one is more than twice as long than what we are currently
- using, then ignore all of these super-long ones! */
- if ( (GCT_count_any_connections (t) > DESIRED_CONNECTIONS_PER_TUNNEL) &&
- (es.min_length * 2 < off) &&
- (es.max_length < off) )
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Ignoring paths of length %u, they are way too long.\n",
- es.min_length * 2);
- return GNUNET_NO;
- }
- /* If we have enough paths and this one looks no better, ignore it. */
- if ( (GCT_count_any_connections (t) >= DESIRED_CONNECTIONS_PER_TUNNEL) &&
- (es.min_length < GCPP_get_length (path)) &&
- (es.min_desire > GCPP_get_desirability (path)) &&
- (es.max_length < off) )
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Ignoring path (%u/%llu) to %s, got something better already.\n",
- GCPP_get_length (path),
- (unsigned long long) GCPP_get_desirability (path),
- GCP_2s (t->destination));
- return GNUNET_YES;
- }
-
- /* Path is interesting (better by some metric, or we don't have
- enough paths yet). */
- ct = GNUNET_new (struct CadetTConnection);
- ct->created = GNUNET_TIME_absolute_get ();
- ct->t = t;
- ct->cc = GCC_create (t->destination,
- path,
- off,
- GNUNET_CADET_OPTION_DEFAULT, /* FIXME: set based on what channels want/need! */
- ct,
- &connection_ready_cb,
- ct);
-
- /* FIXME: schedule job to kill connection (and path?) if it takes
- too long to get ready! (And track performance data on how long
- other connections took with the tunnel!)
- => Note: to be done within 'connection'-logic! */
- GNUNET_CONTAINER_DLL_insert (t->connection_busy_head,
- t->connection_busy_tail,
- ct);
- t->num_busy_connections++;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Found interesting path %s for %s, created %s\n",
- GCPP_2s (path),
- GCT_2s (t),
- GCC_2s (ct->cc));
- return GNUNET_YES;
-}
-
-
-/**
- * Function called to maintain the connections underlying our tunnel.
- * Tries to maintain (incl. tear down) connections for the tunnel, and
- * if there is a significant change, may trigger transmissions.
- *
- * Basically, needs to check if there are connections that perform
- * badly, and if so eventually kill them and trigger a replacement.
- * The strategy is to open one more connection than
- * #DESIRED_CONNECTIONS_PER_TUNNEL, and then periodically kick out the
- * least-performing one, and then inquire for new ones.
- *
- * @param cls the `struct CadetTunnel`
- */
-static void
-maintain_connections_cb (void *cls)
-{
- struct CadetTunnel *t = cls;
- struct GNUNET_TIME_Relative delay;
- struct EvaluationSummary es;
-
- t->maintain_connections_task = NULL;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Performing connection maintenance for %s.\n",
- GCT_2s (t));
-
- es.min_length = UINT_MAX;
- es.max_length = 0;
- es.max_desire = 0;
- es.min_desire = UINT64_MAX;
- es.path = NULL;
- es.worst = NULL;
- es.duplicate = GNUNET_NO;
- GCT_iterate_connections (t,
- &evaluate_connection,
- &es);
- if ( (NULL != es.worst) &&
- (GCT_count_any_connections (t) > DESIRED_CONNECTIONS_PER_TUNNEL) )
- {
- /* Clear out worst-performing connection 'es.worst'. */
- destroy_t_connection (t,
- es.worst);
- }
-
- /* Consider additional paths */
- (void) GCP_iterate_paths (t->destination,
- &consider_path_cb,
- t);
-
- /* FIXME: calculate when to try again based on how well we are doing;
- in particular, if we have to few connections, we might be able
- to do without this (as PATHS should tell us whenever a new path
- is available instantly; however, need to make sure this job is
- restarted after that happens).
- Furthermore, if the paths we do know are in a reasonably narrow
- quality band and are plentyful, we might also consider us stabilized
- and then reduce the frequency accordingly. */
- delay = GNUNET_TIME_UNIT_MINUTES;
- t->maintain_connections_task
- = GNUNET_SCHEDULER_add_delayed (delay,
- &maintain_connections_cb,
- t);
-}
-
-
-/**
- * Consider using the path @a p for the tunnel @a t.
- * The tunnel destination is at offset @a off in path @a p.
- *
- * @param cls our tunnel
- * @param path a path to our destination
- * @param off offset of the destination on path @a path
- */
-void
-GCT_consider_path (struct CadetTunnel *t,
- struct CadetPeerPath *p,
- unsigned int off)
-{
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Considering %s for %s\n",
- GCPP_2s (p),
- GCT_2s (t));
- (void) consider_path_cb (t,
- p,
- off);
-}
-
-
-/**
- * We got a keepalive. Track in statistics.
- *
- * @param cls the `struct CadetTunnel` for which we decrypted the message
- * @param msg the message we received on the tunnel
- */
-static void
-handle_plaintext_keepalive (void *cls,
- const struct GNUNET_MessageHeader *msg)
-{
- struct CadetTunnel *t = cls;
-
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received KEEPALIVE on %s\n",
- GCT_2s (t));
- GNUNET_STATISTICS_update (stats,
- "# keepalives received",
- 1,
- GNUNET_NO);
-}
-
-
-/**
- * Check that @a msg is well-formed.
- *
- * @param cls the `struct CadetTunnel` for which we decrypted the message
- * @param msg the message we received on the tunnel
- * @return #GNUNET_OK (any variable-size payload goes)
- */
-static int
-check_plaintext_data (void *cls,
- const struct GNUNET_CADET_ChannelAppDataMessage *msg)
-{
- return GNUNET_OK;
-}
-
-
-/**
- * We received payload data for a channel. Locate the channel
- * and process the data, or return an error if the channel is unknown.
- *
- * @param cls the `struct CadetTunnel` for which we decrypted the message
- * @param msg the message we received on the tunnel
- */
-static void
-handle_plaintext_data (void *cls,
- const struct GNUNET_CADET_ChannelAppDataMessage *msg)
-{
- struct CadetTunnel *t = cls;
- struct CadetChannel *ch;
-
- ch = lookup_channel (t,
- msg->ctn);
- if (NULL == ch)
- {
- /* We don't know about such a channel, might have been destroyed on our
- end in the meantime, or never existed. Send back a DESTROY. */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received %u bytes of application data for unknown channel %u, sending DESTROY\n",
- (unsigned int) (ntohs (msg->header.size) - sizeof (*msg)),
- ntohl (msg->ctn.cn));
- GCT_send_channel_destroy (t,
- msg->ctn);
- return;
- }
- GCCH_handle_channel_plaintext_data (ch,
- GCC_get_id (t->current_ct->cc),
- msg);
-}
-
-
-/**
- * We received an acknowledgement for data we sent on a channel.
- * Locate the channel and process it, or return an error if the
- * channel is unknown.
- *
- * @param cls the `struct CadetTunnel` for which we decrypted the message
- * @param ack the message we received on the tunnel
- */
-static void
-handle_plaintext_data_ack (void *cls,
- const struct GNUNET_CADET_ChannelDataAckMessage *ack)
-{
- struct CadetTunnel *t = cls;
- struct CadetChannel *ch;
-
- ch = lookup_channel (t,
- ack->ctn);
- if (NULL == ch)
- {
- /* We don't know about such a channel, might have been destroyed on our
- end in the meantime, or never existed. Send back a DESTROY. */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received DATA_ACK for unknown channel %u, sending DESTROY\n",
- ntohl (ack->ctn.cn));
- GCT_send_channel_destroy (t,
- ack->ctn);
- return;
- }
- GCCH_handle_channel_plaintext_data_ack (ch,
- GCC_get_id (t->current_ct->cc),
- ack);
-}
-
-
-/**
- * We have received a request to open a channel to a port from
- * another peer. Creates the incoming channel.
- *
- * @param cls the `struct CadetTunnel` for which we decrypted the message
- * @param copen the message we received on the tunnel
- */
-static void
-handle_plaintext_channel_open (void *cls,
- const struct GNUNET_CADET_ChannelOpenMessage *copen)
-{
- struct CadetTunnel *t = cls;
- struct CadetChannel *ch;
-
- ch = GNUNET_CONTAINER_multihashmap32_get (t->channels,
- ntohl (copen->ctn.cn));
- if (NULL != ch)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received duplicate channel CHANNEL_OPEN on port %s from %s (%s), resending ACK\n",
- GNUNET_h2s (&copen->port),
- GCT_2s (t),
- GCCH_2s (ch));
- GCCH_handle_duplicate_open (ch,
- GCC_get_id (t->current_ct->cc));
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received CHANNEL_OPEN on port %s from %s\n",
- GNUNET_h2s (&copen->port),
- GCT_2s (t));
- ch = GCCH_channel_incoming_new (t,
- copen->ctn,
- &copen->port,
- ntohl (copen->opt));
- if (NULL != t->destroy_task)
- {
- GNUNET_SCHEDULER_cancel (t->destroy_task);
- t->destroy_task = NULL;
- }
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap32_put (t->channels,
- ntohl (copen->ctn.cn),
- ch,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
-}
-
-
-/**
- * Send a DESTROY message via the tunnel.
- *
- * @param t the tunnel to transmit over
- * @param ctn ID of the channel to destroy
- */
-void
-GCT_send_channel_destroy (struct CadetTunnel *t,
- struct GNUNET_CADET_ChannelTunnelNumber ctn)
-{
- struct GNUNET_CADET_ChannelManageMessage msg;
-
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Sending DESTORY message for channel ID %u\n",
- ntohl (ctn.cn));
- msg.header.size = htons (sizeof (msg));
- msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY);
- msg.reserved = htonl (0);
- msg.ctn = ctn;
- GCT_send (t,
- &msg.header,
- NULL,
- NULL);
-}
-
-
-/**
- * We have received confirmation from the target peer that the
- * given channel could be established (the port is open).
- * Tell the client.
- *
- * @param cls the `struct CadetTunnel` for which we decrypted the message
- * @param cm the message we received on the tunnel
- */
-static void
-handle_plaintext_channel_open_ack (void *cls,
- const struct GNUNET_CADET_ChannelManageMessage *cm)
-{
- struct CadetTunnel *t = cls;
- struct CadetChannel *ch;
-
- ch = lookup_channel (t,
- cm->ctn);
- if (NULL == ch)
- {
- /* We don't know about such a channel, might have been destroyed on our
- end in the meantime, or never existed. Send back a DESTROY. */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received channel OPEN_ACK for unknown channel %u, sending DESTROY\n",
- ntohl (cm->ctn.cn));
- GCT_send_channel_destroy (t,
- cm->ctn);
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received channel OPEN_ACK on channel %s from %s\n",
- GCCH_2s (ch),
- GCT_2s (t));
- GCCH_handle_channel_open_ack (ch,
- GCC_get_id (t->current_ct->cc));
-}
-
-
-/**
- * We received a message saying that a channel should be destroyed.
- * Pass it on to the correct channel.
- *
- * @param cls the `struct CadetTunnel` for which we decrypted the message
- * @param cm the message we received on the tunnel
- */
-static void
-handle_plaintext_channel_destroy (void *cls,
- const struct GNUNET_CADET_ChannelManageMessage *cm)
-{
- struct CadetTunnel *t = cls;
- struct CadetChannel *ch;
-
- ch = lookup_channel (t,
- cm->ctn);
- if (NULL == ch)
- {
- /* We don't know about such a channel, might have been destroyed on our
- end in the meantime, or never existed. */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received channel DESTORY for unknown channel %u. Ignoring.\n",
- ntohl (cm->ctn.cn));
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received channel DESTROY on %s from %s\n",
- GCCH_2s (ch),
- GCT_2s (t));
- GCCH_handle_remote_destroy (ch,
- GCC_get_id (t->current_ct->cc));
-}
-
-
-/**
- * Handles a message we decrypted, by injecting it into
- * our message queue (which will do the dispatching).
- *
- * @param cls the `struct CadetTunnel` that got the message
- * @param msg the message
- * @return #GNUNET_OK (continue to process)
- */
-static int
-handle_decrypted (void *cls,
- const struct GNUNET_MessageHeader *msg)
-{
- struct CadetTunnel *t = cls;
-
- GNUNET_assert (NULL != t->current_ct);
- GNUNET_MQ_inject_message (t->mq,
- msg);
- return GNUNET_OK;
-}
-
-
-/**
- * Function called if we had an error processing
- * an incoming decrypted message.
- *
- * @param cls the `struct CadetTunnel`
- * @param error error code
- */
-static void
-decrypted_error_cb (void *cls,
- enum GNUNET_MQ_Error error)
-{
- GNUNET_break_op (0);
-}
-
-
-/**
- * Create a tunnel to @a destionation. Must only be called
- * from within #GCP_get_tunnel().
- *
- * @param destination where to create the tunnel to
- * @return new tunnel to @a destination
- */
-struct CadetTunnel *
-GCT_create_tunnel (struct CadetPeer *destination)
-{
- struct CadetTunnel *t = GNUNET_new (struct CadetTunnel);
- struct GNUNET_MQ_MessageHandler handlers[] = {
- GNUNET_MQ_hd_fixed_size (plaintext_keepalive,
- GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE,
- struct GNUNET_MessageHeader,
- t),
- GNUNET_MQ_hd_var_size (plaintext_data,
- GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA,
- struct GNUNET_CADET_ChannelAppDataMessage,
- t),
- GNUNET_MQ_hd_fixed_size (plaintext_data_ack,
- GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK,
- struct GNUNET_CADET_ChannelDataAckMessage,
- t),
- GNUNET_MQ_hd_fixed_size (plaintext_channel_open,
- GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN,
- struct GNUNET_CADET_ChannelOpenMessage,
- t),
- GNUNET_MQ_hd_fixed_size (plaintext_channel_open_ack,
- GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK,
- struct GNUNET_CADET_ChannelManageMessage,
- t),
- GNUNET_MQ_hd_fixed_size (plaintext_channel_destroy,
- GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY,
- struct GNUNET_CADET_ChannelManageMessage,
- t),
- GNUNET_MQ_handler_end ()
- };
-
- t->kx_retry_delay = INITIAL_KX_RETRY_DELAY;
- new_ephemeral (&t->ax);
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CRYPTO_ecdhe_key_create2 (&t->ax.kx_0));
- t->destination = destination;
- t->channels = GNUNET_CONTAINER_multihashmap32_create (8);
- t->maintain_connections_task
- = GNUNET_SCHEDULER_add_now (&maintain_connections_cb,
- t);
- t->mq = GNUNET_MQ_queue_for_callbacks (NULL,
- NULL,
- NULL,
- NULL,
- handlers,
- &decrypted_error_cb,
- t);
- t->mst = GNUNET_MST_create (&handle_decrypted,
- t);
- return t;
-}
-
-
-/**
- * Add a @a connection to the @a tunnel.
- *
- * @param t a tunnel
- * @param cid connection identifer to use for the connection
- * @param options options for the connection
- * @param path path to use for the connection
- * @return #GNUNET_OK on success,
- * #GNUNET_SYSERR on failure (duplicate connection)
- */
-int
-GCT_add_inbound_connection (struct CadetTunnel *t,
- const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
- enum GNUNET_CADET_ChannelOption options,
- struct CadetPeerPath *path)
-{
- struct CadetTConnection *ct;
-
- ct = GNUNET_new (struct CadetTConnection);
- ct->created = GNUNET_TIME_absolute_get ();
- ct->t = t;
- ct->cc = GCC_create_inbound (t->destination,
- path,
- options,
- ct,
- cid,
- &connection_ready_cb,
- ct);
- if (NULL == ct->cc)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "%s refused inbound %s (duplicate)\n",
- GCT_2s (t),
- GCC_2s (ct->cc));
- GNUNET_free (ct);
- return GNUNET_SYSERR;
- }
- /* FIXME: schedule job to kill connection (and path?) if it takes
- too long to get ready! (And track performance data on how long
- other connections took with the tunnel!)
- => Note: to be done within 'connection'-logic! */
- GNUNET_CONTAINER_DLL_insert (t->connection_busy_head,
- t->connection_busy_tail,
- ct);
- t->num_busy_connections++;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "%s has new %s\n",
- GCT_2s (t),
- GCC_2s (ct->cc));
- return GNUNET_OK;
-}
-
-
-/**
- * Handle encrypted message.
- *
- * @param ct connection/tunnel combo that received encrypted message
- * @param msg the encrypted message to decrypt
- */
-void
-GCT_handle_encrypted (struct CadetTConnection *ct,
- const struct GNUNET_CADET_TunnelEncryptedMessage *msg)
-{
- struct CadetTunnel *t = ct->t;
- uint16_t size = ntohs (msg->header.size);
- char cbuf [size] GNUNET_ALIGN;
- ssize_t decrypted_size;
-
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "%s received %u bytes of encrypted data in state %d\n",
- GCT_2s (t),
- (unsigned int) size,
- t->estate);
-
- switch (t->estate)
- {
- case CADET_TUNNEL_KEY_UNINITIALIZED:
- case CADET_TUNNEL_KEY_AX_RECV:
- /* We did not even SEND our KX, how can the other peer
- send us encrypted data? Must have been that we went
- down and the other peer still things we are up.
- Let's send it KX back. */
- GNUNET_STATISTICS_update (stats,
- "# received encrypted without any KX",
- 1,
- GNUNET_NO);
- if (NULL != t->kx_task)
- {
- GNUNET_SCHEDULER_cancel (t->kx_task);
- t->kx_task = NULL;
- }
- send_kx (t,
- ct,
- &t->ax);
- return;
- case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
- /* We send KX, and other peer send KX to us at the same time.
- Neither KX is AUTH'ed, so let's try KX_AUTH this time. */
- GNUNET_STATISTICS_update (stats,
- "# received encrypted without KX_AUTH",
- 1,
- GNUNET_NO);
- if (NULL != t->kx_task)
- {
- GNUNET_SCHEDULER_cancel (t->kx_task);
- t->kx_task = NULL;
- }
- send_kx_auth (t,
- ct,
- &t->ax,
- GNUNET_YES);
- return;
- case CADET_TUNNEL_KEY_AX_SENT:
- /* We did not get the KX of the other peer, but that
- might have been lost. Send our KX again immediately. */
- GNUNET_STATISTICS_update (stats,
- "# received encrypted without KX",
- 1,
- GNUNET_NO);
- if (NULL != t->kx_task)
- {
- GNUNET_SCHEDULER_cancel (t->kx_task);
- t->kx_task = NULL;
- }
- send_kx (t,
- ct,
- &t->ax);
- return;
- case CADET_TUNNEL_KEY_AX_AUTH_SENT:
- /* Great, first payload, we might graduate to OK! */
- case CADET_TUNNEL_KEY_OK:
- /* We are up and running, all good. */
- break;
- }
-
- decrypted_size = -1;
- if (CADET_TUNNEL_KEY_OK == t->estate)
- {
- /* We have well-established key material available,
- try that. (This is the common case.) */
- decrypted_size = t_ax_decrypt_and_validate (&t->ax,
- cbuf,
- msg,
- size);
- }
-
- if ( (-1 == decrypted_size) &&
- (NULL != t->unverified_ax) )
- {
- /* We have un-authenticated KX material available. We should try
- this as a back-up option, in case the sender crashed and
- switched keys. */
- decrypted_size = t_ax_decrypt_and_validate (t->unverified_ax,
- cbuf,
- msg,
- size);
- if (-1 != decrypted_size)
- {
- /* It worked! Treat this as authentication of the AX data! */
- cleanup_ax (&t->ax);
- t->ax = *t->unverified_ax;
- GNUNET_free (t->unverified_ax);
- t->unverified_ax = NULL;
- }
- if (CADET_TUNNEL_KEY_AX_AUTH_SENT == t->estate)
- {
- /* First time it worked, move tunnel into production! */
- GCT_change_estate (t,
- CADET_TUNNEL_KEY_OK);
- if (NULL != t->send_task)
- GNUNET_SCHEDULER_cancel (t->send_task);
- t->send_task = GNUNET_SCHEDULER_add_now (&trigger_transmissions,
- t);
- }
- }
- if (NULL != t->unverified_ax)
- {
- /* We had unverified KX material that was useless; so increment
- counter and eventually move to ignore it. Note that we even do
- this increment if we successfully decrypted with the old KX
- material and thus didn't even both with the new one. This is
- the ideal case, as a malicious injection of bogus KX data
- basically only causes us to increment a counter a few times. */
- t->unverified_attempts++;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Failed to decrypt message with unverified KX data %u times\n",
- t->unverified_attempts);
- if (t->unverified_attempts > MAX_UNVERIFIED_ATTEMPTS)
- {
- cleanup_ax (t->unverified_ax);
- GNUNET_free (t->unverified_ax);
- t->unverified_ax = NULL;
- }
- }
-
- if (-1 == decrypted_size)
- {
- /* Decryption failed for good, complain. */
- LOG (GNUNET_ERROR_TYPE_WARNING,
- "%s failed to decrypt and validate encrypted data, retrying KX\n",
- GCT_2s (t));
- GNUNET_STATISTICS_update (stats,
- "# unable to decrypt",
- 1,
- GNUNET_NO);
- if (NULL != t->kx_task)
- {
- GNUNET_SCHEDULER_cancel (t->kx_task);
- t->kx_task = NULL;
- }
- send_kx (t,
- ct,
- &t->ax);
- return;
- }
- GNUNET_STATISTICS_update (stats,
- "# decrypted bytes",
- decrypted_size,
- GNUNET_NO);
-
- /* The MST will ultimately call #handle_decrypted() on each message. */
- t->current_ct = ct;
- GNUNET_break_op (GNUNET_OK ==
- GNUNET_MST_from_buffer (t->mst,
- cbuf,
- decrypted_size,
- GNUNET_YES,
- GNUNET_NO));
- t->current_ct = NULL;
-}
-
-
-/**
- * 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 cont Continuation to call once message is really sent.
- * @param cont_cls Closure for @c cont.
- * @return Handle to cancel message
- */
-struct CadetTunnelQueueEntry *
-GCT_send (struct CadetTunnel *t,
- const struct GNUNET_MessageHeader *message,
- GCT_SendContinuation cont,
- void *cont_cls)
-{
- struct CadetTunnelQueueEntry *tq;
- uint16_t payload_size;
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_CADET_TunnelEncryptedMessage *ax_msg;
-
- if (CADET_TUNNEL_KEY_OK != t->estate)
- {
- GNUNET_break (0);
- return NULL;
- }
- payload_size = ntohs (message->size);
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Encrypting %u bytes for %s\n",
- (unsigned int) payload_size,
- GCT_2s (t));
- env = GNUNET_MQ_msg_extra (ax_msg,
- payload_size,
- GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED);
- t_ax_encrypt (&t->ax,
- &ax_msg[1],
- message,
- payload_size);
- GNUNET_STATISTICS_update (stats,
- "# encrypted bytes",
- payload_size,
- GNUNET_NO);
- ax_msg->ax_header.Ns = htonl (t->ax.Ns++);
- ax_msg->ax_header.PNs = htonl (t->ax.PNs);
- /* FIXME: we should do this once, not once per message;
- this is a point multiplication, and DHRs does not
- change all the time. */
- GNUNET_CRYPTO_ecdhe_key_get_public (&t->ax.DHRs,
- &ax_msg->ax_header.DHRs);
- t_h_encrypt (&t->ax,
- ax_msg);
- t_hmac (&ax_msg->ax_header,
- sizeof (struct GNUNET_CADET_AxHeader) + payload_size,
- 0,
- &t->ax.HKs,
- &ax_msg->hmac);
-
- tq = GNUNET_malloc (sizeof (*tq));
- tq->t = t;
- tq->env = env;
- tq->cid = &ax_msg->cid; /* will initialize 'ax_msg->cid' once we know the connection */
- tq->cont = cont;
- tq->cont_cls = cont_cls;
- GNUNET_CONTAINER_DLL_insert_tail (t->tq_head,
- t->tq_tail,
- tq);
- if (NULL != t->send_task)
- GNUNET_SCHEDULER_cancel (t->send_task);
- t->send_task
- = GNUNET_SCHEDULER_add_now (&trigger_transmissions,
- t);
- return tq;
-}
-
-
-/**
- * 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 tq Handle to the queue entry to cancel.
- */
-void
-GCT_send_cancel (struct CadetTunnelQueueEntry *tq)
-{
- struct CadetTunnel *t = tq->t;
-
- GNUNET_CONTAINER_DLL_remove (t->tq_head,
- t->tq_tail,
- tq);
- GNUNET_MQ_discard (tq->env);
- GNUNET_free (tq);
-}
-
-
-/**
- * Iterate over all connections of a tunnel.
- *
- * @param t Tunnel whose connections to iterate.
- * @param iter Iterator.
- * @param iter_cls Closure for @c iter.
- */
-void
-GCT_iterate_connections (struct CadetTunnel *t,
- GCT_ConnectionIterator iter,
- void *iter_cls)
-{
- struct CadetTConnection *n;
- for (struct CadetTConnection *ct = t->connection_ready_head;
- NULL != ct;
- ct = n)
- {
- n = ct->next;
- iter (iter_cls,
- ct);
- }
- for (struct CadetTConnection *ct = t->connection_busy_head;
- NULL != ct;
- ct = n)
- {
- n = ct->next;
- iter (iter_cls,
- ct);
- }
-}
-
-
-/**
- * Closure for #iterate_channels_cb.
- */
-struct ChanIterCls
-{
- /**
- * Function to call.
- */
- GCT_ChannelIterator iter;
-
- /**
- * Closure for @e iter.
- */
- void *iter_cls;
-};
-
-
-/**
- * Helper function for #GCT_iterate_channels.
- *
- * @param cls the `struct ChanIterCls`
- * @param key unused
- * @param value a `struct CadetChannel`
- * @return #GNUNET_OK
- */
-static int
-iterate_channels_cb (void *cls,
- uint32_t key,
- void *value)
-{
- struct ChanIterCls *ctx = cls;
- struct CadetChannel *ch = value;
-
- ctx->iter (ctx->iter_cls,
- ch);
- return GNUNET_OK;
-}
-
-
-/**
- * Iterate over all channels of a tunnel.
- *
- * @param t Tunnel whose channels to iterate.
- * @param iter Iterator.
- * @param iter_cls Closure for @c iter.
- */
-void
-GCT_iterate_channels (struct CadetTunnel *t,
- GCT_ChannelIterator iter,
- void *iter_cls)
-{
- struct ChanIterCls ctx;
-
- ctx.iter = iter;
- ctx.iter_cls = iter_cls;
- GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
- &iterate_channels_cb,
- &ctx);
-
-}
-
-
-/**
- * Call #GCCH_debug() on a channel.
- *
- * @param cls points to the log level to use
- * @param key unused
- * @param value the `struct CadetChannel` to dump
- * @return #GNUNET_OK (continue iteration)
- */
-static int
-debug_channel (void *cls,
- uint32_t key,
- void *value)
-{
- const enum GNUNET_ErrorType *level = cls;
- struct CadetChannel *ch = value;
-
- GCCH_debug (ch, *level);
- return GNUNET_OK;
-}
-
-
-#define LOG2(level, ...) GNUNET_log_from_nocheck(level,"cadet-tun",__VA_ARGS__)
-
-
-/**
- * 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 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 TUNNEL TOWARDS %s in estate %s tq_len: %u #cons: %u\n",
- GCT_2s (t),
- estate2s (t->estate),
- t->tq_len,
- GCT_count_any_connections (t));
- LOG2 (level,
- "TTT channels:\n");
- GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
- &debug_channel,
- &level);
- LOG2 (level,
- "TTT connections:\n");
- for (iter_c = t->connection_ready_head; NULL != iter_c; iter_c = iter_c->next)
- GCC_debug (iter_c->cc,
- level);
- for (iter_c = t->connection_busy_head; NULL != iter_c; iter_c = iter_c->next)
- GCC_debug (iter_c->cc,
- level);
-
- LOG2 (level,
- "TTT TUNNEL END\n");
-}
-
-
-/* end of gnunet-service-cadet-new_tunnels.c */
+++ /dev/null
-
-/*
- 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_tunnels.h
- * @brief Information we track per tunnel.
- * @author Bartlomiej Polot
- * @author Christian Grothoff
- */
-#ifndef GNUNET_SERVICE_CADET_TUNNELS_H
-#define GNUNET_SERVICE_CADET_TUNNELS_H
-
-#include "gnunet-service-cadet-new.h"
-#include "cadet_protocol.h"
-
-
-/**
- * How many connections would we like to have per tunnel?
- */
-#define DESIRED_CONNECTIONS_PER_TUNNEL 3
-
-
-/**
- * All the encryption states a tunnel can be in.
- */
-enum CadetTunnelEState
-{
- /**
- * Uninitialized status, we need to send KX. We will stay
- * in this state until the first connection is up.
- */
- CADET_TUNNEL_KEY_UNINITIALIZED,
-
- /**
- * KX message sent, waiting for other peer's KX_AUTH.
- */
- CADET_TUNNEL_KEY_AX_SENT,
-
- /**
- * KX message received, trying to send back KX_AUTH.
- */
- CADET_TUNNEL_KEY_AX_RECV,
-
- /**
- * KX message sent and received, trying to send back KX_AUTH.
- */
- CADET_TUNNEL_KEY_AX_SENT_AND_RECV,
-
- /**
- * KX received and we sent KX_AUTH back, but we got no traffic yet,
- * so we're waiting for either KX_AUTH or ENCRYPED traffic from
- * the other peer.
- *
- * We will not yet send traffic, as this might have been a replay.
- * The other (initiating) peer should send a CHANNEL_OPEN next
- * anyway, and then we are in business!
- */
- CADET_TUNNEL_KEY_AX_AUTH_SENT,
-
- /**
- * Handshake completed: session key available.
- */
- CADET_TUNNEL_KEY_OK
-
-};
-
-
-/**
- * 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);
-
-
-/**
- * Create a tunnel to @a destionation. Must only be called
- * from within #GCP_get_tunnel().
- *
- * @param destination where to create the tunnel to
- * @return new tunnel to @a destination
- */
-struct CadetTunnel *
-GCT_create_tunnel (struct CadetPeer *destination);
-
-
-/**
- * Destroys the tunnel @a t now, without delay. Used during shutdown.
- *
- * @param t tunnel to destroy
- */
-void
-GCT_destroy_tunnel_now (struct CadetTunnel *t);
-
-
-/**
- * Add a @a connection to the @a tunnel.
- *
- * @param t a tunnel
- * @param cid connection identifer to use for the connection
- * @param options options for the connection
- * @param path path to use for the connection
- * @return #GNUNET_OK on success,
- * #GNUNET_SYSERR on failure (duplicate connection)
- */
-int
-GCT_add_inbound_connection (struct CadetTunnel *t,
- const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
- enum GNUNET_CADET_ChannelOption options,
- struct CadetPeerPath *path);
-
-
-/**
- * We lost a connection, remove it from our list and clean up
- * the connection object itself.
- *
- * @param ct binding of connection to tunnel of the connection that was lost.
- */
-void
-GCT_connection_lost (struct CadetTConnection *ct);
-
-
-/**
- * Return the peer to which this tunnel goes.
- *
- * @param t a tunnel
- * @return the destination of the tunnel
- */
-struct CadetPeer *
-GCT_get_destination (struct CadetTunnel *t);
-
-
-/**
- * Consider using the path @a p for the tunnel @a t.
- * The tunnel destination is at offset @a off in path @a p.
- *
- * @param cls our tunnel
- * @param path a path to our destination
- * @param off offset of the destination on path @a path
- */
-void
-GCT_consider_path (struct CadetTunnel *t,
- struct CadetPeerPath *p,
- unsigned int off);
-
-
-/**
- * Add a channel to a tunnel.
- *
- * @param t Tunnel.
- * @param ch Channel
- * @return unique number identifying @a ch within @a t
- */
-struct GNUNET_CADET_ChannelTunnelNumber
-GCT_add_channel (struct CadetTunnel *t,
- struct CadetChannel *ch);
-
-
-/**
- * Remove a channel from a tunnel.
- *
- * @param t Tunnel.
- * @param ch Channel
- * @param ctn unique number identifying @a ch within @a t
- */
-void
-GCT_remove_channel (struct CadetTunnel *t,
- struct CadetChannel *ch,
- struct GNUNET_CADET_ChannelTunnelNumber ctn);
-
-
-/**
- * Send a DESTROY message via the tunnel.
- *
- * @param t the tunnel to transmit over
- * @param ctn ID of the channel to destroy
- */
-void
-GCT_send_channel_destroy (struct CadetTunnel *t,
- struct GNUNET_CADET_ChannelTunnelNumber ctn);
-
-
-/**
- * Function called when a transmission requested using #GCT_send is done.
- *
- * @param cls closure
- * @param ctn identifier of the connection used for transmission, NULL if
- * the transmission failed (to be used to match ACKs to the
- * respective connection for connection performance evaluation)
- */
-typedef void
-(*GCT_SendContinuation)(void *cls,
- const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid);
-
-
-/**
- * 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 cont Continuation to call once message is really sent.
- * @param cont_cls Closure for @c cont.
- * @return Handle to cancel message.
- */
-struct CadetTunnelQueueEntry *
-GCT_send (struct CadetTunnel *t,
- const struct GNUNET_MessageHeader *message,
- GCT_SendContinuation cont,
- void *cont_cls);
-
-
-/**
- * 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 entry to cancel.
- */
-void
-GCT_send_cancel (struct CadetTunnelQueueEntry *q);
-
-
-/**
- * Return the number of channels using a tunnel.
- *
- * @param t tunnel to count obtain the number of channels for
- * @return number of channels using the tunnel
- */
-unsigned int
-GCT_count_channels (struct CadetTunnel *t);
-
-
-/**
- * Return the number of connections available for a tunnel.
- *
- * @param t tunnel to count obtain the number of connections for
- * @return number of connections available for the tunnel
- */
-unsigned int
-GCT_count_any_connections (const struct CadetTunnel *t);
-
-
-/**
- * Iterator over connections.
- *
- * @param cls closure
- * @param ct one of the connections
- */
-typedef void
-(*GCT_ConnectionIterator) (void *cls,
- struct CadetTConnection *ct);
-
-
-/**
- * Iterate over all connections of a tunnel.
- *
- * @param t Tunnel whose connections to iterate.
- * @param iter Iterator.
- * @param iter_cls Closure for @c iter.
- */
-void
-GCT_iterate_connections (struct CadetTunnel *t,
- GCT_ConnectionIterator iter,
- void *iter_cls);
-
-
-/**
- * Iterator over channels.
- *
- * @param cls closure
- * @param ch one of the channels
- */
-typedef void
-(*GCT_ChannelIterator) (void *cls,
- struct CadetChannel *ch);
-
-
-/**
- * Iterate over all channels of a tunnel.
- *
- * @param t Tunnel whose channels to iterate.
- * @param iter Iterator.
- * @param iter_cls Closure for @c iter.
- */
-void
-GCT_iterate_channels (struct CadetTunnel *t,
- GCT_ChannelIterator iter,
- void *iter_cls);
-
-
-/**
- * Get the encryption state of a tunnel.
- *
- * @param t Tunnel.
- *
- * @return Tunnel's encryption state.
- */
-enum CadetTunnelEState
-GCT_get_estate (struct CadetTunnel *t);
-
-
-/**
- * Handle KX message.
- *
- * @param ct connection/tunnel combo that received encrypted message
- * @param msg the key exchange message
- */
-void
-GCT_handle_kx (struct CadetTConnection *ct,
- const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg);
-
-
-/**
- * Handle KX_AUTH message.
- *
- * @param ct connection/tunnel combo that received encrypted message
- * @param msg the key exchange message
- */
-void
-GCT_handle_kx_auth (struct CadetTConnection *ct,
- const struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg);
-
-
-/**
- * Handle encrypted message.
- *
- * @param ct connection/tunnel combo that received encrypted message
- * @param msg the encrypted message to decrypt
- */
-void
-GCT_handle_encrypted (struct CadetTConnection *ct,
- const struct GNUNET_CADET_TunnelEncryptedMessage *msg);
-
-
-/**
- * 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);
-
-
-#endif
--- /dev/null
+/*
+ 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.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.h"
+#include "gnunet-service-cadet_channel.h"
+#include "gnunet-service-cadet_connection.h"
+#include "gnunet-service-cadet_core.h"
+#include "gnunet-service-cadet_dht.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 ****************************/
+/******************************************************************************/
+
+/****************************** 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 */
--- /dev/null
+
+/*
+ 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.h
+ * @brief Information we track per peer.
+ * @author Bartlomiej Polot
+ * @author Christian Grothoff
+ */
+#ifndef GNUNET_SERVICE_CADET_H
+#define GNUNET_SERVICE_CADET_H
+
+#include "gnunet_util_lib.h"
+#define NEW_CADET 1
+#include "cadet_protocol.h"
+
+/**
+ * A client to the CADET service. Each client gets a unique handle.
+ */
+struct CadetClient;
+
+/**
+ * A peer in the GNUnet network. Each peer we care about must have one globally
+ * unique such handle within this process.
+ */
+struct CadetPeer;
+
+/**
+ * Tunnel from us to another peer. There can only be at most one
+ * tunnel per peer.
+ */
+struct CadetTunnel;
+
+/**
+ * Entry in the message queue of a `struct CadetTunnel`.
+ */
+struct CadetTunnelQueueEntry;
+
+/**
+ * A path of peer in the GNUnet network. There must only be at most
+ * once such path. Paths may share disjoint prefixes, but must all
+ * end at a unique suffix. Paths must also not be proper subsets of
+ * other existing paths.
+ */
+struct CadetPeerPath;
+
+/**
+ * Entry in a peer path.
+ */
+struct CadetPeerPathEntry
+{
+ /**
+ * DLL of paths where the same @e peer is at the same offset.
+ */
+ struct CadetPeerPathEntry *next;
+
+ /**
+ * DLL of paths where the same @e peer is at the same offset.
+ */
+ struct CadetPeerPathEntry *prev;
+
+ /**
+ * The peer at this offset of the path.
+ */
+ struct CadetPeer *peer;
+
+ /**
+ * Path this entry belongs to.
+ */
+ struct CadetPeerPath *path;
+
+ /**
+ * Connection using this path, or NULL for none.
+ */
+ struct CadetConnection *cc;
+
+ /**
+ * Path's historic score up to this point. Basically, how often did
+ * we succeed or fail to use the path up to this entry in a
+ * connection. Positive values indicate good experiences, negative
+ * values bad experiences. Code updating the score must guard
+ * against overflows.
+ */
+ int score;
+
+};
+
+/**
+ * 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 *cc;
+
+ /**
+ * Tunnel this connection belongs to.
+ */
+ struct CadetTunnel *t;
+
+ /**
+ * Creation time, to keep oldest connection alive.
+ */
+ struct GNUNET_TIME_Absolute created;
+
+ /**
+ * Connection throughput, to keep fastest connection alive.
+ */
+ uint32_t throughput;
+
+ /**
+ * Is the connection currently ready for transmission?
+ */
+ int is_ready;
+};
+
+
+/**
+ * Active path through the network (used by a tunnel). There may
+ * be at most one connection per path.
+ */
+struct CadetConnection;
+
+/**
+ * Description of a segment of a `struct CadetConnection` at the
+ * intermediate peers. Routes are basically entries in a peer's
+ * routing table for forwarding traffic. At both endpoints, the
+ * routes are terminated by a `struct CadetConnection`, which knows
+ * the complete `struct CadetPath` that is formed by the individual
+ * routes.
+ */
+struct CadetRoute;
+
+/**
+ * Logical end-to-end conenction between clients. There can be
+ * any number of channels between clients.
+ */
+struct CadetChannel;
+
+/**
+ * Handle to our configuration.
+ */
+extern const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Handle to the statistics service.
+ */
+extern struct GNUNET_STATISTICS_Handle *stats;
+
+/**
+ * Handle to communicate with ATS.
+ */
+extern struct GNUNET_ATS_ConnectivityHandle *ats_ch;
+
+/**
+ * Local peer own ID.
+ */
+extern struct GNUNET_PeerIdentity my_full_id;
+
+/**
+ * Own private key.
+ */
+extern struct GNUNET_CRYPTO_EddsaPrivateKey *my_private_key;
+
+/**
+ * All ports clients of this peer have opened.
+ */
+extern struct GNUNET_CONTAINER_MultiHashMap *open_ports;
+
+/**
+ * Map from `struct GNUNET_CADET_ConnectionTunnelIdentifier`
+ * hash codes to `struct CadetConnection` objects.
+ */
+extern struct GNUNET_CONTAINER_MultiShortmap *connections;
+
+/**
+ * Map from ports to channels where the ports were closed at the
+ * time we got the inbound connection.
+ * Indexed by port, contains `struct CadetChannel`.
+ */
+extern struct GNUNET_CONTAINER_MultiHashMap *loose_channels;
+
+/**
+ * Map from PIDs to `struct CadetPeer` entries.
+ */
+extern struct GNUNET_CONTAINER_MultiPeerMap *peers;
+
+/**
+ * How many messages are needed to trigger an AXOLOTL ratchet advance.
+ */
+extern unsigned long long ratchet_messages;
+
+/**
+ * How long until we trigger a ratched advance due to time.
+ */
+extern struct GNUNET_TIME_Relative ratchet_time;
+
+/**
+ * How frequently do we send KEEPALIVE messages on idle connections?
+ */
+extern struct GNUNET_TIME_Relative keepalive_period;
+
+/**
+ * Signal that shutdown is happening: prevent recovery measures.
+ */
+extern int shutting_down;
+
+/**
+ * Set to non-zero values to create random drops to test retransmissions.
+ */
+extern 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);
+
+
+/**
+ * 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);
+
+/**
+ * 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);
+
+
+/**
+ * Bind incoming channel to this client, and notify client
+ * about incoming connection.
+ *
+ * @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);
+
+
+/**
+ * Return identifier for a client as a string.
+ *
+ * @param c client to identify
+ * @return string for debugging
+ */
+const char *
+GSC_2s (struct CadetClient *c);
+
+
+#endif
--- /dev/null
+/*
+ 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_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 "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"
+
+#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 */
--- /dev/null
+
+/*
+ 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_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.h"
+#include "gnunet-service-cadet_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
--- /dev/null
+/*
+ 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_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_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_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 */
--- /dev/null
+
+/*
+ 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_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.h"
+#include "gnunet-service-cadet_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
--- /dev/null
+/*
+ This file is part of GNUnet.
+ Copyright (C) 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_core.c
+ * @brief cadet service; interaction with CORE service
+ * @author Bartlomiej Polot
+ * @author Christian Grothoff
+ *
+ * All functions in this file should use the prefix GCO (Gnunet Cadet cOre (bottom))
+ *
+ * TODO:
+ * - Optimization: given BROKEN messages, destroy paths (?)
+ */
+#include "platform.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"
+
+
+#define LOG(level, ...) GNUNET_log_from(level,"cadet-cor",__VA_ARGS__)
+
+/**
+ * Information we keep per direction for a route.
+ */
+struct RouteDirection;
+
+
+/**
+ * Set of CadetRoutes that have exactly the same number of messages
+ * in their buffer. Used so we can efficiently find all of those
+ * routes that have the current maximum of messages in the buffer (in
+ * case we have to purge).
+ */
+struct Rung
+{
+
+ /**
+ * Rung of RouteDirections with one more buffer entry each.
+ */
+ struct Rung *next;
+
+ /**
+ * Rung of RouteDirections with one less buffer entry each.
+ */
+ struct Rung *prev;
+
+ /**
+ * DLL of route directions with a number of buffer entries matching this rung.
+ */
+ struct RouteDirection *rd_head;
+
+ /**
+ * DLL of route directions with a number of buffer entries matching this rung.
+ */
+ struct RouteDirection *rd_tail;
+
+ /**
+ * Total number of route directions in this rung.
+ */
+ unsigned int num_routes;
+
+ /**
+ * Number of messages route directions at this rung have
+ * in their buffer.
+ */
+ unsigned int rung_off;
+};
+
+
+/**
+ * Information we keep per direction for a route.
+ */
+struct RouteDirection
+{
+
+ /**
+ * DLL of other route directions within the same `struct Rung`.
+ */
+ struct RouteDirection *prev;
+
+ /**
+ * DLL of other route directions within the same `struct Rung`.
+ */
+ struct RouteDirection *next;
+
+ /**
+ * Rung of this route direction (matches length of the buffer DLL).
+ */
+ struct Rung *rung;
+
+ /**
+ * Head of DLL of envelopes we have in the buffer for this direction.
+ */
+ struct GNUNET_MQ_Envelope *env_head;
+
+ /**
+ * Tail of DLL of envelopes we have in the buffer for this direction.
+ */
+ struct GNUNET_MQ_Envelope *env_tail;
+
+ /**
+ * Target peer.
+ */
+ struct CadetPeer *hop;
+
+ /**
+ * Route this direction is part of.
+ */
+ struct CadetRoute *my_route;
+
+ /**
+ * Message queue manager for @e hop.
+ */
+ struct GCP_MessageQueueManager *mqm;
+
+ /**
+ * Is @e mqm currently ready for transmission?
+ */
+ int is_ready;
+
+};
+
+
+/**
+ * Description of a segment of a `struct CadetConnection` at the
+ * intermediate peers. Routes are basically entries in a peer's
+ * routing table for forwarding traffic. At both endpoints, the
+ * routes are terminated by a `struct CadetConnection`, which knows
+ * the complete `struct CadetPath` that is formed by the individual
+ * routes.
+ */
+struct CadetRoute
+{
+
+ /**
+ * Information about the next hop on this route.
+ */
+ struct RouteDirection next;
+
+ /**
+ * Information about the previous hop on this route.
+ */
+ struct RouteDirection prev;
+
+ /**
+ * Unique identifier for the connection that uses this route.
+ */
+ struct GNUNET_CADET_ConnectionTunnelIdentifier cid;
+
+ /**
+ * When was this route last in use?
+ */
+ struct GNUNET_TIME_Absolute last_use;
+
+ /**
+ * Position of this route in the #route_heap.
+ */
+ struct GNUNET_CONTAINER_HeapNode *hn;
+
+ /**
+ * Options for the route, control buffering.
+ */
+ enum GNUNET_CADET_ChannelOption options;
+};
+
+
+/**
+ * Handle to the CORE service.
+ */
+static struct GNUNET_CORE_Handle *core;
+
+/**
+ * Routes on which this peer is an intermediate.
+ */
+static struct GNUNET_CONTAINER_MultiShortmap *routes;
+
+/**
+ * Heap of routes, MIN-sorted by last activity.
+ */
+static struct GNUNET_CONTAINER_Heap *route_heap;
+
+/**
+ * Rung zero (always pointed to by #rung_head).
+ */
+static struct Rung rung_zero;
+
+/**
+ * DLL of rungs, with the head always point to a rung of
+ * route directions with no messages in the queue.
+ */
+static struct Rung *rung_head = &rung_zero;
+
+/**
+ * Tail of the #rung_head DLL.
+ */
+static struct Rung *rung_tail = &rung_zero;
+
+/**
+ * Maximum number of concurrent routes this peer will support.
+ */
+static unsigned long long max_routes;
+
+/**
+ * Maximum number of envelopes we will buffer at this peer.
+ */
+static unsigned long long max_buffers;
+
+/**
+ * Current number of envelopes we have buffered at this peer.
+ */
+static unsigned long long cur_buffers;
+
+/**
+ * Task to timeout routes.
+ */
+static struct GNUNET_SCHEDULER_Task *timeout_task;
+
+
+/**
+ * Get the route corresponding to a hash.
+ *
+ * @param cid hash generated from the connection identifier
+ */
+static struct CadetRoute *
+get_route (const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid)
+{
+ return GNUNET_CONTAINER_multishortmap_get (routes,
+ &cid->connection_of_tunnel);
+}
+
+
+/**
+ * Lower the rung in which @a dir is by 1.
+ *
+ * @param dir direction to lower in rung.
+ */
+static void
+lower_rung (struct RouteDirection *dir)
+{
+ struct Rung *rung = dir->rung;
+ struct Rung *prev;
+
+ GNUNET_CONTAINER_DLL_remove (rung->rd_head,
+ rung->rd_tail,
+ dir);
+ prev = rung->prev;
+ GNUNET_assert (NULL != prev);
+ if (prev->rung_off != rung->rung_off - 1)
+ {
+ prev = GNUNET_new (struct Rung);
+ prev->rung_off = rung->rung_off - 1;
+ GNUNET_CONTAINER_DLL_insert_after (rung_head,
+ rung_tail,
+ rung->prev,
+ prev);
+ }
+ GNUNET_assert (NULL != prev);
+ GNUNET_CONTAINER_DLL_insert (prev->rd_head,
+ prev->rd_tail,
+ dir);
+ dir->rung = prev;
+}
+
+
+/**
+ * Discard the buffer @a env from the route direction @a dir and
+ * move @a dir down a rung.
+ *
+ * @param dir direction that contains the @a env in the buffer
+ * @param env envelope to discard
+ */
+static void
+discard_buffer (struct RouteDirection *dir,
+ struct GNUNET_MQ_Envelope *env)
+{
+ GNUNET_MQ_dll_remove (&dir->env_head,
+ &dir->env_tail,
+ env);
+ cur_buffers--;
+ GNUNET_MQ_discard (env);
+ lower_rung (dir);
+ GNUNET_STATISTICS_set (stats,
+ "# buffer use",
+ cur_buffers,
+ GNUNET_NO);
+}
+
+
+/**
+ * Discard all messages from the highest rung, to make space.
+ */
+static void
+discard_all_from_rung_tail ()
+{
+ struct Rung *tail = rung_tail;
+ struct RouteDirection *dir;
+
+ while (NULL != (dir = tail->rd_head))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Queue full due new message %s on connection %s, dropping old message\n",
+ GNUNET_sh2s (&dir->my_route->cid.connection_of_tunnel));
+ GNUNET_STATISTICS_update (stats,
+ "# messages dropped due to full buffer",
+ 1,
+ GNUNET_NO);
+ discard_buffer (dir,
+ dir->env_head);
+ }
+ GNUNET_CONTAINER_DLL_remove (rung_head,
+ rung_tail,
+ tail);
+ GNUNET_free (tail);
+}
+
+
+/**
+ * We message @a msg from @a prev. Find its route by @a cid and
+ * forward to the next hop. Drop and signal broken route if we do not
+ * have a route.
+ *
+ * @param prev previous hop (sender)
+ * @param cid connection identifier, tells us which route to use
+ * @param msg the message to forward
+ */
+static void
+route_message (struct CadetPeer *prev,
+ const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
+ const struct GNUNET_MessageHeader *msg)
+{
+ struct CadetRoute *route;
+ struct RouteDirection *dir;
+ struct Rung *rung;
+ struct Rung *nxt;
+ struct GNUNET_MQ_Envelope *env;
+
+ route = get_route (cid);
+ if (NULL == route)
+ {
+ struct GNUNET_MQ_Envelope *env;
+ struct GNUNET_CADET_ConnectionBrokenMessage *bm;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Failed to route message of type %u from %s on connection %s: no route\n",
+ ntohs (msg->type),
+ GCP_2s (prev),
+ GNUNET_sh2s (&cid->connection_of_tunnel));
+ switch (ntohs (msg->type))
+ {
+ case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY:
+ case GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN:
+ /* No need to respond to these! */
+ return;
+ }
+ env = GNUNET_MQ_msg (bm,
+ GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN);
+ bm->cid = *cid;
+ bm->peer1 = my_full_id;
+ GCP_send_ooo (prev,
+ env);
+ return;
+ }
+ route->last_use = GNUNET_TIME_absolute_get ();
+ GNUNET_CONTAINER_heap_update_cost (route->hn,
+ route->last_use.abs_value_us);
+ dir = (prev == route->prev.hop) ? &route->next : &route->prev;
+ if (GNUNET_YES == dir->is_ready)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Routing message of type %u from %s to %s on connection %s\n",
+ ntohs (msg->type),
+ GCP_2s (prev),
+ GNUNET_i2s (GCP_get_id (dir->hop)),
+ GNUNET_sh2s (&cid->connection_of_tunnel));
+ dir->is_ready = GNUNET_NO;
+ GCP_send (dir->mqm,
+ GNUNET_MQ_msg_copy (msg));
+ return;
+ }
+ /* Check if buffering is disallowed, and if so, make sure we only queue
+ one message per direction. */
+ if ( (0 != (route->options & GNUNET_CADET_OPTION_NOBUFFER)) &&
+ (NULL != dir->env_head) )
+ discard_buffer (dir,
+ dir->env_head);
+ rung = dir->rung;
+ if (cur_buffers == max_buffers)
+ {
+ /* Need to make room. */
+ if (NULL != rung->next)
+ {
+ /* Easy case, drop messages from route directions in highest rung */
+ discard_all_from_rung_tail ();
+ }
+ else
+ {
+ /* We are in the highest rung, drop our own! */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Queue full due new message %s on connection %s, dropping old message\n",
+ GNUNET_sh2s (&dir->my_route->cid.connection_of_tunnel));
+ GNUNET_STATISTICS_update (stats,
+ "# messages dropped due to full buffer",
+ 1,
+ GNUNET_NO);
+ discard_buffer (dir,
+ dir->env_head);
+ rung = dir->rung;
+ }
+ }
+ /* remove 'dir' from current rung */
+ GNUNET_CONTAINER_DLL_remove (rung->rd_head,
+ rung->rd_tail,
+ dir);
+ /* make 'nxt' point to the next higher rung, creat if necessary */
+ nxt = rung->next;
+ if ( (NULL == nxt) ||
+ (rung->rung_off + 1 != nxt->rung_off) )
+ {
+ nxt = GNUNET_new (struct Rung);
+ nxt->rung_off = rung->rung_off + 1;
+ GNUNET_CONTAINER_DLL_insert_after (rung_head,
+ rung_tail,
+ rung,
+ nxt);
+ }
+ /* insert 'dir' into next higher rung */
+ GNUNET_CONTAINER_DLL_insert (nxt->rd_head,
+ nxt->rd_tail,
+ dir);
+ dir->rung = nxt;
+
+ /* add message into 'dir' buffer */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Queueing new message of type %u from %s to %s on connection %s\n",
+ ntohs (msg->type),
+ GCP_2s (prev),
+ GNUNET_i2s (GCP_get_id (dir->hop)),
+ GNUNET_sh2s (&cid->connection_of_tunnel));
+ env = GNUNET_MQ_msg_copy (msg);
+ GNUNET_MQ_dll_insert_tail (&dir->env_head,
+ &dir->env_tail,
+ env);
+ cur_buffers++;
+ GNUNET_STATISTICS_set (stats,
+ "# buffer use",
+ cur_buffers,
+ GNUNET_NO);
+ /* Clean up 'rung' if now empty (and not head) */
+ if ( (NULL == rung->rd_head) &&
+ (rung != rung_head) )
+ {
+ GNUNET_CONTAINER_DLL_remove (rung_head,
+ rung_tail,
+ rung);
+ GNUNET_free (rung);
+ }
+}
+
+
+/**
+ * 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_connection_create (void *cls,
+ const struct GNUNET_CADET_ConnectionCreateMessage *msg)
+{
+ uint16_t size = ntohs (msg->header.size) - sizeof (*msg);
+
+ if (0 != (size % sizeof (struct GNUNET_PeerIdentity)))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_NO;
+ }
+ return GNUNET_YES;
+}
+
+
+/**
+ * Free internal data of a route direction.
+ *
+ * @param dir direction to destroy (do NOT free memory of 'dir' itself)
+ */
+static void
+destroy_direction (struct RouteDirection *dir)
+{
+ struct GNUNET_MQ_Envelope *env;
+
+ while (NULL != (env = dir->env_head))
+ {
+ GNUNET_STATISTICS_update (stats,
+ "# messages dropped due to route destruction",
+ 1,
+ GNUNET_NO);
+ discard_buffer (dir,
+ env);
+ }
+ if (NULL != dir->mqm)
+ {
+ GCP_request_mq_cancel (dir->mqm,
+ NULL);
+ dir->mqm = NULL;
+ }
+ GNUNET_CONTAINER_DLL_remove (rung_head->rd_head,
+ rung_head->rd_tail,
+ dir);
+}
+
+
+/**
+ * Destroy our state for @a route.
+ *
+ * @param route route to destroy
+ */
+static void
+destroy_route (struct CadetRoute *route)
+{
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Destroying route from %s to %s of connection %s\n",
+ GNUNET_i2s (GCP_get_id (route->prev.hop)),
+ GNUNET_i2s2 (GCP_get_id (route->next.hop)),
+ GNUNET_sh2s (&route->cid.connection_of_tunnel));
+ GNUNET_assert (route ==
+ GNUNET_CONTAINER_heap_remove_node (route->hn));
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multishortmap_remove (routes,
+ &route->cid.connection_of_tunnel,
+ route));
+ GNUNET_STATISTICS_set (stats,
+ "# routes",
+ GNUNET_CONTAINER_multishortmap_size (routes),
+ GNUNET_NO);
+ destroy_direction (&route->prev);
+ destroy_direction (&route->next);
+ GNUNET_free (route);
+}
+
+
+/**
+ * Send message that a route is broken between @a peer1 and @a peer2.
+ *
+ * @param target where to send the message
+ * @param cid connection identifier to use
+ * @param peer1 one of the peers where a link is broken
+ * @param peer2 another one of the peers where a link is broken
+ */
+static void
+send_broken (struct RouteDirection *target,
+ const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
+ const struct GNUNET_PeerIdentity *peer1,
+ const struct GNUNET_PeerIdentity *peer2)
+{
+ struct GNUNET_MQ_Envelope *env;
+ struct GNUNET_CADET_ConnectionBrokenMessage *bm;
+
+ if (NULL == target->mqm)
+ return; /* Can't send notification, connection is down! */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Notifying %s about BROKEN route at %s-%s of connection %s\n",
+ GCP_2s (target->hop),
+ GNUNET_i2s (peer1),
+ GNUNET_i2s2 (peer2),
+ GNUNET_sh2s (&cid->connection_of_tunnel));
+
+ env = GNUNET_MQ_msg (bm,
+ GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN);
+ bm->cid = *cid;
+ if (NULL != peer1)
+ bm->peer1 = *peer1;
+ if (NULL != peer2)
+ bm->peer2 = *peer2;
+ GCP_request_mq_cancel (target->mqm,
+ env);
+ target->mqm = NULL;
+}
+
+
+/**
+ * Function called to check if any routes have timed out, and if
+ * so, to clean them up. Finally, schedules itself again at the
+ * earliest time where there might be more work.
+ *
+ * @param cls NULL
+ */
+static void
+timeout_cb (void *cls)
+{
+ struct CadetRoute *r;
+ struct GNUNET_TIME_Relative linger;
+ struct GNUNET_TIME_Absolute exp;
+
+ timeout_task = NULL;
+ linger = GNUNET_TIME_relative_multiply (keepalive_period,
+ 3);
+ while (NULL != (r = GNUNET_CONTAINER_heap_peek (route_heap)))
+ {
+ exp = GNUNET_TIME_absolute_add (r->last_use,
+ linger);
+ if (0 != GNUNET_TIME_absolute_get_duration (exp).rel_value_us)
+ {
+ /* Route not yet timed out, wait until it does. */
+ timeout_task = GNUNET_SCHEDULER_add_at (exp,
+ &timeout_cb,
+ NULL);
+ return;
+ }
+ send_broken (&r->prev,
+ &r->cid,
+ NULL,
+ NULL);
+ send_broken (&r->next,
+ &r->cid,
+ NULL,
+ NULL);
+ destroy_route (r);
+ }
+ /* No more routes left, so no need for a #timeout_task */
+}
+
+
+/**
+ * Function called when the message queue to the previous hop
+ * becomes available/unavailable. We expect this function to
+ * be called immediately when we register, and then again
+ * later if the connection ever goes down.
+ *
+ * @param cls the `struct RouteDirection`
+ * @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
+dir_ready_cb (void *cls,
+ int ready)
+{
+ struct RouteDirection *dir = cls;
+ struct CadetRoute *route = dir->my_route;
+ struct RouteDirection *odir;
+
+ if (GNUNET_YES == ready)
+ {
+ struct GNUNET_MQ_Envelope *env;
+
+ dir->is_ready = GNUNET_YES;
+ if (NULL != (env = dir->env_head))
+ {
+ GNUNET_MQ_dll_remove (&dir->env_head,
+ &dir->env_tail,
+ env);
+ cur_buffers--;
+ GNUNET_STATISTICS_set (stats,
+ "# buffer use",
+ cur_buffers,
+ GNUNET_NO);
+ lower_rung (dir);
+ dir->is_ready = GNUNET_NO;
+ GCP_send (dir->mqm,
+ env);
+ }
+ return;
+ }
+ odir = (dir == &route->next) ? &route->prev : &route->next;
+ send_broken (&route->next,
+ &route->cid,
+ GCP_get_id (odir->hop),
+ &my_full_id);
+ destroy_route (route);
+}
+
+
+/**
+ * Initialize one of the directions of a route.
+ *
+ * @param route route the direction belongs to
+ * @param dir direction to initialize
+ * @param hop next hop on in the @a dir
+ */
+static void
+dir_init (struct RouteDirection *dir,
+ struct CadetRoute *route,
+ struct CadetPeer *hop)
+{
+ dir->hop = hop;
+ dir->my_route = route;
+ dir->mqm = GCP_request_mq (hop,
+ &dir_ready_cb,
+ dir);
+ GNUNET_CONTAINER_DLL_insert (rung_head->rd_head,
+ rung_head->rd_tail,
+ dir);
+ dir->rung = rung_head;
+ GNUNET_assert (GNUNET_YES == dir->is_ready);
+}
+
+
+/**
+ * We could not create the desired route. Send a
+ * #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN
+ * message to @a target.
+ *
+ * @param target who should receive the message
+ * @param cid identifier of the connection/route that failed
+ * @param failure_at neighbour with which we failed to route,
+ * or NULL.
+ */
+static void
+send_broken_without_mqm (struct CadetPeer *target,
+ const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
+ const struct GNUNET_PeerIdentity *failure_at)
+{
+ struct GNUNET_MQ_Envelope *env;
+ struct GNUNET_CADET_ConnectionBrokenMessage *bm;
+
+ env = GNUNET_MQ_msg (bm,
+ GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN);
+ bm->cid = *cid;
+ bm->peer1 = my_full_id;
+ if (NULL != failure_at)
+ bm->peer2 = *failure_at;
+ GCP_send_ooo (target,
+ env);
+}
+
+
+/**
+ * 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_connection_create (void *cls,
+ const struct GNUNET_CADET_ConnectionCreateMessage *msg)
+{
+ struct CadetPeer *sender = cls;
+ struct CadetPeer *next;
+ const struct GNUNET_PeerIdentity *pids = (const struct GNUNET_PeerIdentity *) &msg[1];
+ struct CadetRoute *route;
+ uint16_t size = ntohs (msg->header.size) - sizeof (*msg);
+ unsigned int path_length;
+ unsigned int off;
+ enum GNUNET_CADET_ChannelOption options;
+
+ options = (enum GNUNET_CADET_ChannelOption) ntohl (msg->options);
+ path_length = size / sizeof (struct GNUNET_PeerIdentity);
+ /* Initiator is at offset 0. */
+ for (off=1;off<path_length;off++)
+ if (0 == memcmp (&my_full_id,
+ &pids[off],
+ sizeof (struct GNUNET_PeerIdentity)))
+ break;
+ if (off == path_length)
+ {
+ /* We are not on the path, bogus request */
+ GNUNET_break_op (0);
+ return;
+ }
+ /* Check previous hop */
+ if (sender != GCP_get (&pids[off - 1],
+ GNUNET_NO))
+ {
+ /* sender is not on the path, not allowed */
+ GNUNET_break_op (0);
+ return;
+ }
+ if (NULL !=
+ get_route (&msg->cid))
+ {
+ /* Duplicate CREATE, pass it on, previous one might have been lost! */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Passing on duplicate CADET_CONNECTION_CREATE message on connection %s\n",
+ GNUNET_sh2s (&msg->cid.connection_of_tunnel));
+ route_message (sender,
+ &msg->cid,
+ &msg->header);
+ return;
+ }
+ if (off == path_length - 1)
+ {
+ /* We are the destination, create connection */
+ struct CadetConnection *cc;
+ struct CadetPeerPath *path;
+ struct CadetPeer *origin;
+
+ cc = GCC_lookup (&msg->cid);
+ if (NULL != cc)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received duplicate CADET_CONNECTION_CREATE message on connection %s\n",
+ GNUNET_sh2s (&msg->cid.connection_of_tunnel));
+ GCC_handle_duplicate_create (cc);
+ return;
+ }
+
+ origin = GCP_get (&pids[0],
+ GNUNET_YES);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received CADET_CONNECTION_CREATE message from %s for connection %s, building inverse path\n",
+ GCP_2s (origin),
+ GNUNET_sh2s (&msg->cid.connection_of_tunnel));
+ path = GCPP_get_path_from_route (path_length - 1,
+ pids);
+ if (GNUNET_OK !=
+ GCT_add_inbound_connection (GCP_get_tunnel (origin,
+ GNUNET_YES),
+ &msg->cid,
+ (enum GNUNET_CADET_ChannelOption) ntohl (msg->options),
+ path))
+ {
+ /* Send back BROKEN: duplicate connection on the same path,
+ we will use the other one. */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received CADET_CONNECTION_CREATE from %s for %s, but %s already has a connection. Sending BROKEN\n",
+ GCP_2s (sender),
+ GNUNET_sh2s (&msg->cid.connection_of_tunnel),
+ GCPP_2s (path));
+ send_broken_without_mqm (sender,
+ &msg->cid,
+ NULL);
+ return;
+ }
+ return;
+ }
+ /* We are merely a hop on the way, check if we can support the route */
+ next = GCP_get (&pids[off + 1],
+ GNUNET_NO);
+ if ( (NULL == next) ||
+ (GNUNET_NO == GCP_has_core_connection (next)) )
+ {
+ /* unworkable, send back BROKEN notification */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received CADET_CONNECTION_CREATE from %s for %s. Next hop %s:%u is down. Sending BROKEN\n",
+ GCP_2s (sender),
+ GNUNET_sh2s (&msg->cid.connection_of_tunnel),
+ GNUNET_i2s (&pids[off + 1]),
+ off + 1);
+ send_broken_without_mqm (sender,
+ &msg->cid,
+ &pids[off + 1]);
+ return;
+ }
+ if (max_routes <= GNUNET_CONTAINER_multishortmap_size (routes))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received CADET_CONNECTION_CREATE from %s for %s. We have reached our route limit. Sending BROKEN\n",
+ GCP_2s (sender),
+ GNUNET_sh2s (&msg->cid.connection_of_tunnel));
+ send_broken_without_mqm (sender,
+ &msg->cid,
+ &pids[off - 1]);
+ return;
+ }
+
+ /* Workable route, create routing entry */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received CADET_CONNECTION_CREATE from %s for %s. Next hop %s:%u is up. Creating route\n",
+ GCP_2s (sender),
+ GNUNET_sh2s (&msg->cid.connection_of_tunnel),
+ GNUNET_i2s (&pids[off + 1]),
+ off + 1);
+ route = GNUNET_new (struct CadetRoute);
+ route->options = options;
+ route->cid = msg->cid;
+ route->last_use = GNUNET_TIME_absolute_get ();
+ dir_init (&route->prev,
+ route,
+ sender);
+ dir_init (&route->next,
+ route,
+ next);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multishortmap_put (routes,
+ &route->cid.connection_of_tunnel,
+ route,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ GNUNET_STATISTICS_set (stats,
+ "# routes",
+ GNUNET_CONTAINER_multishortmap_size (routes),
+ GNUNET_NO);
+ route->hn = GNUNET_CONTAINER_heap_insert (route_heap,
+ route,
+ route->last_use.abs_value_us);
+ if (NULL == timeout_task)
+ timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (keepalive_period,
+ 3),
+ &timeout_cb,
+ NULL);
+}
+
+
+/**
+ * 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_connection_create_ack (void *cls,
+ const struct GNUNET_CADET_ConnectionCreateAckMessage *msg)
+{
+ struct CadetPeer *peer = cls;
+ struct CadetConnection *cc;
+
+ /* First, check if ACK belongs to a connection that ends here. */
+ cc = GCC_lookup (&msg->cid);
+ if (NULL != cc)
+ {
+ /* verify ACK came from the right direction */
+ struct CadetPeerPath *path = GCC_get_path (cc);
+
+ if (peer !=
+ GCPP_get_peer_at_offset (path,
+ 0))
+ {
+ /* received ACK from unexpected direction, ignore! */
+ GNUNET_break_op (0);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received CONNECTION_CREATE_ACK for connection %s.\n",
+ GNUNET_sh2s (&msg->cid.connection_of_tunnel));
+ GCC_handle_connection_create_ack (cc);
+ return;
+ }
+
+ /* We're just an intermediary peer, route the message along its path */
+ route_message (peer,
+ &msg->cid,
+ &msg->header);
+}
+
+
+/**
+ * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN
+ *
+ * @param cls Closure (CadetPeer for neighbor that sent the message).
+ * @param msg Message itself.
+ * @deprecated duplicate logic with #handle_destroy(); dedup!
+ */
+static void
+handle_connection_broken (void *cls,
+ const struct GNUNET_CADET_ConnectionBrokenMessage *msg)
+{
+ struct CadetPeer *peer = cls;
+ struct CadetConnection *cc;
+ struct CadetRoute *route;
+
+ /* First, check if message belongs to a connection that ends here. */
+ cc = GCC_lookup (&msg->cid);
+ if (NULL != cc)
+ {
+ /* verify message came from the right direction */
+ struct CadetPeerPath *path = GCC_get_path (cc);
+
+ if (peer !=
+ GCPP_get_peer_at_offset (path,
+ 0))
+ {
+ /* received message from unexpected direction, ignore! */
+ GNUNET_break_op (0);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received CONNECTION_BROKEN for connection %s. Destroying it.\n",
+ GNUNET_sh2s (&msg->cid.connection_of_tunnel));
+ GCC_destroy_without_core (cc);
+
+ /* FIXME: also destroy the path up to the specified link! */
+ return;
+ }
+
+ /* We're just an intermediary peer, route the message along its path */
+ route_message (peer,
+ &msg->cid,
+ &msg->header);
+ route = get_route (&msg->cid);
+ if (NULL != route)
+ destroy_route (route);
+ /* FIXME: also destroy paths we MAY have up to the specified link! */
+}
+
+
+/**
+ * Handle for #GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY
+ *
+ * @param cls Closure (CadetPeer for neighbor that sent the message).
+ * @param msg Message itself.
+ */
+static void
+handle_connection_destroy (void *cls,
+ const struct GNUNET_CADET_ConnectionDestroyMessage *msg)
+{
+ struct CadetPeer *peer = cls;
+ struct CadetConnection *cc;
+ struct CadetRoute *route;
+
+ /* First, check if message belongs to a connection that ends here. */
+ cc = GCC_lookup (&msg->cid);
+ if (NULL != cc)
+ {
+ /* verify message came from the right direction */
+ struct CadetPeerPath *path = GCC_get_path (cc);
+
+ if (peer !=
+ GCPP_get_peer_at_offset (path,
+ 0))
+ {
+ /* received message from unexpected direction, ignore! */
+ GNUNET_break_op (0);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received CONNECTION_DESTROY for connection %s. Destroying connection.\n",
+ GNUNET_sh2s (&msg->cid.connection_of_tunnel));
+
+ GCC_destroy_without_core (cc);
+ return;
+ }
+
+ /* We're just an intermediary peer, route the message along its path */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received CONNECTION_DESTROY for connection %s. Destroying route.\n",
+ GNUNET_sh2s (&msg->cid.connection_of_tunnel));
+ route_message (peer,
+ &msg->cid,
+ &msg->header);
+ route = get_route (&msg->cid);
+ if (NULL != route)
+ destroy_route (route);
+}
+
+
+/**
+ * Handle for #GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX
+ *
+ * @param cls Closure (CadetPeer for neighbor that sent the message).
+ * @param msg Message itself.
+ */
+static void
+handle_tunnel_kx (void *cls,
+ const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg)
+{
+ struct CadetPeer *peer = cls;
+ struct CadetConnection *cc;
+
+ /* First, check if message belongs to a connection that ends here. */
+ cc = GCC_lookup (&msg->cid);
+ if (NULL != cc)
+ {
+ /* verify message came from the right direction */
+ struct CadetPeerPath *path = GCC_get_path (cc);
+
+ if (peer !=
+ GCPP_get_peer_at_offset (path,
+ 0))
+ {
+ /* received message from unexpected direction, ignore! */
+ GNUNET_break_op (0);
+ return;
+ }
+ GCC_handle_kx (cc,
+ msg);
+ return;
+ }
+
+ /* We're just an intermediary peer, route the message along its path */
+ route_message (peer,
+ &msg->cid,
+ &msg->header);
+}
+
+
+/**
+ * Handle for #GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX_AUTH
+ *
+ * @param cls Closure (CadetPeer for neighbor that sent the message).
+ * @param msg Message itself.
+ */
+static void
+handle_tunnel_kx_auth (void *cls,
+ const struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg)
+{
+ struct CadetPeer *peer = cls;
+ struct CadetConnection *cc;
+
+ /* First, check if message belongs to a connection that ends here. */
+ cc = GCC_lookup (&msg->kx.cid);
+ if (NULL != cc)
+ {
+ /* verify message came from the right direction */
+ struct CadetPeerPath *path = GCC_get_path (cc);
+
+ if (peer !=
+ GCPP_get_peer_at_offset (path,
+ 0))
+ {
+ /* received message from unexpected direction, ignore! */
+ GNUNET_break_op (0);
+ return;
+ }
+ GCC_handle_kx_auth (cc,
+ msg);
+ return;
+ }
+
+ /* We're just an intermediary peer, route the message along its path */
+ route_message (peer,
+ &msg->kx.cid,
+ &msg->kx.header);
+}
+
+
+/**
+ * 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_tunnel_encrypted (void *cls,
+ const struct GNUNET_CADET_TunnelEncryptedMessage *msg)
+{
+ return GNUNET_YES;
+}
+
+
+/**
+ * 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_tunnel_encrypted (void *cls,
+ const struct GNUNET_CADET_TunnelEncryptedMessage *msg)
+{
+ struct CadetPeer *peer = cls;
+ struct CadetConnection *cc;
+
+ /* First, check if message belongs to a connection that ends here. */
+ cc = GCC_lookup (&msg->cid);
+ if (NULL != cc)
+ {
+ /* verify message came from the right direction */
+ struct CadetPeerPath *path = GCC_get_path (cc);
+
+ if (peer !=
+ GCPP_get_peer_at_offset (path,
+ 0))
+ {
+ /* received message from unexpected direction, ignore! */
+ GNUNET_break_op (0);
+ return;
+ }
+ GCC_handle_encrypted (cc,
+ msg);
+ return;
+ }
+ /* We're just an intermediary peer, route the message along its path */
+ route_message (peer,
+ &msg->cid,
+ &msg->header);
+}
+
+
+/**
+ * Function called after #GNUNET_CORE_connect has succeeded (or failed
+ * for good). Note that the private key of the peer is intentionally
+ * not exposed here; if you need it, your process should try to read
+ * the private key file directly (which should work if you are
+ * authorized...). Implementations of this function must not call
+ * #GNUNET_CORE_disconnect (other than by scheduling a new task to
+ * do this later).
+ *
+ * @param cls closure
+ * @param my_identity ID of this peer, NULL if we failed
+ */
+static void
+core_init_cb (void *cls,
+ const struct GNUNET_PeerIdentity *my_identity)
+{
+ if (NULL == my_identity)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ GNUNET_break (0 ==
+ memcmp (my_identity,
+ &my_full_id,
+ sizeof (struct GNUNET_PeerIdentity)));
+}
+
+
+/**
+ * Method called whenever a given peer connects.
+ *
+ * @param cls closure
+ * @param peer peer identity this notification is about
+ */
+static void *
+core_connect_cb (void *cls,
+ const struct GNUNET_PeerIdentity *peer,
+ struct GNUNET_MQ_Handle *mq)
+{
+ struct CadetPeer *cp;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "CORE connection to peer %s was established.\n",
+ GNUNET_i2s (peer));
+ cp = GCP_get (peer,
+ GNUNET_YES);
+ GCP_set_mq (cp,
+ mq);
+ return cp;
+}
+
+
+/**
+ * Method called whenever a peer disconnects.
+ *
+ * @param cls closure
+ * @param peer peer identity this notification is about
+ */
+static void
+core_disconnect_cb (void *cls,
+ const struct GNUNET_PeerIdentity *peer,
+ void *peer_cls)
+{
+ struct CadetPeer *cp = peer_cls;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "CORE connection to peer %s went down.\n",
+ GNUNET_i2s (peer));
+ GCP_set_mq (cp,
+ NULL);
+}
+
+
+/**
+ * Initialize the CORE subsystem.
+ *
+ * @param c Configuration.
+ */
+void
+GCO_init (const struct GNUNET_CONFIGURATION_Handle *c)
+{
+ struct GNUNET_MQ_MessageHandler handlers[] = {
+ GNUNET_MQ_hd_var_size (connection_create,
+ GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE,
+ struct GNUNET_CADET_ConnectionCreateMessage,
+ NULL),
+ GNUNET_MQ_hd_fixed_size (connection_create_ack,
+ GNUNET_MESSAGE_TYPE_CADET_CONNECTION_CREATE_ACK,
+ struct GNUNET_CADET_ConnectionCreateAckMessage,
+ NULL),
+ GNUNET_MQ_hd_fixed_size (connection_broken,
+ GNUNET_MESSAGE_TYPE_CADET_CONNECTION_BROKEN,
+ struct GNUNET_CADET_ConnectionBrokenMessage,
+ NULL),
+ GNUNET_MQ_hd_fixed_size (connection_destroy,
+ GNUNET_MESSAGE_TYPE_CADET_CONNECTION_DESTROY,
+ struct GNUNET_CADET_ConnectionDestroyMessage,
+ NULL),
+ GNUNET_MQ_hd_fixed_size (tunnel_kx,
+ GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX,
+ struct GNUNET_CADET_TunnelKeyExchangeMessage,
+ NULL),
+ GNUNET_MQ_hd_fixed_size (tunnel_kx_auth,
+ GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX_AUTH,
+ struct GNUNET_CADET_TunnelKeyExchangeAuthMessage,
+ NULL),
+ GNUNET_MQ_hd_var_size (tunnel_encrypted,
+ GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED,
+ struct GNUNET_CADET_TunnelEncryptedMessage,
+ NULL),
+ GNUNET_MQ_handler_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (c,
+ "CADET",
+ "MAX_ROUTES",
+ &max_routes))
+ max_routes = 5000;
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (c,
+ "CADET",
+ "MAX_MSGS_QUEUE",
+ &max_buffers))
+ max_buffers = 10000;
+ routes = GNUNET_CONTAINER_multishortmap_create (1024,
+ GNUNET_NO);
+ route_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
+ core = GNUNET_CORE_connect (c,
+ NULL,
+ &core_init_cb,
+ &core_connect_cb,
+ &core_disconnect_cb,
+ handlers);
+}
+
+
+/**
+ * Shut down the CORE subsystem.
+ */
+void
+GCO_shutdown ()
+{
+ if (NULL != core)
+ {
+ GNUNET_CORE_disconnect (core);
+ core = NULL;
+ }
+ GNUNET_assert (0 == GNUNET_CONTAINER_multishortmap_size (routes));
+ GNUNET_CONTAINER_multishortmap_destroy (routes);
+ routes = NULL;
+ GNUNET_CONTAINER_heap_destroy (route_heap);
+ route_heap = NULL;
+ if (NULL != timeout_task)
+ {
+ GNUNET_SCHEDULER_cancel (timeout_task);
+ timeout_task = NULL;
+ }
+}
+
+/* end of gnunet-cadet-service_core.c */
--- /dev/null
+/*
+ This file is part of GNUnet.
+ Copyright (C) 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_core.h
+ * @brief cadet service; interaction with CORE service
+ * @author Bartlomiej Polot
+ * @author Christian Grothoff
+ *
+ * All functions in this file should use the prefix GCO (Gnunet Cadet cOre (bottom))
+ */
+
+#ifndef GNUNET_SERVICE_CADET_CORE_H
+#define GNUNET_SERVICE_CADET_CORE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0 /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+#include "gnunet_util_lib.h"
+
+
+/**
+ * Initialize the CORE subsystem.
+ *
+ * @param c Configuration.
+ */
+void
+GCO_init (const struct GNUNET_CONFIGURATION_Handle *c);
+
+
+/**
+ * Shut down the CORE subsystem.
+ */
+void
+GCO_shutdown (void);
+
+
+#if 0 /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+/* ifndef GNUNET_CADET_SERVICE_CORE_H */
+#endif
+/* end of gnunet-cadet-service_core.h */
--- /dev/null
+/*
+ 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.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.h"
+#include "gnunet-service-cadet_dht.h"
+#include "gnunet-service-cadet_hello.h"
+#include "gnunet-service-cadet_peer.h"
+#include "gnunet-service-cadet_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 */
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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.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.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__)
+
+/**
+ * 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 */
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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_paths.c
+ * @brief Information we track per path.
+ * @author Bartlomiej Polot
+ * @author Christian Grothoff
+ */
+#include "platform.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__)
+
+
+/**
+ * Information regarding a possible path to reach a peer.
+ */
+struct CadetPeerPath
+{
+
+ /**
+ * Array of all the peers on the path. If @e hn is non-NULL, the
+ * last one is our owner.
+ */
+ struct CadetPeerPathEntry **entries;
+
+ /**
+ * Node of this path in the owner's heap. Used to update our position
+ * in the heap whenever our @e desirability changes.
+ */
+ struct GNUNET_CONTAINER_HeapNode *hn;
+
+ /**
+ * Desirability of the path. How unique is it for the various peers
+ * on it?
+ */
+ GNUNET_CONTAINER_HeapCostType desirability;
+
+ /**
+ * Length of the @e entries array.
+ */
+ unsigned int entries_length;
+
+};
+
+
+/**
+ * Calculate the path's desirability score.
+ *
+ * @param path path to calculate the score for
+ */
+static void
+recalculate_path_desirability (struct CadetPeerPath *path)
+{
+ double result = 0.0;
+
+ for (unsigned int i=0;i<path->entries_length;i++)
+ {
+ struct CadetPeer *cp = path->entries[i]->peer;
+
+ result += GCP_get_desirability_of_path (cp,
+ i);
+ }
+ path->desirability = (GNUNET_CONTAINER_HeapCostType) result;
+}
+
+
+/**
+ * Return how much we like keeping the path. This is an aggregate
+ * score based on various factors, including the age of the path
+ * (older == better), and the value of this path to all of its ajacent
+ * peers. For example, long paths that end at a peer that we have no
+ * shorter way to reach are very desirable, while long paths that end
+ * at a peer for which we have a shorter way as well are much less
+ * desirable. Higher values indicate more valuable paths. The
+ * returned value should be used to decide which paths to remember.
+ *
+ * @param path path to return the length for
+ * @return desirability of the path, larger is more desirable
+ */
+GNUNET_CONTAINER_HeapCostType
+GCPP_get_desirability (const struct CadetPeerPath *path)
+{
+ return path->desirability;
+}
+
+
+/**
+ * Return connection to @a destination using @a path, or return
+ * NULL if no such connection exists.
+ *
+ * @param path path to traverse
+ * @param destination destination node to get to, must be on path
+ * @param off offset of @a destination on @a path
+ * @return NULL if we have no existing connection
+ * otherwise connection from us to @a destination via @a path
+ */
+struct CadetConnection *
+GCPP_get_connection (struct CadetPeerPath *path,
+ struct CadetPeer *destination,
+ unsigned int off)
+{
+ struct CadetPeerPathEntry *entry;
+
+ GNUNET_assert (off < path->entries_length);
+ entry = path->entries[off];
+ GNUNET_assert (entry->peer == destination);
+ return entry->cc;
+}
+
+
+/**
+ * Notify @a path that it is used for connection @a cc
+ * which ends at the path's offset @a off.
+ *
+ * @param path the path to remember the @a cc
+ * @param off the offset where the @a cc ends
+ * @param cc the connection to remember
+ */
+void
+GCPP_add_connection (struct CadetPeerPath *path,
+ unsigned int off,
+ struct CadetConnection *cc)
+{
+ struct CadetPeerPathEntry *entry;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding connection %s to path %s at offset %u\n",
+ GCC_2s (cc),
+ GCPP_2s (path),
+ off);
+ GNUNET_assert (off < path->entries_length);
+ entry = path->entries[off];
+ GNUNET_assert (NULL == entry->cc);
+ GNUNET_assert (NULL != cc);
+ entry->cc = cc;
+}
+
+
+
+/**
+ * Notify @a path that it is no longer used for connection @a cc which
+ * ended at the path's offset @a off.
+ *
+ * @param path the path to forget the @a cc
+ * @param off the offset where the @a cc ended
+ * @param cc the connection to forget
+ */
+void
+GCPP_del_connection (struct CadetPeerPath *path,
+ unsigned int off,
+ struct CadetConnection *cc)
+{
+ struct CadetPeerPathEntry *entry;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Removing connection %s to path %s at offset %u\n",
+ GCC_2s (cc),
+ GCPP_2s (path),
+ off);
+ GNUNET_assert (off < path->entries_length);
+ entry = path->entries[off];
+ GNUNET_assert (cc == entry->cc);
+ entry->cc = NULL;
+}
+
+
+/**
+ * This path is no longer needed, free resources.
+ *
+ * @param path path resources to free
+ */
+static void
+path_destroy (struct CadetPeerPath *path)
+{
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Destroying path %s\n",
+ GCPP_2s (path));
+ for (unsigned int i=0;i<path->entries_length;i++)
+ {
+ struct CadetPeerPathEntry *entry = path->entries[i];
+
+ if (NULL != entry->cc)
+ {
+ struct CadetTConnection *ct;
+
+ ct = GCC_get_ct (entry->cc);
+ if (NULL != ct)
+ GCT_connection_lost (ct);
+ GCC_destroy_without_tunnel (entry->cc);
+ }
+ GNUNET_free (entry);
+ }
+ GNUNET_free (path->entries);
+ GNUNET_free (path);
+}
+
+
+/**
+ * The owning peer of this path is no longer interested in maintaining
+ * it, so the path should be discarded or shortened (in case a
+ * previous peer on the path finds the path desirable).
+ *
+ * @param path the path that is being released
+ */
+void
+GCPP_release (struct CadetPeerPath *path)
+{
+ struct CadetPeerPathEntry *entry;
+ int force;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Owner releases path %s\n",
+ GCPP_2s (path));
+ path->hn = NULL;
+ entry = path->entries[path->entries_length - 1];
+ GNUNET_assert (path == entry->path);
+ while (1)
+ {
+ /* cut 'off' end of path */
+ GNUNET_assert (NULL == entry->cc);
+ GCP_path_entry_remove (entry->peer,
+ entry,
+ path->entries_length - 1);
+ path->entries_length--; /* We don't bother shrinking the 'entries' array,
+ as it's probably not worth it. */
+ GNUNET_free (entry);
+ if (0 == path->entries_length)
+ break; /* the end */
+
+ /* see if new peer at the end likes this path any better */
+ entry = path->entries[path->entries_length - 1];
+ GNUNET_assert (path == entry->path);
+ force = (NULL == entry->cc) ? GNUNET_NO : GNUNET_YES;
+ path->hn = GCP_attach_path (entry->peer,
+ path,
+ path->entries_length - 1,
+ force);
+ if (NULL != path->hn)
+ return; /* yep, got attached, we are done. */
+ GNUNET_assert (GNUNET_NO == force);
+ }
+
+ /* nobody wants us, discard the path */
+ path_destroy (path);
+}
+
+
+/**
+ * Updates the score for an entry on the path based
+ * on our experiences with using @a path.
+ *
+ * @param path the path to update
+ * @param off offset of the entry to update
+ * @param delta change in the score to apply
+ */
+void
+GCPP_update_score (struct CadetPeerPath *path,
+ unsigned int off,
+ int delta)
+{
+ struct CadetPeerPathEntry *entry;
+
+ GNUNET_assert (off < path->entries_length);
+ entry = path->entries[off];
+
+ /* Add delta, with checks for overflows */
+ if (delta >= 0)
+ {
+ if (delta + entry->score < entry->score)
+ entry->score = INT_MAX;
+ else
+ entry->score += delta;
+ }
+ else
+ {
+ if (delta + entry->score > entry->score)
+ entry->score = INT_MIN;
+ else
+ entry->score += delta;
+ }
+ recalculate_path_desirability (path);
+}
+
+
+/**
+ * Closure for #find_peer_at() and #check_match().
+ */
+struct CheckMatchContext
+{
+
+ /**
+ * Set to a matching path, if any.
+ */
+ struct CadetPeerPath *match;
+
+ /**
+ * Array the combined paths.
+ */
+ struct CadetPeer **cpath;
+
+ /**
+ * How long is the @e cpath array?
+ */
+ unsigned int cpath_length;
+
+};
+
+
+/**
+ * Check if the given path is identical on all of the
+ * hops until @a off, and not longer than @a off. If the
+ * @a path matches, store it in `match`.
+ *
+ * @param cls the `struct CheckMatchContext` to check against
+ * @param path the path to check
+ * @param off offset to check at
+ * @return #GNUNET_YES (continue to iterate), or if found #GNUNET_NO
+ */
+static int
+check_match (void *cls,
+ struct CadetPeerPath *path,
+ unsigned int off)
+{
+ struct CheckMatchContext *cm_ctx = cls;
+
+ GNUNET_assert (path->entries_length > off);
+ if ( (path->entries_length != off + 1) &&
+ (off + 1 != cm_ctx->cpath_length) )
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "check_match missmatch because path %s is too long (%u vs. %u vs. %u)\n",
+ GCPP_2s (path),
+ path->entries_length,
+ off + 1,
+ cm_ctx->cpath_length);
+ return GNUNET_YES; /* too long, goes somewhere else already, thus cannot be useful */
+ }
+ for (unsigned int i=0;i<off;i++)
+ if (cm_ctx->cpath[i] !=
+ GCPP_get_peer_at_offset (path,
+ i))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "check_match path %s missmatches at offset %u\n",
+ GCPP_2s (path),
+ i);
+ return GNUNET_YES; /* missmatch, ignore */
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "check_match found match with path %s\n",
+ GCPP_2s (path));
+ cm_ctx->match = path;
+ return GNUNET_NO; /* match, we are done! */
+}
+
+
+/**
+ * Extend path @a path by the @a num_peers from the @a peers
+ * array, assuming the owners past the current owner want it.
+ *
+ * @param path path to extend
+ * @param peers list of peers beyond the end of @a path
+ * @param num_peers length of the @a peers array
+ * @param force force attachment, even if we have other
+ * paths already
+ */
+static void
+extend_path (struct CadetPeerPath *path,
+ struct CadetPeer **peers,
+ unsigned int num_peers,
+ int force)
+{
+ unsigned int old_len = path->entries_length;
+ int i;
+
+ /* Expand path */
+ GNUNET_array_grow (path->entries,
+ path->entries_length,
+ old_len + num_peers);
+ for (i=num_peers-1;i >= 0;i--)
+ {
+ struct CadetPeerPathEntry *entry = GNUNET_new (struct CadetPeerPathEntry);
+
+ path->entries[old_len + i] = entry;
+ entry->peer = peers[i];
+ entry->path = path;
+ }
+ for (i=num_peers-1;i >= 0;i--)
+ {
+ struct CadetPeerPathEntry *entry = path->entries[old_len + i];
+
+ GCP_path_entry_add (entry->peer,
+ entry,
+ old_len + i);
+ }
+
+ /* If we extend an existing path, detach it from the
+ old owner and re-attach to the new one */
+ GCP_detach_path (path->entries[old_len-1]->peer,
+ path,
+ path->hn);
+ path->hn = NULL;
+ for (i=num_peers-1;i>=0;i--)
+ {
+ struct CadetPeerPathEntry *entry = path->entries[old_len + i];
+
+ path->entries_length = old_len + i + 1;
+ recalculate_path_desirability (path);
+ path->hn = GCP_attach_path (peers[i],
+ path,
+ old_len + (unsigned int) i,
+ force);
+ if (NULL != path->hn)
+ break;
+ GNUNET_assert (NULL == entry->cc);
+ GCP_path_entry_remove (entry->peer,
+ entry,
+ old_len + i);
+ GNUNET_free (entry);
+ path->entries[old_len + i] = NULL;
+ }
+ if (NULL == path->hn)
+ {
+ /* none of the peers is interested in this path;
+ shrink path back and re-attach. */
+ GNUNET_array_grow (path->entries,
+ path->entries_length,
+ old_len);
+ path->hn = GCP_attach_path (path->entries[old_len - 1]->peer,
+ path,
+ old_len - 1,
+ GNUNET_YES);
+ GNUNET_assert (NULL != path->hn);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Extended path %s\n",
+ GCPP_2s (path));
+}
+
+
+/**
+ * Create a peer path based on the result of a DHT lookup. If we
+ * already know this path, or one that is longer, simply return NULL.
+ * Otherwise, we try to extend an existing path, or create a new one
+ * if applicable.
+ *
+ * @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
+ * @return a path through the network
+ */
+void
+GCPP_try_path_from_dht (const struct GNUNET_PeerIdentity *get_path,
+ unsigned int get_path_length,
+ const struct GNUNET_PeerIdentity *put_path,
+ unsigned int put_path_length)
+{
+ struct CadetPeer *cpath[get_path_length + put_path_length];
+ struct CheckMatchContext cm_ctx;
+ struct CadetPeerPath *path;
+ struct GNUNET_CONTAINER_HeapNode *hn;
+ int i;
+ unsigned int skip;
+ unsigned int total_len;
+
+ /* precompute 'cpath' so we can avoid doing the lookups lots of times */
+ skip = 0;
+ memset (cpath,
+ 0,
+ sizeof (cpath)); /* Just to trigger harder errors later. */
+ total_len = get_path_length + put_path_length;
+ for (unsigned int off=0;off<total_len;off++)
+ {
+ const struct GNUNET_PeerIdentity *pid;
+
+ pid = (off < get_path_length)
+ ? &get_path[get_path_length - off]
+ : &put_path[get_path_length + put_path_length - off];
+ cpath[off - skip] = GCP_get (pid,
+ GNUNET_YES);
+ /* Check that no peer is twice on the path */
+ for (unsigned int i=0;i<off - skip;i++)
+ {
+ if (cpath[i] == cpath[off - skip])
+ {
+ skip = off - i;
+ break;
+ }
+ }
+ }
+ total_len -= skip;
+
+ /* First figure out if this path is a subset of an existing path, an
+ extension of an existing path, or a new path. */
+ cm_ctx.cpath_length = total_len;
+ cm_ctx.cpath = cpath;
+ cm_ctx.match = NULL;
+ for (i=total_len-1;i>=0;i--)
+ {
+ GCP_iterate_paths_at (cpath[i],
+ (unsigned int) i,
+ &check_match,
+ &cm_ctx);
+ if (NULL != cm_ctx.match)
+ {
+ if (i == total_len - 1)
+ {
+ /* Existing path includes this one, nothing to do! */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Path discovered from DHT is already known\n");
+ return;
+ }
+ if (cm_ctx.match->entries_length == i + 1)
+ {
+ /* Existing path ends in the middle of new path, extend it! */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Trying to extend existing path %s by additional links discovered from DHT\n",
+ GCPP_2s (cm_ctx.match));
+ extend_path (cm_ctx.match,
+ &cpath[i + 1],
+ total_len - i - 1,
+ GNUNET_NO);
+ return;
+ }
+ }
+ }
+
+ /* No match at all, create completely new path */
+ path = GNUNET_new (struct CadetPeerPath);
+ path->entries_length = total_len;
+ path->entries = GNUNET_new_array (path->entries_length,
+ struct CadetPeerPathEntry *);
+ for (i=path->entries_length-1;i>=0;i--)
+ {
+ struct CadetPeerPathEntry *entry = GNUNET_new (struct CadetPeerPathEntry);
+
+ path->entries[i] = entry;
+ entry->peer = cpath[i];
+ entry->path = path;
+ }
+ for (i=path->entries_length-1;i>=0;i--)
+ {
+ struct CadetPeerPathEntry *entry = path->entries[i];
+
+ GCP_path_entry_add (entry->peer,
+ entry,
+ i);
+ }
+
+ /* Finally, try to attach it */
+ hn = NULL;
+ for (i=total_len-1;i>=0;i--)
+ {
+ struct CadetPeerPathEntry *entry = path->entries[i];
+
+ path->entries_length = i + 1;
+ recalculate_path_desirability (path);
+ hn = GCP_attach_path (cpath[i],
+ path,
+ (unsigned int) i,
+ GNUNET_NO);
+ if (NULL != hn)
+ break;
+ GCP_path_entry_remove (entry->peer,
+ entry,
+ i);
+ GNUNET_free (entry);
+ path->entries[i] = NULL;
+ }
+ if (NULL == hn)
+ {
+ /* None of the peers on the path care about it. */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Path discovered from DHT is not interesting to us\n");
+ GNUNET_free (path->entries);
+ GNUNET_free (path);
+ return;
+ }
+ path->hn = hn;
+ /* Shrink path to actual useful length */
+ GNUNET_array_grow (path->entries,
+ path->entries_length,
+ i + 1);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Created new path %s based on information from DHT\n",
+ GCPP_2s (path));
+}
+
+
+/**
+ * We got an incoming connection, obtain the corresponding path.
+ *
+ * @param path_length number of segments on the @a path
+ * @param pids path through the network, in reverse order (we are at the end at index @a path_length)
+ * @return corresponding path object
+ */
+struct CadetPeerPath *
+GCPP_get_path_from_route (unsigned int path_length,
+ const struct GNUNET_PeerIdentity *pids)
+{
+ struct CheckMatchContext cm_ctx;
+ struct CadetPeer *cpath[path_length];
+ struct CadetPeerPath *path;
+
+ /* precompute inverted 'cpath' so we can avoid doing the lookups and
+ have the correct order */
+ for (unsigned int off=0;off<path_length;off++)
+ cpath[off] = GCP_get (&pids[path_length - 1 - off],
+ GNUNET_YES);
+
+ /* First figure out if this path is a subset of an existing path, an
+ extension of an existing path, or a new path. */
+ cm_ctx.cpath = cpath;
+ cm_ctx.cpath_length = path_length;
+ cm_ctx.match = NULL;
+ for (int i=path_length-1;i>=0;i--)
+ {
+ GCP_iterate_paths_at (cpath[i],
+ (unsigned int) i,
+ &check_match,
+ &cm_ctx);
+ if (NULL != cm_ctx.match)
+ {
+ if (i == path_length - 1)
+ {
+ /* Existing path includes this one, return the match! */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Returning existing path %s as inverse for incoming connection\n",
+ GCPP_2s (cm_ctx.match));
+ return cm_ctx.match;
+ }
+ if (cm_ctx.match->entries_length == i + 1)
+ {
+ /* Existing path ends in the middle of new path, extend it! */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Extending existing path %s to create inverse for incoming connection\n",
+ GCPP_2s (cm_ctx.match));
+ extend_path (cm_ctx.match,
+ &cpath[i + 1],
+ path_length - i - 1,
+ GNUNET_YES);
+ /* Check that extension was successful */
+ GNUNET_assert (cm_ctx.match->entries_length == path_length);
+ return cm_ctx.match;
+ }
+ /* Eh, we found a match but couldn't use it? Something is wrong. */
+ GNUNET_break (0);
+ }
+ }
+
+ /* No match at all, create completely new path */
+ path = GNUNET_new (struct CadetPeerPath);
+ path->entries_length = path_length;
+ path->entries = GNUNET_new_array (path->entries_length,
+ struct CadetPeerPathEntry *);
+ for (int i=path_length-1;i>=0;i--)
+ {
+ struct CadetPeerPathEntry *entry = GNUNET_new (struct CadetPeerPathEntry);
+
+ path->entries[i] = entry;
+ entry->peer = cpath[i];
+ entry->path = path;
+ }
+ for (int i=path_length-1;i>=0;i--)
+ {
+ struct CadetPeerPathEntry *entry = path->entries[i];
+
+ GCP_path_entry_add (entry->peer,
+ entry,
+ i);
+ }
+ recalculate_path_desirability (path);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Created new path %s to create inverse for incoming connection\n",
+ GCPP_2s (path));
+ path->hn = GCP_attach_path (cpath[path_length - 1],
+ path,
+ path_length - 1,
+ GNUNET_YES);
+ return path;
+}
+
+
+/**
+ * Return the length of the path. Excludes one end of the
+ * path, so the loopback path has length 0.
+ *
+ * @param path path to return the length for
+ * @return number of peers on the path
+ */
+unsigned int
+GCPP_get_length (struct CadetPeerPath *path)
+{
+ return path->entries_length;
+}
+
+
+/**
+ * Find peer's offset on path.
+ *
+ * @param path path to search
+ * @param cp peer to look for
+ * @return offset of @a cp on @a path, or UINT_MAX if not found
+ */
+unsigned int
+GCPP_find_peer (struct CadetPeerPath *path,
+ struct CadetPeer *cp)
+{
+ for (unsigned int off = 0;
+ off < path->entries_length;
+ off++)
+ if (cp == GCPP_get_peer_at_offset (path,
+ off))
+ return off;
+ return UINT_MAX;
+}
+
+
+/**
+ * Obtain the peer at offset @a off in @a path.
+ *
+ * @param path peer path to inspect
+ * @param off offset to return, must be smaller than path length
+ * @return the peer at offset @a off
+ */
+struct CadetPeer *
+GCPP_get_peer_at_offset (struct CadetPeerPath *path,
+ unsigned int off)
+{
+ GNUNET_assert (off < path->entries_length);
+ return path->entries[off]->peer;
+}
+
+
+/**
+ * Convert a path to a human-readable string.
+ *
+ * @param path path to convert
+ * @return string, to be freed by caller (unlike other *_2s APIs!)
+ */
+const char *
+GCPP_2s (struct CadetPeerPath *path)
+{
+ static char buf[2048];
+ size_t off;
+ const unsigned int max_plen = (sizeof(buf) - 16) / 5 - 2; /* 5 characters per entry */
+
+ off = 0;
+ for (unsigned int i = 0;
+ i < path->entries_length;
+ i++)
+ {
+ if ( (path->entries_length > max_plen) &&
+ (i == max_plen / 2) )
+ off += GNUNET_snprintf (&buf[off],
+ sizeof (buf) - off,
+ "...-");
+ if ( (path->entries_length > max_plen) &&
+ (i > max_plen / 2) &&
+ (i < path->entries_length - max_plen / 2) )
+ continue;
+ off += GNUNET_snprintf (&buf[off],
+ sizeof (buf) - off,
+ "%s%s",
+ GNUNET_i2s (GCP_get_id (GCPP_get_peer_at_offset (path,
+ i))),
+ (i == path->entries_length -1) ? "" : "-");
+ }
+ GNUNET_snprintf (&buf[off],
+ sizeof (buf) - off,
+ "(%p)",
+ path);
+ return buf;
+}
+
+
+/* end of gnunet-service-cadet-new_paths.c */
--- /dev/null
+
+/*
+ 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_paths.h
+ * @brief Information we track per path.
+ * @author Bartlomiej Polot
+ * @author Christian Grothoff
+ */
+#ifndef GNUNET_SERVICE_CADET_PATHS_H
+#define GNUNET_SERVICE_CADET_PATHS_H
+
+#include "gnunet_util_lib.h"
+#include "gnunet-service-cadet.h"
+
+/**
+ * Create a peer path based on the result of a DHT lookup. If we
+ * already know this path, or one that is longer, simply return NULL.
+ * Otherwise, we try to extend an existing path, or create a new one
+ * if applicable.
+ *
+ * @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
+ */
+void
+GCPP_try_path_from_dht (const struct GNUNET_PeerIdentity *get_path,
+ unsigned int get_path_length,
+ const struct GNUNET_PeerIdentity *put_path,
+ unsigned int put_path_length);
+
+
+/**
+ * We got an incoming connection, obtain the corresponding path.
+ *
+ * @param path_length number of segments on the @a path
+ * @param path through the network, in reverse order (we are at the end!)
+ * @return corresponding path object
+ */
+struct CadetPeerPath *
+GCPP_get_path_from_route (unsigned int path_length,
+ const struct GNUNET_PeerIdentity *pids);
+
+
+/**
+ * Return the length of the path. Excludes one end of the
+ * path, so the loopback path has length 0.
+ *
+ * @param path path to return the length for
+ * @return number of peers on the path
+ */
+unsigned int
+GCPP_get_length (struct CadetPeerPath *path);
+
+
+/**
+ * Return connection to @a destination using @a path, or return
+ * NULL if no such connection exists.
+ *
+ * @param path path to traverse
+ * @param destination destination node to get to, must be on path
+ * @param off offset of @a destination on @a path
+ * @return NULL if we have no existing connection
+ * otherwise connection from us to @a destination via @a path
+ */
+struct CadetConnection *
+GCPP_get_connection (struct CadetPeerPath *path,
+ struct CadetPeer *destination,
+ unsigned int off);
+
+
+/**
+ * Notify @a path that it is used for connection @a cc
+ * which ends at the path's offset @a off.
+ *
+ * @param path the path to remember the @a cc
+ * @param off the offset where the @a cc ends
+ * @param cc the connection to remember
+ */
+void
+GCPP_add_connection (struct CadetPeerPath *path,
+ unsigned int off,
+ struct CadetConnection *cc);
+
+
+/**
+ * Notify @a path that it is no longer used for connection @a cc which
+ * ended at the path's offset @a off.
+ *
+ * @param path the path to forget the @a cc
+ * @param off the offset where the @a cc ended
+ * @param cc the connection to forget
+ */
+void
+GCPP_del_connection (struct CadetPeerPath *path,
+ unsigned int off,
+ struct CadetConnection *cc);
+
+
+/**
+ * Find peer's offset on path.
+ *
+ * @param path path to search
+ * @param cp peer to look for
+ * @return offset of @a cp on @a path, or UINT_MAX if not found
+ */
+unsigned int
+GCPP_find_peer (struct CadetPeerPath *path,
+ struct CadetPeer *cp);
+
+
+/**
+ * Return how much we like keeping the path. This is an aggregate
+ * score based on various factors, including the age of the path
+ * (older == better), and the value of this path to all of its ajacent
+ * peers. For example, long paths that end at a peer that we have no
+ * shorter way to reach are very desirable, while long paths that end
+ * at a peer for which we have a shorter way as well are much less
+ * desirable. Higher values indicate more valuable paths. The
+ * returned value should be used to decide which paths to remember.
+ *
+ * @param path path to return the length for
+ * @return desirability of the path, larger is more desirable
+ */
+GNUNET_CONTAINER_HeapCostType
+GCPP_get_desirability (const struct CadetPeerPath *path);
+
+
+/**
+ * The given peer @a cp used to own this @a path. However, it is no
+ * longer interested in maintaining it, so the path should be
+ * discarded or shortened (in case a previous peer on the path finds
+ * the path desirable).
+ *
+ * @param path the path that is being released
+ */
+void
+GCPP_release (struct CadetPeerPath *path);
+
+
+/**
+ * Obtain the peer at offset @a off in @a path.
+ *
+ * @param path peer path to inspect
+ * @param off offset to return, must be smaller than path length
+ * @return peer at offset @a off
+ */
+struct CadetPeer *
+GCPP_get_peer_at_offset (struct CadetPeerPath *path,
+ unsigned int off);
+
+
+/**
+ * Convert a path to a human-readable string.
+ *
+ * @param path path to convert
+ * @return string, statically allocated
+ */
+const char *
+GCPP_2s (struct CadetPeerPath *p);
+
+
+#endif
--- /dev/null
+/*
+ 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_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_connection.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-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 */
--- /dev/null
+
+/*
+ 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.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
--- /dev/null
+/*
+ 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_tunnels.c
+ * @brief Information we track per tunnel.
+ * @author Bartlomiej Polot
+ * @author Christian Grothoff
+ *
+ * FIXME:
+ * - proper connection evaluation during connection management:
+ * + consider quality (or quality spread?) of current connection set
+ * when deciding how often to do maintenance
+ * + interact with PEER to drive DHT GET/PUT operations based
+ * on how much we like our connections
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_statistics_service.h"
+#include "gnunet_signatures.h"
+#include "cadet_protocol.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__)
+
+/**
+ * How often do we try to decrypt payload with unverified key
+ * material? Used to limit CPU increase upon receiving bogus
+ * KX.
+ */
+#define MAX_UNVERIFIED_ATTEMPTS 16
+
+/**
+ * How long do we wait until tearing down an idle tunnel?
+ */
+#define IDLE_DESTROY_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 90)
+
+/**
+ * How long do we wait initially before retransmitting the KX?
+ * TODO: replace by 2 RTT if/once we have connection-level RTT data!
+ */
+#define INITIAL_KX_RETRY_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 250)
+
+/**
+ * Maximum number of skipped keys we keep in memory per tunnel.
+ */
+#define MAX_SKIPPED_KEYS 64
+
+/**
+ * Maximum number of keys (and thus ratchet steps) we are willing to
+ * skip before we decide this is either a bogus packet or a DoS-attempt.
+ */
+#define MAX_KEY_GAP 256
+
+
+/**
+ * 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;
+
+ /**
+ * 32-byte root key which gets updated by DH ratchet.
+ */
+ struct GNUNET_CRYPTO_SymmetricSessionKey RK;
+
+ /**
+ * 32-byte header key (currently used for sending).
+ */
+ struct GNUNET_CRYPTO_SymmetricSessionKey HKs;
+
+ /**
+ * 32-byte header key (currently used for receiving)
+ */
+ struct GNUNET_CRYPTO_SymmetricSessionKey HKr;
+
+ /**
+ * 32-byte next header key (for sending), used once the
+ * ratchet advances. We are sure that the sender has this
+ * key as well only after @e ratchet_allowed is #GNUNET_YES.
+ */
+ struct GNUNET_CRYPTO_SymmetricSessionKey NHKs;
+
+ /**
+ * 32-byte next header key (for receiving). To be tried
+ * when decrypting with @e HKr fails and thus the sender
+ * may have advanced the ratchet.
+ */
+ struct GNUNET_CRYPTO_SymmetricSessionKey NHKr;
+
+ /**
+ * 32-byte chain keys (used for forward-secrecy) for
+ * sending messages. Updated for every message.
+ */
+ struct GNUNET_CRYPTO_SymmetricSessionKey CKs;
+
+ /**
+ * 32-byte chain keys (used for forward-secrecy) for
+ * receiving messages. Updated for every message. If
+ * messages are skipped, the respective derived MKs
+ * (and the current @HKr) are kept in the @e skipped_head DLL.
+ */
+ struct GNUNET_CRYPTO_SymmetricSessionKey CKr;
+
+ /**
+ * ECDH for key exchange (A0 / B0).
+ */
+ struct GNUNET_CRYPTO_EcdhePrivateKey kx_0;
+
+ /**
+ * ECDH Ratchet key (our private key in the current DH).
+ */
+ struct GNUNET_CRYPTO_EcdhePrivateKey DHRs;
+
+ /**
+ * ECDH Ratchet key (other peer's public key in the current DH).
+ */
+ struct GNUNET_CRYPTO_EcdhePublicKey DHRr;
+
+ /**
+ * Time when the current ratchet expires and a new one is triggered
+ * (if @e ratchet_allowed is #GNUNET_YES).
+ */
+ struct GNUNET_TIME_Absolute ratchet_expiration;
+
+ /**
+ * Number of elements in @a skipped_head <-> @a skipped_tail.
+ */
+ unsigned int skipped;
+
+ /**
+ * 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;
+
+ /**
+ * True (#GNUNET_YES) if we have received a message from the
+ * other peer that uses the keys from our last ratchet step.
+ * This implies that we are again allowed to advance the ratchet,
+ * otherwise we have to wait until the other peer sees our current
+ * ephemeral key and advances first.
+ *
+ * #GNUNET_NO if we have advanced the ratched but lack any evidence
+ * that the other peer has noticed this.
+ */
+ int ratchet_allowed;
+
+ /**
+ * Number of messages recieved since our last ratchet advance.
+ *
+ * If this counter = 0, we cannot send a new ratchet key in the next
+ * message.
+ *
+ * If this counter > 0, we could (but don't have to) send a new key.
+ *
+ * Once the @e ratchet_counter is larger than
+ * #ratchet_messages (or @e ratchet_expiration time has past), and
+ * @e ratchet_allowed is #GNUNET_YES, we advance the ratchet.
+ */
+ unsigned int ratchet_counter;
+
+};
+
+
+/**
+ * Struct used to save messages in a non-ready tunnel to send once connected.
+ */
+struct CadetTunnelQueueEntry
+{
+ /**
+ * We are entries in a DLL
+ */
+ struct CadetTunnelQueueEntry *next;
+
+ /**
+ * We are entries in a DLL
+ */
+ struct CadetTunnelQueueEntry *prev;
+
+ /**
+ * Tunnel these messages belong in.
+ */
+ struct CadetTunnel *t;
+
+ /**
+ * Continuation to call once sent (on the channel layer).
+ */
+ GCT_SendContinuation cont;
+
+ /**
+ * Closure for @c cont.
+ */
+ void *cont_cls;
+
+ /**
+ * Envelope of message to send follows.
+ */
+ struct GNUNET_MQ_Envelope *env;
+
+ /**
+ * Where to put the connection identifier into the payload
+ * of the message in @e env once we have it?
+ */
+ struct GNUNET_CADET_ConnectionTunnelIdentifier *cid;
+};
+
+
+/**
+ * Struct containing all information regarding a tunnel to a peer.
+ */
+struct CadetTunnel
+{
+ /**
+ * Destination of the tunnel.
+ */
+ struct CadetPeer *destination;
+
+ /**
+ * 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;
+
+ /**
+ * Axolotl info.
+ */
+ struct CadetTunnelAxolotl ax;
+
+ /**
+ * Unverified Axolotl info, used only if we got a fresh KX (not a
+ * KX_AUTH) while our end of the tunnel was still up. In this case,
+ * we keep the fresh KX around but do not put it into action until
+ * we got encrypted payload that assures us of the authenticity of
+ * the KX.
+ */
+ struct CadetTunnelAxolotl *unverified_ax;
+
+ /**
+ * Task scheduled if there are no more channels using the tunnel.
+ */
+ struct GNUNET_SCHEDULER_Task *destroy_task;
+
+ /**
+ * Task to trim connections if too many are present.
+ */
+ struct GNUNET_SCHEDULER_Task *maintain_connections_task;
+
+ /**
+ * Task to send messages from queue (if possible).
+ */
+ struct GNUNET_SCHEDULER_Task *send_task;
+
+ /**
+ * Task to trigger KX.
+ */
+ struct GNUNET_SCHEDULER_Task *kx_task;
+
+ /**
+ * Tokenizer for decrypted messages.
+ */
+ struct GNUNET_MessageStreamTokenizer *mst;
+
+ /**
+ * Dispatcher for decrypted messages only (do NOT use for sending!).
+ */
+ struct GNUNET_MQ_Handle *mq;
+
+ /**
+ * DLL of ready connections that are actively used to reach the destination peer.
+ */
+ struct CadetTConnection *connection_ready_head;
+
+ /**
+ * DLL of ready connections that are actively used to reach the destination peer.
+ */
+ struct CadetTConnection *connection_ready_tail;
+
+ /**
+ * DLL of connections that we maintain that might be used to reach the destination peer.
+ */
+ struct CadetTConnection *connection_busy_head;
+
+ /**
+ * DLL of connections that we maintain that might be used to reach the destination peer.
+ */
+ struct CadetTConnection *connection_busy_tail;
+
+ /**
+ * Channels inside this tunnel. Maps
+ * `struct GNUNET_CADET_ChannelTunnelNumber` to a `struct CadetChannel`.
+ */
+ struct GNUNET_CONTAINER_MultiHashMap32 *channels;
+
+ /**
+ * Channel ID for the next created channel in this tunnel.
+ */
+ struct GNUNET_CADET_ChannelTunnelNumber next_ctn;
+
+ /**
+ * Queued messages, to transmit once tunnel gets connected.
+ */
+ struct CadetTunnelQueueEntry *tq_head;
+
+ /**
+ * Queued messages, to transmit once tunnel gets connected.
+ */
+ struct CadetTunnelQueueEntry *tq_tail;
+
+ /**
+ * Identification of the connection from which we are currently processing
+ * a message. Only valid (non-NULL) during #handle_decrypted() and the
+ * handle-*()-functions called from our @e mq during that function.
+ */
+ struct CadetTConnection *current_ct;
+
+ /**
+ * How long do we wait until we retry the KX?
+ */
+ struct GNUNET_TIME_Relative kx_retry_delay;
+
+ /**
+ * When do we try the next KX?
+ */
+ struct GNUNET_TIME_Absolute next_kx_attempt;
+
+ /**
+ * Number of connections in the @e connection_ready_head DLL.
+ */
+ unsigned int num_ready_connections;
+
+ /**
+ * Number of connections in the @e connection_busy_head DLL.
+ */
+ unsigned int num_busy_connections;
+
+ /**
+ * How often have we tried and failed to decrypt a message using
+ * the unverified KX material from @e unverified_ax? Used to
+ * stop trying after #MAX_UNVERIFIED_ATTEMPTS.
+ */
+ unsigned int unverified_attempts;
+
+ /**
+ * Number of entries in the @e tq_head DLL.
+ */
+ unsigned int tq_len;
+
+ /**
+ * State of the tunnel encryption.
+ */
+ enum CadetTunnelEState estate;
+
+ /**
+ * Force triggering KX_AUTH independent of @e estate.
+ */
+ int kx_auth_requested;
+
+};
+
+
+/**
+ * Connection @a ct is now unready, clear it's ready flag
+ * and move it from the ready DLL to the busy DLL.
+ *
+ * @param ct connection to move to unready status
+ */
+static void
+mark_connection_unready (struct CadetTConnection *ct)
+{
+ struct CadetTunnel *t = ct->t;
+
+ GNUNET_assert (GNUNET_YES == ct->is_ready);
+ GNUNET_CONTAINER_DLL_remove (t->connection_ready_head,
+ t->connection_ready_tail,
+ ct);
+ GNUNET_assert (0 < t->num_ready_connections);
+ t->num_ready_connections--;
+ ct->is_ready = GNUNET_NO;
+ GNUNET_CONTAINER_DLL_insert (t->connection_busy_head,
+ t->connection_busy_tail,
+ ct);
+ t->num_busy_connections++;
+}
+
+
+/**
+ * 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)
+{
+ static char buf[64];
+
+ if (NULL == t)
+ return "Tunnel(NULL)";
+ GNUNET_snprintf (buf,
+ sizeof (buf),
+ "Tunnel %s",
+ GNUNET_i2s (GCP_get_id (t->destination)));
+ return buf;
+}
+
+
+/**
+ * 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_RECV:
+ return "CADET_TUNNEL_KEY_AX_RECV";
+ case CADET_TUNNEL_KEY_AX_SENT:
+ return "CADET_TUNNEL_KEY_AX_SENT";
+ case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
+ return "CADET_TUNNEL_KEY_AX_SENT_AND_RECV";
+ case CADET_TUNNEL_KEY_AX_AUTH_SENT:
+ return "CADET_TUNNEL_KEY_AX_AUTH_SENT";
+ case CADET_TUNNEL_KEY_OK:
+ return "CADET_TUNNEL_KEY_OK";
+ default:
+ GNUNET_snprintf (buf,
+ sizeof (buf),
+ "%u (UNKNOWN STATE)",
+ es);
+ return buf;
+ }
+}
+
+
+/**
+ * Return the peer to which this tunnel goes.
+ *
+ * @param t a tunnel
+ * @return the destination of the tunnel
+ */
+struct CadetPeer *
+GCT_get_destination (struct CadetTunnel *t)
+{
+ return t->destination;
+}
+
+
+/**
+ * Count channels of a tunnel.
+ *
+ * @param t Tunnel on which to count.
+ *
+ * @return Number of channels.
+ */
+unsigned int
+GCT_count_channels (struct CadetTunnel *t)
+{
+ return GNUNET_CONTAINER_multihashmap32_size (t->channels);
+}
+
+
+/**
+ * Lookup a channel by its @a ctn.
+ *
+ * @param t tunnel to look in
+ * @param ctn number of channel to find
+ * @return NULL if channel does not exist
+ */
+struct CadetChannel *
+lookup_channel (struct CadetTunnel *t,
+ struct GNUNET_CADET_ChannelTunnelNumber ctn)
+{
+ return GNUNET_CONTAINER_multihashmap32_get (t->channels,
+ ntohl (ctn.cn));
+}
+
+
+/**
+ * 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 (const struct CadetTunnel *t)
+{
+ return t->num_ready_connections + t->num_busy_connections;
+}
+
+
+/**
+ * Find first connection that is ready in the list of
+ * our connections. Picks ready connections round-robin.
+ *
+ * @param t tunnel to search
+ * @return NULL if we have no connection that is ready
+ */
+static struct CadetTConnection *
+get_ready_connection (struct CadetTunnel *t)
+{
+ struct CadetTConnection *hd = t->connection_ready_head;
+
+ GNUNET_assert ( (NULL == hd) ||
+ (GNUNET_YES == hd->is_ready) );
+ return hd;
+}
+
+
+/**
+ * Get the encryption state of a tunnel.
+ *
+ * @param t Tunnel.
+ *
+ * @return Tunnel's encryption state.
+ */
+enum CadetTunnelEState
+GCT_get_estate (struct CadetTunnel *t)
+{
+ return t->estate;
+}
+
+
+/**
+ * Called when either we have a new connection, or a new message in the
+ * queue, or some existing connection has transmission capacity. Looks
+ * at our message queue and if there is a message, picks a connection
+ * to send it on.
+ *
+ * @param cls the `struct CadetTunnel` to process messages on
+ */
+static void
+trigger_transmissions (void *cls);
+
+
+/* ************************************** start core crypto ***************************** */
+
+
+/**
+ * Create a new Axolotl ephemeral (ratchet) key.
+ *
+ * @param ax key material to update
+ */
+static void
+new_ephemeral (struct CadetTunnelAxolotl *ax)
+{
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Creating new ephemeral ratchet key (DHRs)\n");
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_ecdhe_key_create2 (&ax->DHRs));
+}
+
+
+/**
+ * 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;
+
+ GNUNET_CRYPTO_hmac_derive_key (&auth_key,
+ key,
+ &iv, sizeof (iv),
+ key, sizeof (*key),
+ ctx, sizeof (ctx),
+ NULL);
+ /* Two step: GNUNET_ShortHash is only 256 bits,
+ GNUNET_HashCode is 512, so we truncate. */
+ GNUNET_CRYPTO_hmac (&auth_key,
+ plaintext,
+ size,
+ &hash);
+ GNUNET_memcpy (hmac,
+ &hash,
+ sizeof (*hmac));
+}
+
+
+/**
+ * Perform a HMAC.
+ *
+ * @param key Key to use.
+ * @param[out] hash Resulting HMAC.
+ * @param source Source key material (data to HMAC).
+ * @param len Length of @a source.
+ */
+static void
+t_ax_hmac_hash (const struct GNUNET_CRYPTO_SymmetricSessionKey *key,
+ struct GNUNET_HashCode *hash,
+ const 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 symmetric encryption key from an HMAC-HASH.
+ *
+ * @param key Key to use for the HMAC.
+ * @param[out] out Key to generate.
+ * @param source Source key material (data to HMAC).
+ * @param len Length of @a source.
+ */
+static void
+t_hmac_derive_key (const struct GNUNET_CRYPTO_SymmetricSessionKey *key,
+ struct GNUNET_CRYPTO_SymmetricSessionKey *out,
+ const 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 ax key material to use.
+ * @param dst Destination with @a size bytes for the encrypted data.
+ * @param src Source of the plaintext. Can overlap with @c dst, must contain @a size bytes
+ * @param size Size of the buffers at @a src and @a dst
+ */
+static void
+t_ax_encrypt (struct CadetTunnelAxolotl *ax,
+ void *dst,
+ const void *src,
+ size_t size)
+{
+ struct GNUNET_CRYPTO_SymmetricSessionKey MK;
+ struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
+ size_t out_size;
+
+ 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 (ax);
+ 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);
+
+ out_size = GNUNET_CRYPTO_symmetric_encrypt (src,
+ size,
+ &MK,
+ &iv,
+ dst);
+ GNUNET_assert (size == out_size);
+ t_hmac_derive_key (&ax->CKs,
+ &ax->CKs,
+ "1",
+ 1);
+}
+
+
+/**
+ * Decrypt data with the axolotl tunnel key.
+ *
+ * @param ax key material to use.
+ * @param dst Destination for the decrypted data, must contain @a size bytes.
+ * @param src Source of the ciphertext. Can overlap with @c dst, must contain @a size bytes.
+ * @param size Size of the @a src and @a dst buffers
+ */
+static void
+t_ax_decrypt (struct CadetTunnelAxolotl *ax,
+ void *dst,
+ const void *src,
+ size_t size)
+{
+ struct GNUNET_CRYPTO_SymmetricSessionKey MK;
+ struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
+ size_t out_size;
+
+ t_hmac_derive_key (&ax->CKr,
+ &MK,
+ "0",
+ 1);
+ GNUNET_CRYPTO_symmetric_derive_iv (&iv,
+ &MK,
+ NULL, 0,
+ NULL);
+ 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);
+}
+
+
+/**
+ * Encrypt header with the axolotl header key.
+ *
+ * @param ax key material to use.
+ * @param[in|out] msg Message whose header to encrypt.
+ */
+static void
+t_h_encrypt (struct CadetTunnelAxolotl *ax,
+ struct GNUNET_CADET_TunnelEncryptedMessage *msg)
+{
+ struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
+ size_t out_size;
+
+ GNUNET_CRYPTO_symmetric_derive_iv (&iv,
+ &ax->HKs,
+ NULL, 0,
+ NULL);
+ out_size = GNUNET_CRYPTO_symmetric_encrypt (&msg->ax_header,
+ sizeof (struct GNUNET_CADET_AxHeader),
+ &ax->HKs,
+ &iv,
+ &msg->ax_header);
+ GNUNET_assert (sizeof (struct GNUNET_CADET_AxHeader) == out_size);
+}
+
+
+/**
+ * Decrypt header with the current axolotl header key.
+ *
+ * @param ax key material to use.
+ * @param src Message whose header to decrypt.
+ * @param dst Where to decrypt header to.
+ */
+static void
+t_h_decrypt (struct CadetTunnelAxolotl *ax,
+ const struct GNUNET_CADET_TunnelEncryptedMessage *src,
+ struct GNUNET_CADET_TunnelEncryptedMessage *dst)
+{
+ struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
+ size_t out_size;
+
+ GNUNET_CRYPTO_symmetric_derive_iv (&iv,
+ &ax->HKr,
+ NULL, 0,
+ NULL);
+ out_size = GNUNET_CRYPTO_symmetric_decrypt (&src->ax_header.Ns,
+ sizeof (struct GNUNET_CADET_AxHeader),
+ &ax->HKr,
+ &iv,
+ &dst->ax_header.Ns);
+ GNUNET_assert (sizeof (struct GNUNET_CADET_AxHeader) == out_size);
+}
+
+
+/**
+ * Delete a key from the list of skipped keys.
+ *
+ * @param ax key material to delete @a key from.
+ * @param key Key to delete.
+ */
+static void
+delete_skipped_key (struct CadetTunnelAxolotl *ax,
+ struct CadetTunnelSkippedKey *key)
+{
+ GNUNET_CONTAINER_DLL_remove (ax->skipped_head,
+ ax->skipped_tail,
+ key);
+ GNUNET_free (key);
+ ax->skipped--;
+}
+
+
+/**
+ * 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 ax key material 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 ssize_t
+try_old_ax_keys (struct CadetTunnelAxolotl *ax,
+ 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 skipped keys\n");
+ hmac = &plaintext_header.hmac;
+ esize = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage);
+
+ /* Find a correct Header Key */
+ valid_HK = NULL;
+ for (key = ax->skipped_head; NULL != key; key = key->next)
+ {
+ t_hmac (&src->ax_header,
+ sizeof (struct GNUNET_CADET_AxHeader) + esize,
+ 0,
+ &key->HK,
+ hmac);
+ if (0 == memcmp (hmac,
+ &src->hmac,
+ sizeof (*hmac)))
+ {
+ 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->ax_header.Ns,
+ sizeof (struct GNUNET_CADET_AxHeader),
+ &key->HK,
+ &iv,
+ &plaintext_header.ax_header.Ns);
+ GNUNET_assert (sizeof (struct GNUNET_CADET_AxHeader) == res);
+
+ /* Find the correct message key */
+ N = ntohl (plaintext_header.ax_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;
+
+ /* 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);
+ delete_skipped_key (ax,
+ key);
+ return res;
+}
+
+
+/**
+ * Delete a key from the list of skipped keys.
+ *
+ * @param ax key material to delete from.
+ * @param HKr Header Key to use.
+ */
+static void
+store_skipped_key (struct CadetTunnelAxolotl *ax,
+ const struct GNUNET_CRYPTO_SymmetricSessionKey *HKr)
+{
+ struct CadetTunnelSkippedKey *key;
+
+ key = GNUNET_new (struct CadetTunnelSkippedKey);
+ key->timestamp = GNUNET_TIME_absolute_get ();
+ key->Kn = ax->Nr;
+ key->HK = ax->HKr;
+ t_hmac_derive_key (&ax->CKr,
+ &key->MK,
+ "0",
+ 1);
+ t_hmac_derive_key (&ax->CKr,
+ &ax->CKr,
+ "1",
+ 1);
+ GNUNET_CONTAINER_DLL_insert (ax->skipped_head,
+ ax->skipped_tail,
+ key);
+ ax->skipped++;
+ ax->Nr++;
+}
+
+
+/**
+ * Stage skipped AX keys and calculate the message key.
+ * Stores each HK and MK for skipped messages.
+ *
+ * @param ax key material to use
+ * @param HKr Header key.
+ * @param Np Received meesage number.
+ * @return #GNUNET_OK if keys were stored.
+ * #GNUNET_SYSERR if an error ocurred (@a Np not expected).
+ */
+static int
+store_ax_keys (struct CadetTunnelAxolotl *ax,
+ const struct GNUNET_CRYPTO_SymmetricSessionKey *HKr,
+ uint32_t Np)
+{
+ int gap;
+
+ gap = Np - ax->Nr;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Storing skipped keys [%u, %u)\n",
+ ax->Nr,
+ Np);
+ if (MAX_KEY_GAP < gap)
+ {
+ /* Avoid DoS (forcing peer to do more than #MAX_KEY_GAP 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,
+ ax->Nr);
+ return GNUNET_SYSERR;
+ }
+ if (0 > gap)
+ {
+ /* Delayed message: don't store keys, flag to try old keys. */
+ return GNUNET_SYSERR;
+ }
+
+ while (ax->Nr < Np)
+ store_skipped_key (ax,
+ HKr);
+
+ while (ax->skipped > MAX_SKIPPED_KEYS)
+ delete_skipped_key (ax,
+ 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 ax key material 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 ssize_t
+t_ax_decrypt_and_validate (struct CadetTunnelAxolotl *ax,
+ void *dst,
+ const struct GNUNET_CADET_TunnelEncryptedMessage *src,
+ size_t size)
+{
+ 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 */
+
+ esize = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage);
+
+ /* Try current HK */
+ t_hmac (&src->ax_header,
+ sizeof (struct GNUNET_CADET_AxHeader) + 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 */
+ t_hmac (&src->ax_header,
+ sizeof (struct GNUNET_CADET_AxHeader) + 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 (ax,
+ dst,
+ src,
+ size);
+ }
+ HK = ax->HKr;
+ ax->HKr = ax->NHKr;
+ t_h_decrypt (ax,
+ src,
+ &plaintext_header);
+ Np = ntohl (plaintext_header.ax_header.Ns);
+ PNp = ntohl (plaintext_header.ax_header.PNs);
+ DHRp = &plaintext_header.ax_header.DHRs;
+ store_ax_keys (ax,
+ &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
+ {
+ t_h_decrypt (ax,
+ src,
+ &plaintext_header);
+ Np = ntohl (plaintext_header.ax_header.Ns);
+ PNp = ntohl (plaintext_header.ax_header.PNs);
+ }
+ if ( (Np != ax->Nr) &&
+ (GNUNET_OK != store_ax_keys (ax,
+ &ax->HKr,
+ Np)) )
+ {
+ /* Try the skipped keys, if that fails, we're out of luck. */
+ return try_old_ax_keys (ax,
+ dst,
+ src,
+ size);
+ }
+
+ t_ax_decrypt (ax,
+ dst,
+ &src[1],
+ esize);
+ ax->Nr = Np + 1;
+ return esize;
+}
+
+
+/**
+ * Our tunnel became ready for the first time, notify channels
+ * that have been waiting.
+ *
+ * @param cls our tunnel, not used
+ * @param key unique ID of the channel, not used
+ * @param value the `struct CadetChannel` to notify
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static int
+notify_tunnel_up_cb (void *cls,
+ uint32_t key,
+ void *value)
+{
+ struct CadetChannel *ch = value;
+
+ GCCH_tunnel_up (ch);
+ return GNUNET_OK;
+}
+
+
+/**
+ * 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 = t->estate;
+
+ t->estate = state;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "%s estate changed from %s to %s\n",
+ GCT_2s (t),
+ estate2s (old),
+ estate2s (state));
+
+ if ( (CADET_TUNNEL_KEY_OK != old) &&
+ (CADET_TUNNEL_KEY_OK == t->estate) )
+ {
+ if (NULL != t->kx_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = NULL;
+ }
+ /* notify all channels that have been waiting */
+ GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
+ ¬ify_tunnel_up_cb,
+ t);
+ if (NULL != t->send_task)
+ GNUNET_SCHEDULER_cancel (t->send_task);
+ t->send_task = GNUNET_SCHEDULER_add_now (&trigger_transmissions,
+ t);
+ }
+}
+
+
+/**
+ * Send a KX message.
+ *
+ * @param t tunnel on which to send the KX_AUTH
+ * @param ct Tunnel and connection on which to send the KX_AUTH, NULL if
+ * we are to find one that is ready.
+ * @param ax axolotl key context to use
+ */
+static void
+send_kx (struct CadetTunnel *t,
+ struct CadetTConnection *ct,
+ struct CadetTunnelAxolotl *ax)
+{
+ struct CadetConnection *cc;
+ struct GNUNET_MQ_Envelope *env;
+ struct GNUNET_CADET_TunnelKeyExchangeMessage *msg;
+ enum GNUNET_CADET_KX_Flags flags;
+
+ if ( (NULL == ct) ||
+ (GNUNET_NO == ct->is_ready) )
+ ct = get_ready_connection (t);
+ if (NULL == ct)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Wanted to send %s in state %s, but no connection is ready, deferring\n",
+ GCT_2s (t),
+ estate2s (t->estate));
+ t->next_kx_attempt = GNUNET_TIME_absolute_get ();
+ return;
+ }
+ cc = ct->cc;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending KX on %s via %s in state %s\n",
+ GCT_2s (t),
+ GCC_2s (cc),
+ estate2s (t->estate));
+ env = GNUNET_MQ_msg (msg,
+ GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX);
+ flags = GNUNET_CADET_KX_FLAG_FORCE_REPLY; /* always for KX */
+ msg->flags = htonl (flags);
+ msg->cid = *GCC_get_id (cc);
+ GNUNET_CRYPTO_ecdhe_key_get_public (&ax->kx_0,
+ &msg->ephemeral_key);
+ GNUNET_CRYPTO_ecdhe_key_get_public (&ax->DHRs,
+ &msg->ratchet_key);
+ mark_connection_unready (ct);
+ t->kx_retry_delay = GNUNET_TIME_STD_BACKOFF (t->kx_retry_delay);
+ t->next_kx_attempt = GNUNET_TIME_relative_to_absolute (t->kx_retry_delay);
+ if (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate)
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_AX_SENT);
+ else if (CADET_TUNNEL_KEY_AX_RECV == t->estate)
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_AX_SENT_AND_RECV);
+ GCC_transmit (cc,
+ env);
+}
+
+
+/**
+ * Send a KX_AUTH message.
+ *
+ * @param t tunnel on which to send the KX_AUTH
+ * @param ct Tunnel and connection on which to send the KX_AUTH, NULL if
+ * we are to find one that is ready.
+ * @param ax axolotl key context to use
+ * @param force_reply Force the other peer to reply with a KX_AUTH message
+ * (set if we would like to transmit right now, but cannot)
+ */
+static void
+send_kx_auth (struct CadetTunnel *t,
+ struct CadetTConnection *ct,
+ struct CadetTunnelAxolotl *ax,
+ int force_reply)
+{
+ struct CadetConnection *cc;
+ struct GNUNET_MQ_Envelope *env;
+ struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg;
+ enum GNUNET_CADET_KX_Flags flags;
+
+ if ( (NULL == ct) ||
+ (GNUNET_NO == ct->is_ready) )
+ ct = get_ready_connection (t);
+ if (NULL == ct)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Wanted to send KX_AUTH on %s, but no connection is ready, deferring\n",
+ GCT_2s (t));
+ t->next_kx_attempt = GNUNET_TIME_absolute_get ();
+ t->kx_auth_requested = GNUNET_YES; /* queue KX_AUTH independent of estate */
+ return;
+ }
+ t->kx_auth_requested = GNUNET_NO; /* clear flag */
+ cc = ct->cc;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending KX_AUTH on %s using %s\n",
+ GCT_2s (t),
+ GCC_2s (ct->cc));
+
+ env = GNUNET_MQ_msg (msg,
+ GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX_AUTH);
+ flags = GNUNET_CADET_KX_FLAG_NONE;
+ if (GNUNET_YES == force_reply)
+ flags |= GNUNET_CADET_KX_FLAG_FORCE_REPLY;
+ msg->kx.flags = htonl (flags);
+ msg->kx.cid = *GCC_get_id (cc);
+ GNUNET_CRYPTO_ecdhe_key_get_public (&ax->kx_0,
+ &msg->kx.ephemeral_key);
+ GNUNET_CRYPTO_ecdhe_key_get_public (&ax->DHRs,
+ &msg->kx.ratchet_key);
+ /* Compute authenticator (this is the main difference to #send_kx()) */
+ GNUNET_CRYPTO_hash (&ax->RK,
+ sizeof (ax->RK),
+ &msg->auth);
+
+ /* Compute when to be triggered again; actual job will
+ be scheduled via #connection_ready_cb() */
+ t->kx_retry_delay
+ = GNUNET_TIME_STD_BACKOFF (t->kx_retry_delay);
+ t->next_kx_attempt
+ = GNUNET_TIME_relative_to_absolute (t->kx_retry_delay);
+
+ /* Send via cc, mark it as unready */
+ mark_connection_unready (ct);
+
+ /* Update state machine, unless we are already OK */
+ if (CADET_TUNNEL_KEY_OK != t->estate)
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_AX_AUTH_SENT);
+
+ GCC_transmit (cc,
+ env);
+}
+
+
+/**
+ * Cleanup state used by @a ax.
+ *
+ * @param ax state to free, but not memory of @a ax itself
+ */
+static void
+cleanup_ax (struct CadetTunnelAxolotl *ax)
+{
+ while (NULL != ax->skipped_head)
+ delete_skipped_key (ax,
+ ax->skipped_head);
+ GNUNET_assert (0 == ax->skipped);
+ GNUNET_CRYPTO_ecdhe_key_clear (&ax->kx_0);
+ GNUNET_CRYPTO_ecdhe_key_clear (&ax->DHRs);
+}
+
+
+/**
+ * Update our Axolotl key state based on the KX data we received.
+ * Computes the new chain keys, and root keys, etc, and also checks
+ * wether this is a replay of the current chain.
+ *
+ * @param[in|out] axolotl chain key state to recompute
+ * @param pid peer identity of the other peer
+ * @param ephemeral_key ephemeral public key of the other peer
+ * @param ratchet_key senders next ephemeral public key
+ * @return #GNUNET_OK on success, #GNUNET_NO if the resulting
+ * root key is already in @a ax and thus the KX is useless;
+ * #GNUNET_SYSERR on hard errors (i.e. @a pid is #my_full_id)
+ */
+static int
+update_ax_by_kx (struct CadetTunnelAxolotl *ax,
+ const struct GNUNET_PeerIdentity *pid,
+ const struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral_key,
+ const struct GNUNET_CRYPTO_EcdhePublicKey *ratchet_key)
+{
+ struct GNUNET_HashCode key_material[3];
+ struct GNUNET_CRYPTO_SymmetricSessionKey keys[5];
+ const char salt[] = "CADET Axolotl salt";
+ int am_I_alice;
+
+ 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 GNUNET_SYSERR;
+ }
+
+ if (0 == memcmp (&ax->DHRr,
+ ratchet_key,
+ sizeof (*ratchet_key)))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Ratchet key already known. Ignoring KX.\n");
+ return GNUNET_NO;
+ }
+
+ ax->DHRr = *ratchet_key;
+
+ /* ECDH A B0 */
+ if (GNUNET_YES == am_I_alice)
+ {
+ GNUNET_CRYPTO_eddsa_ecdh (my_private_key, /* A */
+ 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 (my_private_key, /* A */
+ 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 */
+ ephemeral_key, /* B0 or A0 */
+ &key_material[2]);
+
+ /* 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_DEBUG,
+ "Root key of handshake already known. Ignoring KX.\n");
+ return GNUNET_NO;
+ }
+
+ 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_expiration
+ = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(),
+ ratchet_time);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Try to redo the KX or KX_AUTH handshake, if we can.
+ *
+ * @param cls the `struct CadetTunnel` to do KX for.
+ */
+static void
+retry_kx (void *cls)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetTunnelAxolotl *ax;
+
+ t->kx_task = NULL;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Trying to make KX progress on %s in state %s\n",
+ GCT_2s (t),
+ estate2s (t->estate));
+ switch (t->estate)
+ {
+ case CADET_TUNNEL_KEY_UNINITIALIZED: /* first attempt */
+ case CADET_TUNNEL_KEY_AX_SENT: /* trying again */
+ send_kx (t,
+ NULL,
+ &t->ax);
+ break;
+ case CADET_TUNNEL_KEY_AX_RECV:
+ case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
+ /* We are responding, so only require reply
+ if WE have a channel waiting. */
+ if (NULL != t->unverified_ax)
+ {
+ /* Send AX_AUTH so we might get this one verified */
+ ax = t->unverified_ax;
+ }
+ else
+ {
+ /* How can this be? */
+ GNUNET_break (0);
+ ax = &t->ax;
+ }
+ send_kx_auth (t,
+ NULL,
+ ax,
+ (0 == GCT_count_channels (t))
+ ? GNUNET_NO
+ : GNUNET_YES);
+ break;
+ case CADET_TUNNEL_KEY_AX_AUTH_SENT:
+ /* We are responding, so only require reply
+ if WE have a channel waiting. */
+ if (NULL != t->unverified_ax)
+ {
+ /* Send AX_AUTH so we might get this one verified */
+ ax = t->unverified_ax;
+ }
+ else
+ {
+ /* How can this be? */
+ GNUNET_break (0);
+ ax = &t->ax;
+ }
+ send_kx_auth (t,
+ NULL,
+ ax,
+ (0 == GCT_count_channels (t))
+ ? GNUNET_NO
+ : GNUNET_YES);
+ break;
+ case CADET_TUNNEL_KEY_OK:
+ /* Must have been the *other* peer asking us to
+ respond with a KX_AUTH. */
+ if (NULL != t->unverified_ax)
+ {
+ /* Sending AX_AUTH in response to AX so we might get this one verified */
+ ax = t->unverified_ax;
+ }
+ else
+ {
+ /* Sending AX_AUTH in response to AX_AUTH */
+ ax = &t->ax;
+ }
+ send_kx_auth (t,
+ NULL,
+ ax,
+ GNUNET_NO);
+ break;
+ }
+}
+
+
+/**
+ * Handle KX message that lacks authentication (and which will thus
+ * only be considered authenticated after we respond with our own
+ * KX_AUTH and finally successfully decrypt payload).
+ *
+ * @param ct connection/tunnel combo that received encrypted message
+ * @param msg the key exchange message
+ */
+void
+GCT_handle_kx (struct CadetTConnection *ct,
+ const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg)
+{
+ struct CadetTunnel *t = ct->t;
+ struct CadetTunnelAxolotl *ax;
+ int ret;
+
+ if (0 ==
+ memcmp (&t->ax.DHRr,
+ &msg->ratchet_key,
+ sizeof (msg->ratchet_key)))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Got duplicate KX. Firing back KX_AUTH.\n");
+ send_kx_auth (t,
+ ct,
+ &t->ax,
+ GNUNET_NO);
+ return;
+ }
+
+ /* We only keep ONE unverified KX around, so if there is an existing one,
+ clean it up. */
+ if (NULL != t->unverified_ax)
+ {
+ if (0 ==
+ memcmp (&t->unverified_ax->DHRr,
+ &msg->ratchet_key,
+ sizeof (msg->ratchet_key)))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Got duplicate unverified KX on %s. Fire back KX_AUTH again.\n",
+ GCT_2s (t));
+ send_kx_auth (t,
+ ct,
+ t->unverified_ax,
+ GNUNET_NO);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Dropping old unverified KX state. Got a fresh KX for %s.\n",
+ GCT_2s (t));
+ memset (t->unverified_ax,
+ 0,
+ sizeof (struct CadetTunnelAxolotl));
+ t->unverified_ax->DHRs = t->ax.DHRs;
+ t->unverified_ax->kx_0 = t->ax.kx_0;
+ }
+ else
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Creating fresh unverified KX for %s.\n",
+ GCT_2s (t));
+ t->unverified_ax = GNUNET_new (struct CadetTunnelAxolotl);
+ t->unverified_ax->DHRs = t->ax.DHRs;
+ t->unverified_ax->kx_0 = t->ax.kx_0;
+ }
+ /* Set as the 'current' RK/DHRr the one we are currently using,
+ so that the duplicate-detection logic of
+ #update_ax_by_kx can work. */
+ t->unverified_ax->RK = t->ax.RK;
+ t->unverified_ax->DHRr = t->ax.DHRr;
+ t->unverified_attempts = 0;
+ ax = t->unverified_ax;
+
+ /* Update 'ax' by the new key material */
+ ret = update_ax_by_kx (ax,
+ GCP_get_id (t->destination),
+ &msg->ephemeral_key,
+ &msg->ratchet_key);
+ GNUNET_break (GNUNET_SYSERR != ret);
+ if (GNUNET_OK != ret)
+ return; /* duplicate KX, nothing to do */
+
+ /* move ahead in our state machine */
+ if (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate)
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_AX_RECV);
+ else if (CADET_TUNNEL_KEY_AX_SENT == t->estate)
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_AX_SENT_AND_RECV);
+
+ /* KX is still not done, try again our end. */
+ if (CADET_TUNNEL_KEY_OK != t->estate)
+ {
+ if (NULL != t->kx_task)
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task
+ = GNUNET_SCHEDULER_add_now (&retry_kx,
+ t);
+ }
+}
+
+
+/**
+ * Handle KX_AUTH message.
+ *
+ * @param ct connection/tunnel combo that received encrypted message
+ * @param msg the key exchange message
+ */
+void
+GCT_handle_kx_auth (struct CadetTConnection *ct,
+ const struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg)
+{
+ struct CadetTunnel *t = ct->t;
+ struct CadetTunnelAxolotl ax_tmp;
+ struct GNUNET_HashCode kx_auth;
+ int ret;
+
+ if ( (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate) ||
+ (CADET_TUNNEL_KEY_AX_RECV == t->estate) )
+ {
+ /* Confusing, we got a KX_AUTH before we even send our own
+ KX. This should not happen. We'll send our own KX ASAP anyway,
+ so let's ignore this here. */
+ GNUNET_break_op (0);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Handling KX_AUTH message for %s\n",
+ GCT_2s (t));
+
+ /* We do everything in ax_tmp until we've checked the authentication
+ so we don't clobber anything we care about by accident. */
+ ax_tmp = t->ax;
+
+ /* Update 'ax' by the new key material */
+ ret = update_ax_by_kx (&ax_tmp,
+ GCP_get_id (t->destination),
+ &msg->kx.ephemeral_key,
+ &msg->kx.ratchet_key);
+ if (GNUNET_OK != ret)
+ {
+ if (GNUNET_NO == ret)
+ GNUNET_STATISTICS_update (stats,
+ "# redundant KX_AUTH received",
+ 1,
+ GNUNET_NO);
+ else
+ GNUNET_break (0); /* connect to self!? */
+ return;
+ }
+ GNUNET_CRYPTO_hash (&ax_tmp.RK,
+ sizeof (ax_tmp.RK),
+ &kx_auth);
+ if (0 != memcmp (&kx_auth,
+ &msg->auth,
+ sizeof (kx_auth)))
+ {
+ /* This KX_AUTH is not using the latest KX/KX_AUTH data
+ we transmitted to the sender, refuse it, try KX again. */
+ GNUNET_STATISTICS_update (stats,
+ "# KX_AUTH not using our last KX received (auth failure)",
+ 1,
+ GNUNET_NO);
+ send_kx (t,
+ ct,
+ &t->ax);
+ return;
+ }
+ /* Yep, we're good. */
+ t->ax = ax_tmp;
+ if (NULL != t->unverified_ax)
+ {
+ /* We got some "stale" KX before, drop that. */
+ cleanup_ax (t->unverified_ax);
+ GNUNET_free (t->unverified_ax);
+ t->unverified_ax = NULL;
+ }
+
+ /* move ahead in our state machine */
+ switch (t->estate)
+ {
+ case CADET_TUNNEL_KEY_UNINITIALIZED:
+ case CADET_TUNNEL_KEY_AX_RECV:
+ /* Checked above, this is impossible. */
+ GNUNET_assert (0);
+ break;
+ case CADET_TUNNEL_KEY_AX_SENT: /* This is the normal case */
+ case CADET_TUNNEL_KEY_AX_SENT_AND_RECV: /* both peers started KX */
+ case CADET_TUNNEL_KEY_AX_AUTH_SENT: /* both peers now did KX_AUTH */
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_OK);
+ break;
+ case CADET_TUNNEL_KEY_OK:
+ /* Did not expect another KX_AUTH, but so what, still acceptable.
+ Nothing to do here. */
+ break;
+ }
+}
+
+
+
+/* ************************************** end core crypto ***************************** */
+
+
+/**
+ * Compute the next free channel tunnel number for this tunnel.
+ *
+ * @param t the tunnel
+ * @return unused number that can uniquely identify a channel in the tunnel
+ */
+static struct GNUNET_CADET_ChannelTunnelNumber
+get_next_free_ctn (struct CadetTunnel *t)
+{
+#define HIGH_BIT 0x8000000
+ struct GNUNET_CADET_ChannelTunnelNumber ret;
+ uint32_t ctn;
+ int cmp;
+ uint32_t highbit;
+
+ cmp = GNUNET_CRYPTO_cmp_peer_identity (&my_full_id,
+ GCP_get_id (GCT_get_destination (t)));
+ if (0 < cmp)
+ highbit = HIGH_BIT;
+ else if (0 > cmp)
+ highbit = 0;
+ else
+ GNUNET_assert (0); // loopback must never go here!
+ ctn = ntohl (t->next_ctn.cn);
+ while (NULL !=
+ GNUNET_CONTAINER_multihashmap32_get (t->channels,
+ ctn | highbit))
+ {
+ ctn = ((ctn + 1) & (~ HIGH_BIT));
+ }
+ t->next_ctn.cn = htonl ((ctn + 1) & (~ HIGH_BIT));
+ ret.cn = htonl (ctn | highbit);
+ return ret;
+}
+
+
+/**
+ * Add a channel to a tunnel, and notify channel that we are ready
+ * for transmission if we are already up. Otherwise that notification
+ * will be done later in #notify_tunnel_up_cb().
+ *
+ * @param t Tunnel.
+ * @param ch Channel
+ * @return unique number identifying @a ch within @a t
+ */
+struct GNUNET_CADET_ChannelTunnelNumber
+GCT_add_channel (struct CadetTunnel *t,
+ struct CadetChannel *ch)
+{
+ struct GNUNET_CADET_ChannelTunnelNumber ctn;
+
+ ctn = get_next_free_ctn (t);
+ if (NULL != t->destroy_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->destroy_task);
+ t->destroy_task = NULL;
+ }
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap32_put (t->channels,
+ ntohl (ctn.cn),
+ ch,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding %s to %s\n",
+ GCCH_2s (ch),
+ GCT_2s (t));
+ switch (t->estate)
+ {
+ case CADET_TUNNEL_KEY_UNINITIALIZED:
+ /* waiting for connection to start KX */
+ break;
+ case CADET_TUNNEL_KEY_AX_RECV:
+ case CADET_TUNNEL_KEY_AX_SENT:
+ case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
+ /* we're currently waiting for KX to complete */
+ break;
+ case CADET_TUNNEL_KEY_AX_AUTH_SENT:
+ /* waiting for OTHER peer to send us data,
+ we might need to prompt more aggressively! */
+ if (NULL == t->kx_task)
+ t->kx_task
+ = GNUNET_SCHEDULER_add_at (t->next_kx_attempt,
+ &retry_kx,
+ t);
+ break;
+ case CADET_TUNNEL_KEY_OK:
+ /* We are ready. Tell the new channel that we are up. */
+ GCCH_tunnel_up (ch);
+ break;
+ }
+ return ctn;
+}
+
+
+/**
+ * We lost a connection, remove it from our list and clean up
+ * the connection object itself.
+ *
+ * @param ct binding of connection to tunnel of the connection that was lost.
+ */
+void
+GCT_connection_lost (struct CadetTConnection *ct)
+{
+ struct CadetTunnel *t = ct->t;
+
+ if (GNUNET_YES == ct->is_ready)
+ {
+ GNUNET_CONTAINER_DLL_remove (t->connection_ready_head,
+ t->connection_ready_tail,
+ ct);
+ t->num_ready_connections--;
+ }
+ else
+ {
+ GNUNET_CONTAINER_DLL_remove (t->connection_busy_head,
+ t->connection_busy_tail,
+ ct);
+ t->num_busy_connections--;
+ }
+ GNUNET_free (ct);
+}
+
+
+/**
+ * Clean up connection @a ct of a tunnel.
+ *
+ * @param cls the `struct CadetTunnel`
+ * @param ct connection to clean up
+ */
+static void
+destroy_t_connection (void *cls,
+ struct CadetTConnection *ct)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetConnection *cc = ct->cc;
+
+ GNUNET_assert (ct->t == t);
+ GCT_connection_lost (ct);
+ GCC_destroy_without_tunnel (cc);
+}
+
+
+/**
+ * This tunnel is no longer used, destroy it.
+ *
+ * @param cls the idle tunnel
+ */
+static void
+destroy_tunnel (void *cls)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetTunnelQueueEntry *tq;
+
+ t->destroy_task = NULL;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Destroying idle %s\n",
+ GCT_2s (t));
+ GNUNET_assert (0 == GCT_count_channels (t));
+ GCT_iterate_connections (t,
+ &destroy_t_connection,
+ t);
+ GNUNET_assert (NULL == t->connection_ready_head);
+ GNUNET_assert (NULL == t->connection_busy_head);
+ while (NULL != (tq = t->tq_head))
+ {
+ if (NULL != tq->cont)
+ tq->cont (tq->cont_cls,
+ NULL);
+ GCT_send_cancel (tq);
+ }
+ GCP_drop_tunnel (t->destination,
+ t);
+ GNUNET_CONTAINER_multihashmap32_destroy (t->channels);
+ if (NULL != t->maintain_connections_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->maintain_connections_task);
+ t->maintain_connections_task = NULL;
+ }
+ if (NULL != t->send_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->send_task);
+ t->send_task = NULL;
+ }
+ if (NULL != t->kx_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = NULL;
+ }
+ GNUNET_MST_destroy (t->mst);
+ GNUNET_MQ_destroy (t->mq);
+ if (NULL != t->unverified_ax)
+ {
+ cleanup_ax (t->unverified_ax);
+ GNUNET_free (t->unverified_ax);
+ }
+ cleanup_ax (&t->ax);
+ GNUNET_assert (NULL == t->destroy_task);
+ GNUNET_free (t);
+}
+
+
+/**
+ * Remove a channel from a tunnel.
+ *
+ * @param t Tunnel.
+ * @param ch Channel
+ * @param ctn unique number identifying @a ch within @a t
+ */
+void
+GCT_remove_channel (struct CadetTunnel *t,
+ struct CadetChannel *ch,
+ struct GNUNET_CADET_ChannelTunnelNumber ctn)
+{
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Removing %s from %s\n",
+ GCCH_2s (ch),
+ GCT_2s (t));
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap32_remove (t->channels,
+ ntohl (ctn.cn),
+ ch));
+ if ( (0 ==
+ GCT_count_channels (t)) &&
+ (NULL == t->destroy_task) )
+ {
+ t->destroy_task
+ = GNUNET_SCHEDULER_add_delayed (IDLE_DESTROY_DELAY,
+ &destroy_tunnel,
+ t);
+ }
+}
+
+
+/**
+ * Destroy remaining channels during shutdown.
+ *
+ * @param cls the `struct CadetTunnel` of the channel
+ * @param key key of the channel
+ * @param value the `struct CadetChannel`
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static int
+destroy_remaining_channels (void *cls,
+ uint32_t key,
+ void *value)
+{
+ struct CadetChannel *ch = value;
+
+ GCCH_handle_remote_destroy (ch,
+ NULL);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Destroys the tunnel @a t now, without delay. Used during shutdown.
+ *
+ * @param t tunnel to destroy
+ */
+void
+GCT_destroy_tunnel_now (struct CadetTunnel *t)
+{
+ GNUNET_assert (GNUNET_YES == shutting_down);
+ GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
+ &destroy_remaining_channels,
+ t);
+ GNUNET_assert (0 ==
+ GCT_count_channels (t));
+ if (NULL != t->destroy_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->destroy_task);
+ t->destroy_task = NULL;
+ }
+ destroy_tunnel (t);
+}
+
+
+/**
+ * Send normal payload from queue in @a t via connection @a ct.
+ * Does nothing if our payload queue is empty.
+ *
+ * @param t tunnel to send data from
+ * @param ct connection to use for transmission (is ready)
+ */
+static void
+try_send_normal_payload (struct CadetTunnel *t,
+ struct CadetTConnection *ct)
+{
+ struct CadetTunnelQueueEntry *tq;
+
+ GNUNET_assert (GNUNET_YES == ct->is_ready);
+ tq = t->tq_head;
+ if (NULL == tq)
+ {
+ /* no messages pending right now */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Not sending payload of %s on ready %s (nothing pending)\n",
+ GCT_2s (t),
+ GCC_2s (ct->cc));
+ return;
+ }
+ /* ready to send message 'tq' on tunnel 'ct' */
+ GNUNET_assert (t == tq->t);
+ GNUNET_CONTAINER_DLL_remove (t->tq_head,
+ t->tq_tail,
+ tq);
+ if (NULL != tq->cid)
+ *tq->cid = *GCC_get_id (ct->cc);
+ mark_connection_unready (ct);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending payload of %s on %s\n",
+ GCT_2s (t),
+ GCC_2s (ct->cc));
+ GCC_transmit (ct->cc,
+ tq->env);
+ if (NULL != tq->cont)
+ tq->cont (tq->cont_cls,
+ GCC_get_id (ct->cc));
+ GNUNET_free (tq);
+}
+
+
+/**
+ * A connection is @a is_ready for transmission. Looks at our message
+ * queue and if there is a message, sends it out via the connection.
+ *
+ * @param cls the `struct CadetTConnection` that is @a is_ready
+ * @param is_ready #GNUNET_YES if connection are now ready,
+ * #GNUNET_NO if connection are no longer ready
+ */
+static void
+connection_ready_cb (void *cls,
+ int is_ready)
+{
+ struct CadetTConnection *ct = cls;
+ struct CadetTunnel *t = ct->t;
+
+ if (GNUNET_NO == is_ready)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "%s no longer ready for %s\n",
+ GCC_2s (ct->cc),
+ GCT_2s (t));
+ mark_connection_unready (ct);
+ return;
+ }
+ GNUNET_assert (GNUNET_NO == ct->is_ready);
+ GNUNET_CONTAINER_DLL_remove (t->connection_busy_head,
+ t->connection_busy_tail,
+ ct);
+ GNUNET_assert (0 < t->num_busy_connections);
+ t->num_busy_connections--;
+ ct->is_ready = GNUNET_YES;
+ GNUNET_CONTAINER_DLL_insert_tail (t->connection_ready_head,
+ t->connection_ready_tail,
+ ct);
+ t->num_ready_connections++;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "%s now ready for %s in state %s\n",
+ GCC_2s (ct->cc),
+ GCT_2s (t),
+ estate2s (t->estate));
+ switch (t->estate)
+ {
+ case CADET_TUNNEL_KEY_UNINITIALIZED:
+ /* Do not begin KX if WE have no channels waiting! */
+ if (0 == GCT_count_channels (t))
+ return;
+ /* We are uninitialized, just transmit immediately,
+ without undue delay. */
+ if (NULL != t->kx_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = NULL;
+ }
+ send_kx (t,
+ ct,
+ &t->ax);
+ break;
+ case CADET_TUNNEL_KEY_AX_RECV:
+ case CADET_TUNNEL_KEY_AX_SENT:
+ case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
+ case CADET_TUNNEL_KEY_AX_AUTH_SENT:
+ /* we're currently waiting for KX to complete, schedule job */
+ if (NULL == t->kx_task)
+ t->kx_task
+ = GNUNET_SCHEDULER_add_at (t->next_kx_attempt,
+ &retry_kx,
+ t);
+ break;
+ case CADET_TUNNEL_KEY_OK:
+ if (GNUNET_YES == t->kx_auth_requested)
+ {
+ if (NULL != t->kx_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = NULL;
+ }
+ send_kx_auth (t,
+ ct,
+ &t->ax,
+ GNUNET_NO);
+ return;
+ }
+ try_send_normal_payload (t,
+ ct);
+ break;
+ }
+}
+
+
+/**
+ * Called when either we have a new connection, or a new message in the
+ * queue, or some existing connection has transmission capacity. Looks
+ * at our message queue and if there is a message, picks a connection
+ * to send it on.
+ *
+ * @param cls the `struct CadetTunnel` to process messages on
+ */
+static void
+trigger_transmissions (void *cls)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetTConnection *ct;
+
+ t->send_task = NULL;
+ if (NULL == t->tq_head)
+ return; /* no messages pending right now */
+ ct = get_ready_connection (t);
+ if (NULL == ct)
+ return; /* no connections ready */
+ try_send_normal_payload (t,
+ ct);
+}
+
+
+/**
+ * Closure for #evaluate_connection. Used to assemble summary information
+ * about the existing connections so we can evaluate a new path.
+ */
+struct EvaluationSummary
+{
+
+ /**
+ * Minimum length of any of our connections, `UINT_MAX` if we have none.
+ */
+ unsigned int min_length;
+
+ /**
+ * Maximum length of any of our connections, 0 if we have none.
+ */
+ unsigned int max_length;
+
+ /**
+ * Minimum desirability of any of our connections, UINT64_MAX if we have none.
+ */
+ GNUNET_CONTAINER_HeapCostType min_desire;
+
+ /**
+ * Maximum desirability of any of our connections, 0 if we have none.
+ */
+ GNUNET_CONTAINER_HeapCostType max_desire;
+
+ /**
+ * Path we are comparing against for #evaluate_connection, can be NULL.
+ */
+ struct CadetPeerPath *path;
+
+ /**
+ * Connection deemed the "worst" so far encountered by #evaluate_connection,
+ * NULL if we did not yet encounter any connections.
+ */
+ struct CadetTConnection *worst;
+
+ /**
+ * Numeric score of @e worst, only set if @e worst is non-NULL.
+ */
+ double worst_score;
+
+ /**
+ * Set to #GNUNET_YES if we have a connection over @e path already.
+ */
+ int duplicate;
+
+};
+
+
+/**
+ * Evaluate a connection, updating our summary information in @a cls about
+ * what kinds of connections we have.
+ *
+ * @param cls the `struct EvaluationSummary *` to update
+ * @param ct a connection to include in the summary
+ */
+static void
+evaluate_connection (void *cls,
+ struct CadetTConnection *ct)
+{
+ struct EvaluationSummary *es = cls;
+ struct CadetConnection *cc = ct->cc;
+ struct CadetPeerPath *ps = GCC_get_path (cc);
+ const struct CadetConnectionMetrics *metrics;
+ GNUNET_CONTAINER_HeapCostType ct_desirability;
+ struct GNUNET_TIME_Relative uptime;
+ struct GNUNET_TIME_Relative last_use;
+ uint32_t ct_length;
+ double score;
+ double success_rate;
+
+ if (ps == es->path)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Ignoring duplicate path %s.\n",
+ GCPP_2s (es->path));
+ es->duplicate = GNUNET_YES;
+ return;
+ }
+ ct_desirability = GCPP_get_desirability (ps);
+ ct_length = GCPP_get_length (ps);
+ metrics = GCC_get_metrics (cc);
+ uptime = GNUNET_TIME_absolute_get_duration (metrics->age);
+ last_use = GNUNET_TIME_absolute_get_duration (metrics->last_use);
+ /* We add 1.0 here to avoid division by zero. */
+ success_rate = (metrics->num_acked_transmissions + 1.0) / (metrics->num_successes + 1.0);
+ score
+ = ct_desirability
+ + 100.0 / (1.0 + ct_length) /* longer paths = better */
+ + sqrt (uptime.rel_value_us / 60000000LL) /* larger uptime = better */
+ - last_use.rel_value_us / 1000L; /* longer idle = worse */
+ score *= success_rate; /* weigh overall by success rate */
+
+ if ( (NULL == es->worst) ||
+ (score < es->worst_score) )
+ {
+ es->worst = ct;
+ es->worst_score = score;
+ }
+ es->min_length = GNUNET_MIN (es->min_length,
+ ct_length);
+ es->max_length = GNUNET_MAX (es->max_length,
+ ct_length);
+ es->min_desire = GNUNET_MIN (es->min_desire,
+ ct_desirability);
+ es->max_desire = GNUNET_MAX (es->max_desire,
+ ct_desirability);
+}
+
+
+/**
+ * Consider using the path @a p for the tunnel @a t.
+ * The tunnel destination is at offset @a off in path @a p.
+ *
+ * @param cls our tunnel
+ * @param path a path to our destination
+ * @param off offset of the destination on path @a path
+ * @return #GNUNET_YES (should keep iterating)
+ */
+static int
+consider_path_cb (void *cls,
+ struct CadetPeerPath *path,
+ unsigned int off)
+{
+ struct CadetTunnel *t = cls;
+ struct EvaluationSummary es;
+ struct CadetTConnection *ct;
+
+ GNUNET_assert (off < GCPP_get_length (path));
+ es.min_length = UINT_MAX;
+ es.max_length = 0;
+ es.max_desire = 0;
+ es.min_desire = UINT64_MAX;
+ es.path = path;
+ es.duplicate = GNUNET_NO;
+ es.worst = NULL;
+
+ /* Compute evaluation summary over existing connections. */
+ GCT_iterate_connections (t,
+ &evaluate_connection,
+ &es);
+ if (GNUNET_YES == es.duplicate)
+ return GNUNET_YES;
+
+ /* FIXME: not sure we should really just count
+ 'num_connections' here, as they may all have
+ consistently failed to connect. */
+
+ /* We iterate by increasing path length; if we have enough paths and
+ this one is more than twice as long than what we are currently
+ using, then ignore all of these super-long ones! */
+ if ( (GCT_count_any_connections (t) > DESIRED_CONNECTIONS_PER_TUNNEL) &&
+ (es.min_length * 2 < off) &&
+ (es.max_length < off) )
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Ignoring paths of length %u, they are way too long.\n",
+ es.min_length * 2);
+ return GNUNET_NO;
+ }
+ /* If we have enough paths and this one looks no better, ignore it. */
+ if ( (GCT_count_any_connections (t) >= DESIRED_CONNECTIONS_PER_TUNNEL) &&
+ (es.min_length < GCPP_get_length (path)) &&
+ (es.min_desire > GCPP_get_desirability (path)) &&
+ (es.max_length < off) )
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Ignoring path (%u/%llu) to %s, got something better already.\n",
+ GCPP_get_length (path),
+ (unsigned long long) GCPP_get_desirability (path),
+ GCP_2s (t->destination));
+ return GNUNET_YES;
+ }
+
+ /* Path is interesting (better by some metric, or we don't have
+ enough paths yet). */
+ ct = GNUNET_new (struct CadetTConnection);
+ ct->created = GNUNET_TIME_absolute_get ();
+ ct->t = t;
+ ct->cc = GCC_create (t->destination,
+ path,
+ off,
+ GNUNET_CADET_OPTION_DEFAULT, /* FIXME: set based on what channels want/need! */
+ ct,
+ &connection_ready_cb,
+ ct);
+
+ /* FIXME: schedule job to kill connection (and path?) if it takes
+ too long to get ready! (And track performance data on how long
+ other connections took with the tunnel!)
+ => Note: to be done within 'connection'-logic! */
+ GNUNET_CONTAINER_DLL_insert (t->connection_busy_head,
+ t->connection_busy_tail,
+ ct);
+ t->num_busy_connections++;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Found interesting path %s for %s, created %s\n",
+ GCPP_2s (path),
+ GCT_2s (t),
+ GCC_2s (ct->cc));
+ return GNUNET_YES;
+}
+
+
+/**
+ * Function called to maintain the connections underlying our tunnel.
+ * Tries to maintain (incl. tear down) connections for the tunnel, and
+ * if there is a significant change, may trigger transmissions.
+ *
+ * Basically, needs to check if there are connections that perform
+ * badly, and if so eventually kill them and trigger a replacement.
+ * The strategy is to open one more connection than
+ * #DESIRED_CONNECTIONS_PER_TUNNEL, and then periodically kick out the
+ * least-performing one, and then inquire for new ones.
+ *
+ * @param cls the `struct CadetTunnel`
+ */
+static void
+maintain_connections_cb (void *cls)
+{
+ struct CadetTunnel *t = cls;
+ struct GNUNET_TIME_Relative delay;
+ struct EvaluationSummary es;
+
+ t->maintain_connections_task = NULL;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Performing connection maintenance for %s.\n",
+ GCT_2s (t));
+
+ es.min_length = UINT_MAX;
+ es.max_length = 0;
+ es.max_desire = 0;
+ es.min_desire = UINT64_MAX;
+ es.path = NULL;
+ es.worst = NULL;
+ es.duplicate = GNUNET_NO;
+ GCT_iterate_connections (t,
+ &evaluate_connection,
+ &es);
+ if ( (NULL != es.worst) &&
+ (GCT_count_any_connections (t) > DESIRED_CONNECTIONS_PER_TUNNEL) )
+ {
+ /* Clear out worst-performing connection 'es.worst'. */
+ destroy_t_connection (t,
+ es.worst);
+ }
+
+ /* Consider additional paths */
+ (void) GCP_iterate_paths (t->destination,
+ &consider_path_cb,
+ t);
+
+ /* FIXME: calculate when to try again based on how well we are doing;
+ in particular, if we have to few connections, we might be able
+ to do without this (as PATHS should tell us whenever a new path
+ is available instantly; however, need to make sure this job is
+ restarted after that happens).
+ Furthermore, if the paths we do know are in a reasonably narrow
+ quality band and are plentyful, we might also consider us stabilized
+ and then reduce the frequency accordingly. */
+ delay = GNUNET_TIME_UNIT_MINUTES;
+ t->maintain_connections_task
+ = GNUNET_SCHEDULER_add_delayed (delay,
+ &maintain_connections_cb,
+ t);
+}
+
+
+/**
+ * Consider using the path @a p for the tunnel @a t.
+ * The tunnel destination is at offset @a off in path @a p.
+ *
+ * @param cls our tunnel
+ * @param path a path to our destination
+ * @param off offset of the destination on path @a path
+ */
+void
+GCT_consider_path (struct CadetTunnel *t,
+ struct CadetPeerPath *p,
+ unsigned int off)
+{
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Considering %s for %s\n",
+ GCPP_2s (p),
+ GCT_2s (t));
+ (void) consider_path_cb (t,
+ p,
+ off);
+}
+
+
+/**
+ * We got a keepalive. Track in statistics.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param msg the message we received on the tunnel
+ */
+static void
+handle_plaintext_keepalive (void *cls,
+ const struct GNUNET_MessageHeader *msg)
+{
+ struct CadetTunnel *t = cls;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received KEEPALIVE on %s\n",
+ GCT_2s (t));
+ GNUNET_STATISTICS_update (stats,
+ "# keepalives received",
+ 1,
+ GNUNET_NO);
+}
+
+
+/**
+ * Check that @a msg is well-formed.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param msg the message we received on the tunnel
+ * @return #GNUNET_OK (any variable-size payload goes)
+ */
+static int
+check_plaintext_data (void *cls,
+ const struct GNUNET_CADET_ChannelAppDataMessage *msg)
+{
+ return GNUNET_OK;
+}
+
+
+/**
+ * We received payload data for a channel. Locate the channel
+ * and process the data, or return an error if the channel is unknown.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param msg the message we received on the tunnel
+ */
+static void
+handle_plaintext_data (void *cls,
+ const struct GNUNET_CADET_ChannelAppDataMessage *msg)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetChannel *ch;
+
+ ch = lookup_channel (t,
+ msg->ctn);
+ if (NULL == ch)
+ {
+ /* We don't know about such a channel, might have been destroyed on our
+ end in the meantime, or never existed. Send back a DESTROY. */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received %u bytes of application data for unknown channel %u, sending DESTROY\n",
+ (unsigned int) (ntohs (msg->header.size) - sizeof (*msg)),
+ ntohl (msg->ctn.cn));
+ GCT_send_channel_destroy (t,
+ msg->ctn);
+ return;
+ }
+ GCCH_handle_channel_plaintext_data (ch,
+ GCC_get_id (t->current_ct->cc),
+ msg);
+}
+
+
+/**
+ * We received an acknowledgement for data we sent on a channel.
+ * Locate the channel and process it, or return an error if the
+ * channel is unknown.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param ack the message we received on the tunnel
+ */
+static void
+handle_plaintext_data_ack (void *cls,
+ const struct GNUNET_CADET_ChannelDataAckMessage *ack)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetChannel *ch;
+
+ ch = lookup_channel (t,
+ ack->ctn);
+ if (NULL == ch)
+ {
+ /* We don't know about such a channel, might have been destroyed on our
+ end in the meantime, or never existed. Send back a DESTROY. */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received DATA_ACK for unknown channel %u, sending DESTROY\n",
+ ntohl (ack->ctn.cn));
+ GCT_send_channel_destroy (t,
+ ack->ctn);
+ return;
+ }
+ GCCH_handle_channel_plaintext_data_ack (ch,
+ GCC_get_id (t->current_ct->cc),
+ ack);
+}
+
+
+/**
+ * We have received a request to open a channel to a port from
+ * another peer. Creates the incoming channel.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param copen the message we received on the tunnel
+ */
+static void
+handle_plaintext_channel_open (void *cls,
+ const struct GNUNET_CADET_ChannelOpenMessage *copen)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetChannel *ch;
+
+ ch = GNUNET_CONTAINER_multihashmap32_get (t->channels,
+ ntohl (copen->ctn.cn));
+ if (NULL != ch)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received duplicate channel CHANNEL_OPEN on port %s from %s (%s), resending ACK\n",
+ GNUNET_h2s (&copen->port),
+ GCT_2s (t),
+ GCCH_2s (ch));
+ GCCH_handle_duplicate_open (ch,
+ GCC_get_id (t->current_ct->cc));
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received CHANNEL_OPEN on port %s from %s\n",
+ GNUNET_h2s (&copen->port),
+ GCT_2s (t));
+ ch = GCCH_channel_incoming_new (t,
+ copen->ctn,
+ &copen->port,
+ ntohl (copen->opt));
+ if (NULL != t->destroy_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->destroy_task);
+ t->destroy_task = NULL;
+ }
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap32_put (t->channels,
+ ntohl (copen->ctn.cn),
+ ch,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+}
+
+
+/**
+ * Send a DESTROY message via the tunnel.
+ *
+ * @param t the tunnel to transmit over
+ * @param ctn ID of the channel to destroy
+ */
+void
+GCT_send_channel_destroy (struct CadetTunnel *t,
+ struct GNUNET_CADET_ChannelTunnelNumber ctn)
+{
+ struct GNUNET_CADET_ChannelManageMessage msg;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending DESTORY message for channel ID %u\n",
+ ntohl (ctn.cn));
+ msg.header.size = htons (sizeof (msg));
+ msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY);
+ msg.reserved = htonl (0);
+ msg.ctn = ctn;
+ GCT_send (t,
+ &msg.header,
+ NULL,
+ NULL);
+}
+
+
+/**
+ * We have received confirmation from the target peer that the
+ * given channel could be established (the port is open).
+ * Tell the client.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param cm the message we received on the tunnel
+ */
+static void
+handle_plaintext_channel_open_ack (void *cls,
+ const struct GNUNET_CADET_ChannelManageMessage *cm)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetChannel *ch;
+
+ ch = lookup_channel (t,
+ cm->ctn);
+ if (NULL == ch)
+ {
+ /* We don't know about such a channel, might have been destroyed on our
+ end in the meantime, or never existed. Send back a DESTROY. */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received channel OPEN_ACK for unknown channel %u, sending DESTROY\n",
+ ntohl (cm->ctn.cn));
+ GCT_send_channel_destroy (t,
+ cm->ctn);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received channel OPEN_ACK on channel %s from %s\n",
+ GCCH_2s (ch),
+ GCT_2s (t));
+ GCCH_handle_channel_open_ack (ch,
+ GCC_get_id (t->current_ct->cc));
+}
+
+
+/**
+ * We received a message saying that a channel should be destroyed.
+ * Pass it on to the correct channel.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param cm the message we received on the tunnel
+ */
+static void
+handle_plaintext_channel_destroy (void *cls,
+ const struct GNUNET_CADET_ChannelManageMessage *cm)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetChannel *ch;
+
+ ch = lookup_channel (t,
+ cm->ctn);
+ if (NULL == ch)
+ {
+ /* We don't know about such a channel, might have been destroyed on our
+ end in the meantime, or never existed. */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received channel DESTORY for unknown channel %u. Ignoring.\n",
+ ntohl (cm->ctn.cn));
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received channel DESTROY on %s from %s\n",
+ GCCH_2s (ch),
+ GCT_2s (t));
+ GCCH_handle_remote_destroy (ch,
+ GCC_get_id (t->current_ct->cc));
+}
+
+
+/**
+ * Handles a message we decrypted, by injecting it into
+ * our message queue (which will do the dispatching).
+ *
+ * @param cls the `struct CadetTunnel` that got the message
+ * @param msg the message
+ * @return #GNUNET_OK (continue to process)
+ */
+static int
+handle_decrypted (void *cls,
+ const struct GNUNET_MessageHeader *msg)
+{
+ struct CadetTunnel *t = cls;
+
+ GNUNET_assert (NULL != t->current_ct);
+ GNUNET_MQ_inject_message (t->mq,
+ msg);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called if we had an error processing
+ * an incoming decrypted message.
+ *
+ * @param cls the `struct CadetTunnel`
+ * @param error error code
+ */
+static void
+decrypted_error_cb (void *cls,
+ enum GNUNET_MQ_Error error)
+{
+ GNUNET_break_op (0);
+}
+
+
+/**
+ * Create a tunnel to @a destionation. Must only be called
+ * from within #GCP_get_tunnel().
+ *
+ * @param destination where to create the tunnel to
+ * @return new tunnel to @a destination
+ */
+struct CadetTunnel *
+GCT_create_tunnel (struct CadetPeer *destination)
+{
+ struct CadetTunnel *t = GNUNET_new (struct CadetTunnel);
+ struct GNUNET_MQ_MessageHandler handlers[] = {
+ GNUNET_MQ_hd_fixed_size (plaintext_keepalive,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE,
+ struct GNUNET_MessageHeader,
+ t),
+ GNUNET_MQ_hd_var_size (plaintext_data,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA,
+ struct GNUNET_CADET_ChannelAppDataMessage,
+ t),
+ GNUNET_MQ_hd_fixed_size (plaintext_data_ack,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK,
+ struct GNUNET_CADET_ChannelDataAckMessage,
+ t),
+ GNUNET_MQ_hd_fixed_size (plaintext_channel_open,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN,
+ struct GNUNET_CADET_ChannelOpenMessage,
+ t),
+ GNUNET_MQ_hd_fixed_size (plaintext_channel_open_ack,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK,
+ struct GNUNET_CADET_ChannelManageMessage,
+ t),
+ GNUNET_MQ_hd_fixed_size (plaintext_channel_destroy,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY,
+ struct GNUNET_CADET_ChannelManageMessage,
+ t),
+ GNUNET_MQ_handler_end ()
+ };
+
+ t->kx_retry_delay = INITIAL_KX_RETRY_DELAY;
+ new_ephemeral (&t->ax);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_ecdhe_key_create2 (&t->ax.kx_0));
+ t->destination = destination;
+ t->channels = GNUNET_CONTAINER_multihashmap32_create (8);
+ t->maintain_connections_task
+ = GNUNET_SCHEDULER_add_now (&maintain_connections_cb,
+ t);
+ t->mq = GNUNET_MQ_queue_for_callbacks (NULL,
+ NULL,
+ NULL,
+ NULL,
+ handlers,
+ &decrypted_error_cb,
+ t);
+ t->mst = GNUNET_MST_create (&handle_decrypted,
+ t);
+ return t;
+}
+
+
+/**
+ * Add a @a connection to the @a tunnel.
+ *
+ * @param t a tunnel
+ * @param cid connection identifer to use for the connection
+ * @param options options for the connection
+ * @param path path to use for the connection
+ * @return #GNUNET_OK on success,
+ * #GNUNET_SYSERR on failure (duplicate connection)
+ */
+int
+GCT_add_inbound_connection (struct CadetTunnel *t,
+ const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
+ enum GNUNET_CADET_ChannelOption options,
+ struct CadetPeerPath *path)
+{
+ struct CadetTConnection *ct;
+
+ ct = GNUNET_new (struct CadetTConnection);
+ ct->created = GNUNET_TIME_absolute_get ();
+ ct->t = t;
+ ct->cc = GCC_create_inbound (t->destination,
+ path,
+ options,
+ ct,
+ cid,
+ &connection_ready_cb,
+ ct);
+ if (NULL == ct->cc)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "%s refused inbound %s (duplicate)\n",
+ GCT_2s (t),
+ GCC_2s (ct->cc));
+ GNUNET_free (ct);
+ return GNUNET_SYSERR;
+ }
+ /* FIXME: schedule job to kill connection (and path?) if it takes
+ too long to get ready! (And track performance data on how long
+ other connections took with the tunnel!)
+ => Note: to be done within 'connection'-logic! */
+ GNUNET_CONTAINER_DLL_insert (t->connection_busy_head,
+ t->connection_busy_tail,
+ ct);
+ t->num_busy_connections++;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "%s has new %s\n",
+ GCT_2s (t),
+ GCC_2s (ct->cc));
+ return GNUNET_OK;
+}
+
+
+/**
+ * Handle encrypted message.
+ *
+ * @param ct connection/tunnel combo that received encrypted message
+ * @param msg the encrypted message to decrypt
+ */
+void
+GCT_handle_encrypted (struct CadetTConnection *ct,
+ const struct GNUNET_CADET_TunnelEncryptedMessage *msg)
+{
+ struct CadetTunnel *t = ct->t;
+ uint16_t size = ntohs (msg->header.size);
+ char cbuf [size] GNUNET_ALIGN;
+ ssize_t decrypted_size;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "%s received %u bytes of encrypted data in state %d\n",
+ GCT_2s (t),
+ (unsigned int) size,
+ t->estate);
+
+ switch (t->estate)
+ {
+ case CADET_TUNNEL_KEY_UNINITIALIZED:
+ case CADET_TUNNEL_KEY_AX_RECV:
+ /* We did not even SEND our KX, how can the other peer
+ send us encrypted data? Must have been that we went
+ down and the other peer still things we are up.
+ Let's send it KX back. */
+ GNUNET_STATISTICS_update (stats,
+ "# received encrypted without any KX",
+ 1,
+ GNUNET_NO);
+ if (NULL != t->kx_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = NULL;
+ }
+ send_kx (t,
+ ct,
+ &t->ax);
+ return;
+ case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
+ /* We send KX, and other peer send KX to us at the same time.
+ Neither KX is AUTH'ed, so let's try KX_AUTH this time. */
+ GNUNET_STATISTICS_update (stats,
+ "# received encrypted without KX_AUTH",
+ 1,
+ GNUNET_NO);
+ if (NULL != t->kx_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = NULL;
+ }
+ send_kx_auth (t,
+ ct,
+ &t->ax,
+ GNUNET_YES);
+ return;
+ case CADET_TUNNEL_KEY_AX_SENT:
+ /* We did not get the KX of the other peer, but that
+ might have been lost. Send our KX again immediately. */
+ GNUNET_STATISTICS_update (stats,
+ "# received encrypted without KX",
+ 1,
+ GNUNET_NO);
+ if (NULL != t->kx_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = NULL;
+ }
+ send_kx (t,
+ ct,
+ &t->ax);
+ return;
+ case CADET_TUNNEL_KEY_AX_AUTH_SENT:
+ /* Great, first payload, we might graduate to OK! */
+ case CADET_TUNNEL_KEY_OK:
+ /* We are up and running, all good. */
+ break;
+ }
+
+ decrypted_size = -1;
+ if (CADET_TUNNEL_KEY_OK == t->estate)
+ {
+ /* We have well-established key material available,
+ try that. (This is the common case.) */
+ decrypted_size = t_ax_decrypt_and_validate (&t->ax,
+ cbuf,
+ msg,
+ size);
+ }
+
+ if ( (-1 == decrypted_size) &&
+ (NULL != t->unverified_ax) )
+ {
+ /* We have un-authenticated KX material available. We should try
+ this as a back-up option, in case the sender crashed and
+ switched keys. */
+ decrypted_size = t_ax_decrypt_and_validate (t->unverified_ax,
+ cbuf,
+ msg,
+ size);
+ if (-1 != decrypted_size)
+ {
+ /* It worked! Treat this as authentication of the AX data! */
+ cleanup_ax (&t->ax);
+ t->ax = *t->unverified_ax;
+ GNUNET_free (t->unverified_ax);
+ t->unverified_ax = NULL;
+ }
+ if (CADET_TUNNEL_KEY_AX_AUTH_SENT == t->estate)
+ {
+ /* First time it worked, move tunnel into production! */
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_OK);
+ if (NULL != t->send_task)
+ GNUNET_SCHEDULER_cancel (t->send_task);
+ t->send_task = GNUNET_SCHEDULER_add_now (&trigger_transmissions,
+ t);
+ }
+ }
+ if (NULL != t->unverified_ax)
+ {
+ /* We had unverified KX material that was useless; so increment
+ counter and eventually move to ignore it. Note that we even do
+ this increment if we successfully decrypted with the old KX
+ material and thus didn't even both with the new one. This is
+ the ideal case, as a malicious injection of bogus KX data
+ basically only causes us to increment a counter a few times. */
+ t->unverified_attempts++;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Failed to decrypt message with unverified KX data %u times\n",
+ t->unverified_attempts);
+ if (t->unverified_attempts > MAX_UNVERIFIED_ATTEMPTS)
+ {
+ cleanup_ax (t->unverified_ax);
+ GNUNET_free (t->unverified_ax);
+ t->unverified_ax = NULL;
+ }
+ }
+
+ if (-1 == decrypted_size)
+ {
+ /* Decryption failed for good, complain. */
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ "%s failed to decrypt and validate encrypted data, retrying KX\n",
+ GCT_2s (t));
+ GNUNET_STATISTICS_update (stats,
+ "# unable to decrypt",
+ 1,
+ GNUNET_NO);
+ if (NULL != t->kx_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = NULL;
+ }
+ send_kx (t,
+ ct,
+ &t->ax);
+ return;
+ }
+ GNUNET_STATISTICS_update (stats,
+ "# decrypted bytes",
+ decrypted_size,
+ GNUNET_NO);
+
+ /* The MST will ultimately call #handle_decrypted() on each message. */
+ t->current_ct = ct;
+ GNUNET_break_op (GNUNET_OK ==
+ GNUNET_MST_from_buffer (t->mst,
+ cbuf,
+ decrypted_size,
+ GNUNET_YES,
+ GNUNET_NO));
+ t->current_ct = NULL;
+}
+
+
+/**
+ * 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 cont Continuation to call once message is really sent.
+ * @param cont_cls Closure for @c cont.
+ * @return Handle to cancel message
+ */
+struct CadetTunnelQueueEntry *
+GCT_send (struct CadetTunnel *t,
+ const struct GNUNET_MessageHeader *message,
+ GCT_SendContinuation cont,
+ void *cont_cls)
+{
+ struct CadetTunnelQueueEntry *tq;
+ uint16_t payload_size;
+ struct GNUNET_MQ_Envelope *env;
+ struct GNUNET_CADET_TunnelEncryptedMessage *ax_msg;
+
+ if (CADET_TUNNEL_KEY_OK != t->estate)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ payload_size = ntohs (message->size);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Encrypting %u bytes for %s\n",
+ (unsigned int) payload_size,
+ GCT_2s (t));
+ env = GNUNET_MQ_msg_extra (ax_msg,
+ payload_size,
+ GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED);
+ t_ax_encrypt (&t->ax,
+ &ax_msg[1],
+ message,
+ payload_size);
+ GNUNET_STATISTICS_update (stats,
+ "# encrypted bytes",
+ payload_size,
+ GNUNET_NO);
+ ax_msg->ax_header.Ns = htonl (t->ax.Ns++);
+ ax_msg->ax_header.PNs = htonl (t->ax.PNs);
+ /* FIXME: we should do this once, not once per message;
+ this is a point multiplication, and DHRs does not
+ change all the time. */
+ GNUNET_CRYPTO_ecdhe_key_get_public (&t->ax.DHRs,
+ &ax_msg->ax_header.DHRs);
+ t_h_encrypt (&t->ax,
+ ax_msg);
+ t_hmac (&ax_msg->ax_header,
+ sizeof (struct GNUNET_CADET_AxHeader) + payload_size,
+ 0,
+ &t->ax.HKs,
+ &ax_msg->hmac);
+
+ tq = GNUNET_malloc (sizeof (*tq));
+ tq->t = t;
+ tq->env = env;
+ tq->cid = &ax_msg->cid; /* will initialize 'ax_msg->cid' once we know the connection */
+ tq->cont = cont;
+ tq->cont_cls = cont_cls;
+ GNUNET_CONTAINER_DLL_insert_tail (t->tq_head,
+ t->tq_tail,
+ tq);
+ if (NULL != t->send_task)
+ GNUNET_SCHEDULER_cancel (t->send_task);
+ t->send_task
+ = GNUNET_SCHEDULER_add_now (&trigger_transmissions,
+ t);
+ return tq;
+}
+
+
+/**
+ * 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 tq Handle to the queue entry to cancel.
+ */
+void
+GCT_send_cancel (struct CadetTunnelQueueEntry *tq)
+{
+ struct CadetTunnel *t = tq->t;
+
+ GNUNET_CONTAINER_DLL_remove (t->tq_head,
+ t->tq_tail,
+ tq);
+ GNUNET_MQ_discard (tq->env);
+ GNUNET_free (tq);
+}
+
+
+/**
+ * Iterate over all connections of a tunnel.
+ *
+ * @param t Tunnel whose connections to iterate.
+ * @param iter Iterator.
+ * @param iter_cls Closure for @c iter.
+ */
+void
+GCT_iterate_connections (struct CadetTunnel *t,
+ GCT_ConnectionIterator iter,
+ void *iter_cls)
+{
+ struct CadetTConnection *n;
+ for (struct CadetTConnection *ct = t->connection_ready_head;
+ NULL != ct;
+ ct = n)
+ {
+ n = ct->next;
+ iter (iter_cls,
+ ct);
+ }
+ for (struct CadetTConnection *ct = t->connection_busy_head;
+ NULL != ct;
+ ct = n)
+ {
+ n = ct->next;
+ iter (iter_cls,
+ ct);
+ }
+}
+
+
+/**
+ * Closure for #iterate_channels_cb.
+ */
+struct ChanIterCls
+{
+ /**
+ * Function to call.
+ */
+ GCT_ChannelIterator iter;
+
+ /**
+ * Closure for @e iter.
+ */
+ void *iter_cls;
+};
+
+
+/**
+ * Helper function for #GCT_iterate_channels.
+ *
+ * @param cls the `struct ChanIterCls`
+ * @param key unused
+ * @param value a `struct CadetChannel`
+ * @return #GNUNET_OK
+ */
+static int
+iterate_channels_cb (void *cls,
+ uint32_t key,
+ void *value)
+{
+ struct ChanIterCls *ctx = cls;
+ struct CadetChannel *ch = value;
+
+ ctx->iter (ctx->iter_cls,
+ ch);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Iterate over all channels of a tunnel.
+ *
+ * @param t Tunnel whose channels to iterate.
+ * @param iter Iterator.
+ * @param iter_cls Closure for @c iter.
+ */
+void
+GCT_iterate_channels (struct CadetTunnel *t,
+ GCT_ChannelIterator iter,
+ void *iter_cls)
+{
+ struct ChanIterCls ctx;
+
+ ctx.iter = iter;
+ ctx.iter_cls = iter_cls;
+ GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
+ &iterate_channels_cb,
+ &ctx);
+
+}
+
+
+/**
+ * Call #GCCH_debug() on a channel.
+ *
+ * @param cls points to the log level to use
+ * @param key unused
+ * @param value the `struct CadetChannel` to dump
+ * @return #GNUNET_OK (continue iteration)
+ */
+static int
+debug_channel (void *cls,
+ uint32_t key,
+ void *value)
+{
+ const enum GNUNET_ErrorType *level = cls;
+ struct CadetChannel *ch = value;
+
+ GCCH_debug (ch, *level);
+ return GNUNET_OK;
+}
+
+
+#define LOG2(level, ...) GNUNET_log_from_nocheck(level,"cadet-tun",__VA_ARGS__)
+
+
+/**
+ * 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 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 TUNNEL TOWARDS %s in estate %s tq_len: %u #cons: %u\n",
+ GCT_2s (t),
+ estate2s (t->estate),
+ t->tq_len,
+ GCT_count_any_connections (t));
+ LOG2 (level,
+ "TTT channels:\n");
+ GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
+ &debug_channel,
+ &level);
+ LOG2 (level,
+ "TTT connections:\n");
+ for (iter_c = t->connection_ready_head; NULL != iter_c; iter_c = iter_c->next)
+ GCC_debug (iter_c->cc,
+ level);
+ for (iter_c = t->connection_busy_head; NULL != iter_c; iter_c = iter_c->next)
+ GCC_debug (iter_c->cc,
+ level);
+
+ LOG2 (level,
+ "TTT TUNNEL END\n");
+}
+
+
+/* end of gnunet-service-cadet-new_tunnels.c */
--- /dev/null
+
+/*
+ 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_tunnels.h
+ * @brief Information we track per tunnel.
+ * @author Bartlomiej Polot
+ * @author Christian Grothoff
+ */
+#ifndef GNUNET_SERVICE_CADET_TUNNELS_H
+#define GNUNET_SERVICE_CADET_TUNNELS_H
+
+#include "gnunet-service-cadet.h"
+#include "cadet_protocol.h"
+
+
+/**
+ * How many connections would we like to have per tunnel?
+ */
+#define DESIRED_CONNECTIONS_PER_TUNNEL 3
+
+
+/**
+ * All the encryption states a tunnel can be in.
+ */
+enum CadetTunnelEState
+{
+ /**
+ * Uninitialized status, we need to send KX. We will stay
+ * in this state until the first connection is up.
+ */
+ CADET_TUNNEL_KEY_UNINITIALIZED,
+
+ /**
+ * KX message sent, waiting for other peer's KX_AUTH.
+ */
+ CADET_TUNNEL_KEY_AX_SENT,
+
+ /**
+ * KX message received, trying to send back KX_AUTH.
+ */
+ CADET_TUNNEL_KEY_AX_RECV,
+
+ /**
+ * KX message sent and received, trying to send back KX_AUTH.
+ */
+ CADET_TUNNEL_KEY_AX_SENT_AND_RECV,
+
+ /**
+ * KX received and we sent KX_AUTH back, but we got no traffic yet,
+ * so we're waiting for either KX_AUTH or ENCRYPED traffic from
+ * the other peer.
+ *
+ * We will not yet send traffic, as this might have been a replay.
+ * The other (initiating) peer should send a CHANNEL_OPEN next
+ * anyway, and then we are in business!
+ */
+ CADET_TUNNEL_KEY_AX_AUTH_SENT,
+
+ /**
+ * Handshake completed: session key available.
+ */
+ CADET_TUNNEL_KEY_OK
+
+};
+
+
+/**
+ * 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);
+
+
+/**
+ * Create a tunnel to @a destionation. Must only be called
+ * from within #GCP_get_tunnel().
+ *
+ * @param destination where to create the tunnel to
+ * @return new tunnel to @a destination
+ */
+struct CadetTunnel *
+GCT_create_tunnel (struct CadetPeer *destination);
+
+
+/**
+ * Destroys the tunnel @a t now, without delay. Used during shutdown.
+ *
+ * @param t tunnel to destroy
+ */
+void
+GCT_destroy_tunnel_now (struct CadetTunnel *t);
+
+
+/**
+ * Add a @a connection to the @a tunnel.
+ *
+ * @param t a tunnel
+ * @param cid connection identifer to use for the connection
+ * @param options options for the connection
+ * @param path path to use for the connection
+ * @return #GNUNET_OK on success,
+ * #GNUNET_SYSERR on failure (duplicate connection)
+ */
+int
+GCT_add_inbound_connection (struct CadetTunnel *t,
+ const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
+ enum GNUNET_CADET_ChannelOption options,
+ struct CadetPeerPath *path);
+
+
+/**
+ * We lost a connection, remove it from our list and clean up
+ * the connection object itself.
+ *
+ * @param ct binding of connection to tunnel of the connection that was lost.
+ */
+void
+GCT_connection_lost (struct CadetTConnection *ct);
+
+
+/**
+ * Return the peer to which this tunnel goes.
+ *
+ * @param t a tunnel
+ * @return the destination of the tunnel
+ */
+struct CadetPeer *
+GCT_get_destination (struct CadetTunnel *t);
+
+
+/**
+ * Consider using the path @a p for the tunnel @a t.
+ * The tunnel destination is at offset @a off in path @a p.
+ *
+ * @param cls our tunnel
+ * @param path a path to our destination
+ * @param off offset of the destination on path @a path
+ */
+void
+GCT_consider_path (struct CadetTunnel *t,
+ struct CadetPeerPath *p,
+ unsigned int off);
+
+
+/**
+ * Add a channel to a tunnel.
+ *
+ * @param t Tunnel.
+ * @param ch Channel
+ * @return unique number identifying @a ch within @a t
+ */
+struct GNUNET_CADET_ChannelTunnelNumber
+GCT_add_channel (struct CadetTunnel *t,
+ struct CadetChannel *ch);
+
+
+/**
+ * Remove a channel from a tunnel.
+ *
+ * @param t Tunnel.
+ * @param ch Channel
+ * @param ctn unique number identifying @a ch within @a t
+ */
+void
+GCT_remove_channel (struct CadetTunnel *t,
+ struct CadetChannel *ch,
+ struct GNUNET_CADET_ChannelTunnelNumber ctn);
+
+
+/**
+ * Send a DESTROY message via the tunnel.
+ *
+ * @param t the tunnel to transmit over
+ * @param ctn ID of the channel to destroy
+ */
+void
+GCT_send_channel_destroy (struct CadetTunnel *t,
+ struct GNUNET_CADET_ChannelTunnelNumber ctn);
+
+
+/**
+ * Function called when a transmission requested using #GCT_send is done.
+ *
+ * @param cls closure
+ * @param ctn identifier of the connection used for transmission, NULL if
+ * the transmission failed (to be used to match ACKs to the
+ * respective connection for connection performance evaluation)
+ */
+typedef void
+(*GCT_SendContinuation)(void *cls,
+ const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid);
+
+
+/**
+ * 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 cont Continuation to call once message is really sent.
+ * @param cont_cls Closure for @c cont.
+ * @return Handle to cancel message.
+ */
+struct CadetTunnelQueueEntry *
+GCT_send (struct CadetTunnel *t,
+ const struct GNUNET_MessageHeader *message,
+ GCT_SendContinuation cont,
+ void *cont_cls);
+
+
+/**
+ * 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 entry to cancel.
+ */
+void
+GCT_send_cancel (struct CadetTunnelQueueEntry *q);
+
+
+/**
+ * Return the number of channels using a tunnel.
+ *
+ * @param t tunnel to count obtain the number of channels for
+ * @return number of channels using the tunnel
+ */
+unsigned int
+GCT_count_channels (struct CadetTunnel *t);
+
+
+/**
+ * Return the number of connections available for a tunnel.
+ *
+ * @param t tunnel to count obtain the number of connections for
+ * @return number of connections available for the tunnel
+ */
+unsigned int
+GCT_count_any_connections (const struct CadetTunnel *t);
+
+
+/**
+ * Iterator over connections.
+ *
+ * @param cls closure
+ * @param ct one of the connections
+ */
+typedef void
+(*GCT_ConnectionIterator) (void *cls,
+ struct CadetTConnection *ct);
+
+
+/**
+ * Iterate over all connections of a tunnel.
+ *
+ * @param t Tunnel whose connections to iterate.
+ * @param iter Iterator.
+ * @param iter_cls Closure for @c iter.
+ */
+void
+GCT_iterate_connections (struct CadetTunnel *t,
+ GCT_ConnectionIterator iter,
+ void *iter_cls);
+
+
+/**
+ * Iterator over channels.
+ *
+ * @param cls closure
+ * @param ch one of the channels
+ */
+typedef void
+(*GCT_ChannelIterator) (void *cls,
+ struct CadetChannel *ch);
+
+
+/**
+ * Iterate over all channels of a tunnel.
+ *
+ * @param t Tunnel whose channels to iterate.
+ * @param iter Iterator.
+ * @param iter_cls Closure for @c iter.
+ */
+void
+GCT_iterate_channels (struct CadetTunnel *t,
+ GCT_ChannelIterator iter,
+ void *iter_cls);
+
+
+/**
+ * Get the encryption state of a tunnel.
+ *
+ * @param t Tunnel.
+ *
+ * @return Tunnel's encryption state.
+ */
+enum CadetTunnelEState
+GCT_get_estate (struct CadetTunnel *t);
+
+
+/**
+ * Handle KX message.
+ *
+ * @param ct connection/tunnel combo that received encrypted message
+ * @param msg the key exchange message
+ */
+void
+GCT_handle_kx (struct CadetTConnection *ct,
+ const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg);
+
+
+/**
+ * Handle KX_AUTH message.
+ *
+ * @param ct connection/tunnel combo that received encrypted message
+ * @param msg the key exchange message
+ */
+void
+GCT_handle_kx_auth (struct CadetTConnection *ct,
+ const struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg);
+
+
+/**
+ * Handle encrypted message.
+ *
+ * @param ct connection/tunnel combo that received encrypted message
+ * @param msg the encrypted message to decrypt
+ */
+void
+GCT_handle_encrypted (struct CadetTConnection *ct,
+ const struct GNUNET_CADET_TunnelEncryptedMessage *msg);
+
+
+/**
+ * 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);
+
+
+#endif
--- /dev/null
+/*
+ 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.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.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 */
+++ /dev/null
-/*
- 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 */