From: Bart Polot Date: Mon, 10 Jun 2013 14:38:05 +0000 (+0000) Subject: - add framework for mesh2 mutipeer tests X-Git-Tag: initial-import-from-subversion-38251~8807 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=7f61d08dcf2e6e895bf01a8c647372c4ef6d04fc;p=oweals%2Fgnunet.git - add framework for mesh2 mutipeer tests --- diff --git a/src/mesh/Makefile.am b/src/mesh/Makefile.am index 135f2bce3..5b59a4652 100644 --- a/src/mesh/Makefile.am +++ b/src/mesh/Makefile.am @@ -21,13 +21,12 @@ plugindir = $(libdir)/gnunet AM_CLFAGS = -g if HAVE_EXPERIMENTAL + noinst_LIB_EXP = libgnunetmesh2test.a EXP_LIB = libgnunetmesh2.la EXP_LIBEXEC = gnunet-service-mesh-new EXP_TESTS = \ - test_mesh2_local - MESH_DEP = $(top_builddir)/src/mesh/libgnunetmesh2.la -else - MESH_DEP = $(top_builddir)/src/mesh/libgnunetmesh.la + test_mesh2_local \ + test_mesh2_small_forward endif libexec_PROGRAMS = \ @@ -127,7 +126,7 @@ gnunet_service_mesh_new_LDFLAGS = -lrt endif -noinst_LIBRARIES = libgnunetmeshtest.a +noinst_LIBRARIES = libgnunetmeshtest.a $(noinst_LIB_EXP) libgnunetmeshtest_a_SOURCES = \ mesh_test_lib.c mesh_test_lib.h @@ -138,6 +137,15 @@ libgnunetmeshtest_a_LIBADD = \ libgnunetmeshtest_a_DEPENDENCIES = \ libgnunetmesh.la +libgnunetmesh2test_a_SOURCES = \ + mesh2_test_lib.c mesh2_test_lib.h +libgnunetmesh2test_a_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/mesh/libgnunetmesh2.la +libgnunetmesh2test_a_DEPENDENCIES = \ + libgnunetmesh2.la + check_PROGRAMS = \ test_mesh_api \ @@ -162,7 +170,7 @@ test_mesh_api_SOURCES = \ test_mesh_api_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(top_builddir)/src/testing/libgnunettesting.la \ - $(MESH_DEP) + $(top_builddir)/src/mesh/libgnunetmesh.la test_mesh_api_DEPENDENCIES = \ libgnunetmesh.la \ $(top_builddir)/src/util/libgnunetutil.la @@ -273,15 +281,27 @@ test_mesh_small_speed_nobuf_backwards_LDADD = $(ld_mesh_test_lib) test_mesh_small_speed_nobuf_backwards_DEPENDENCIES = $(dep_mesh_test_lib) -test_mesh2_local_SOURCES = \ - test_mesh2_local.c -test_mesh2_local_LDADD = \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/testing/libgnunettesting.la \ - $(MESH_DEP) -test_mesh2_local_DEPENDENCIES = \ +ld_mesh2_test_lib = \ + $(top_builddir)/src/mesh/libgnunetmesh2test.a \ + $(top_builddir)/src/mesh/libgnunetmesh2.la \ + $(top_builddir)/src/testbed/libgnunettestbed.la \ + $(top_builddir)/src/util/libgnunetutil.la + +dep_mesh2_test_lib = \ + libgnunetmesh2test.a \ libgnunetmesh2.la +test_mesh2_local_SOURCES = \ + test_mesh2_local.c +test_mesh2_local_LDADD = $(ld_mesh2_test_lib) +test_mesh2_local_DEPENDENCIES = $(dep_mesh2_test_lib) + +test_mesh2_small_forward_SOURCES = \ + test_mesh2_small.c +test_mesh2_small_forward_LDADD = $(ld_mesh2_test_lib) +test_mesh2_small_forward_DEPENDENCIES = $(dep_mesh2_test_lib) + + if ENABLE_TEST_RUN TESTS = \ $(EXP_TESTS) \ diff --git a/src/mesh/mesh2_test_lib.c b/src/mesh/mesh2_test_lib.c new file mode 100644 index 000000000..828c09460 --- /dev/null +++ b/src/mesh/mesh2_test_lib.c @@ -0,0 +1,303 @@ +/* + This file is part of GNUnet. + (C) 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file mesh/mesh2_test_lib.c + * @author Bartlomiej Polot + * @brief library for writing MESH tests + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "mesh_test_lib.h" +#include "gnunet_mesh2_service.h" + +/** + * Test context for a MESH Test. + */ +struct GNUNET_MESH_TEST_Context +{ + /** + * Array of running peers. + */ + struct GNUNET_TESTBED_Peer **peers; + + /** + * Array of handles to the MESH for each peer. + */ + struct GNUNET_MESH_Handle **meshes; + + /** + * Operation associated with the connection to the MESH. + */ + struct GNUNET_TESTBED_Operation **ops; + + /** + * Main function of the test to run once all MESHs are available. + */ + GNUNET_MESH_TEST_AppMain app_main; + + /** + * Closure for 'app_main'. + */ + void *app_main_cls; + + /** + * Number of peers running, size of the arrays above. + */ + unsigned int num_peers; + + /** + * Handler for incoming tunnels. + */ + GNUNET_MESH_InboundTunnelNotificationHandler *new_tunnel; + + /** + * Cleaner for destroyed incoming tunnels. + */ + GNUNET_MESH_TunnelEndHandler *cleaner; + + /** + * Message handlers. + */ + struct GNUNET_MESH_MessageHandler* handlers; + + /** + * Application types. + */ + const GNUNET_MESH_ApplicationType* stypes; + +}; + + +/** + * Context for a mesh adapter callback. + */ +struct GNUNET_MESH_TEST_AdapterContext +{ + /** + * Peer number for the particular peer. + */ + unsigned int peer; + + /** + * General context. + */ + struct GNUNET_MESH_TEST_Context *ctx; +}; + + +/** + * Adapter function called to establish a connection to + * the MESH service. + * + * @param cls closure + * @param cfg configuration of the peer to connect to; will be available until + * GNUNET_TESTBED_operation_done() is called on the operation returned + * from GNUNET_TESTBED_service_connect() + * @return service handle to return in 'op_result', NULL on error + */ +static void * +mesh_connect_adapter (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_MESH_TEST_AdapterContext *actx = cls; + struct GNUNET_MESH_TEST_Context *ctx = actx->ctx; + struct GNUNET_MESH_Handle *h; + + h = GNUNET_MESH_connect (cfg, + (void *) (long) actx->peer, + ctx->new_tunnel, + ctx->cleaner, + ctx->handlers, + ctx->stypes); + return h; +} + + +/** + * Adapter function called to destroy a connection to + * the MESH service. + * + * @param cls closure + * @param op_result service handle returned from the connect adapter + */ +static void +mesh_disconnect_adapter (void *cls, + void *op_result) +{ + struct GNUNET_MESH_Handle *mesh = op_result; + struct GNUNET_MESH_TEST_AdapterContext *actx = cls; + + GNUNET_free (actx); + GNUNET_MESH_disconnect (mesh); +} + + +/** + * Callback to be called when a service connect operation is completed. + * + * @param cls The callback closure from functions generating an operation. + * @param op The operation that has been finished. + * @param ca_result The service handle returned from + * GNUNET_TESTBED_ConnectAdapter() (mesh handle). + * @param emsg Error message in case the operation has failed. + * NULL if operation has executed successfully. + */ +static void +mesh_connect_cb (void *cls, + struct GNUNET_TESTBED_Operation *op, + void *ca_result, + const char *emsg) +{ + struct GNUNET_MESH_TEST_Context *ctx = cls; + unsigned int i; + + if (NULL != emsg) + { + fprintf (stderr, "Failed to connect to MESH service: %s\n", + emsg); + GNUNET_SCHEDULER_shutdown (); + return; + } + for (i = 0; i < ctx->num_peers; i++) + if (op == ctx->ops[i]) + ctx->meshes[i] = ca_result; + for (i = 0; i < ctx->num_peers; i++) + if (NULL == ctx->meshes[i]) + return; /* still some MESH connections missing */ + /* all MESH connections ready! */ + ctx->app_main (ctx->app_main_cls, + ctx, + ctx->num_peers, + ctx->peers, + ctx->meshes); +} + + +/** + * Clean up the testbed. + * + * @param ctx handle for the testbed + */ +void +GNUNET_MESH_TEST_cleanup (struct GNUNET_MESH_TEST_Context *ctx) +{ + unsigned int i; + + for (i = 0; i < ctx->num_peers; i++) + { + GNUNET_assert (NULL != ctx->ops[i]); + GNUNET_TESTBED_operation_done (ctx->ops[i]); + ctx->ops[i] = NULL; + } + GNUNET_free (ctx->ops); + GNUNET_free (ctx->meshes); + GNUNET_free (ctx); + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Callback run when the testbed is ready (peers running and connected to + * each other) + * + * @param cls Closure (context). + * @param num_peers Number of peers that are running. + * @param peers Handles to each one of the @c num_peers peers. + * @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 +mesh_test_run (void *cls, + unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers, + unsigned int links_succeeded, + unsigned int links_failed) +{ + struct GNUNET_MESH_TEST_Context *ctx = cls; + unsigned int i; + + GNUNET_assert (num_peers == ctx->num_peers); + ctx->peers = peers; + for (i = 0; i < num_peers; i++) + { + struct GNUNET_MESH_TEST_AdapterContext *newctx; + newctx = GNUNET_malloc (sizeof (struct GNUNET_MESH_TEST_AdapterContext)); + newctx->peer = i; + newctx->ctx = ctx; + ctx->ops[i] = GNUNET_TESTBED_service_connect (ctx, + peers[i], + "mesh", + &mesh_connect_cb, + ctx, + &mesh_connect_adapter, + &mesh_disconnect_adapter, + newctx); + } +} + + +/** + * Run a test using the given name, configuration file and number of + * peers. + * All mesh callbacks will receive the peer number as the closure. + * + * @param testname Name of the test (for logging). + * @param cfgname Name of the configuration file. + * @param num_peers Number of peers to start. + * @param tmain Main function to run once the testbed is ready. + * @param tmain_cls Closure for 'tmain'. + * @param new_tunnel Handler for incoming tunnels. + * @param cleaner Cleaner for destroyed incoming tunnels. + * @param handlers Message handlers. + * @param stypes Application types. + */ +void +GNUNET_MESH_TEST_run (const char *testname, + const char *cfgname, + unsigned int num_peers, + GNUNET_MESH_TEST_AppMain tmain, + void *tmain_cls, + GNUNET_MESH_InboundTunnelNotificationHandler new_tunnel, + GNUNET_MESH_TunnelEndHandler cleaner, + struct GNUNET_MESH_MessageHandler* handlers, + const GNUNET_MESH_ApplicationType* stypes) +{ + struct GNUNET_MESH_TEST_Context *ctx; + + ctx = GNUNET_malloc (sizeof (struct GNUNET_MESH_TEST_Context)); + ctx->num_peers = num_peers; + ctx->ops = GNUNET_malloc (num_peers * sizeof (struct GNUNET_TESTBED_Operation *)); + ctx->meshes = GNUNET_malloc (num_peers * sizeof (struct GNUNET_MESH_Handle *)); + ctx->app_main = tmain; + ctx->app_main_cls = tmain_cls; + ctx->new_tunnel = new_tunnel; + ctx->cleaner = cleaner; + ctx->handlers = handlers; + ctx->stypes = stypes; + GNUNET_TESTBED_test_run (testname, + cfgname, + num_peers, + 0LL, NULL, NULL, + &mesh_test_run, ctx); +} + +/* end of mesh_test_lib.c */ diff --git a/src/mesh/mesh2_test_lib.h b/src/mesh/mesh2_test_lib.h new file mode 100644 index 000000000..f1822617b --- /dev/null +++ b/src/mesh/mesh2_test_lib.h @@ -0,0 +1,106 @@ +/* + This file is part of GNUnet. + (C) 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file mesh/mesh2_test_lib.h + * @author Bartlomiej Polot + * @brief library for writing MESH tests + */ +#ifndef MESH_TEST_LIB_H +#define MESH_TEST_LIB_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_testbed_service.h" +#include "gnunet_mesh2_service.h" + +/** + * Test context for a MESH Test. + */ +struct GNUNET_MESH_TEST_Context; + + +/** + * Main function of a MESH test. + * + * @param cls Closure. + * @param ctx Argument to give to GNUNET_MESH_TEST_cleanup on test end. + * @param num_peers Number of peers that are running. + * @param peers Array of peers. + * @param meshes Handle to each of the MESHs of the peers. + */ +typedef void (*GNUNET_MESH_TEST_AppMain) (void *cls, + struct GNUNET_MESH_TEST_Context *ctx, + unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers, + struct GNUNET_MESH_Handle **meshes); + + +/** + * Run a test using the given name, configuration file and number of + * peers. + * All mesh callbacks will receive the peer number as the closure. + * + * @param testname Name of the test (for logging). + * @param cfgname Name of the configuration file. + * @param num_peers Number of peers to start. + * @param tmain Main function to run once the testbed is ready. + * @param tmain_cls Closure for 'tmain'. + * @param new_tunnel Handler for incoming tunnels. + * @param cleaner Cleaner for destroyed incoming tunnels. + * @param handlers Message handlers. + * @param stypes Application types. + */ +void +GNUNET_MESH_TEST_run (const char *testname, + const char *cfgname, + unsigned int num_peers, + GNUNET_MESH_TEST_AppMain tmain, + void *tmain_cls, + GNUNET_MESH_InboundTunnelNotificationHandler new_tunnel, + GNUNET_MESH_TunnelEndHandler cleaner, + struct GNUNET_MESH_MessageHandler* handlers, + const GNUNET_MESH_ApplicationType* stypes); + + +/** + * Clean up the testbed. + * + * @param ctx handle for the testbed + */ +void +GNUNET_MESH_TEST_cleanup (struct GNUNET_MESH_TEST_Context *ctx); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + + +/* ifndef MESH_TEST_LIB_H */ +#endif diff --git a/src/mesh/test_mesh2_small.c b/src/mesh/test_mesh2_small.c new file mode 100644 index 000000000..05639f855 --- /dev/null +++ b/src/mesh/test_mesh2_small.c @@ -0,0 +1,964 @@ +/* + This file is part of GNUnet. + (C) 2011 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file mesh/test_mesh2_small.c + * + * @brief Test for the mesh service: retransmission of traffic. + */ +#include +#include "platform.h" +#include "mesh_test_lib.h" +#include "gnunet_mesh2_service.h" +#include + + +/** + * How namy messages to send + */ +#define TOTAL_PACKETS 1000 + +/** + * How long until we give up on connecting the peers? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120) + +/** + * Time to wait 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 UNICAST 1 +#define SPEED 3 +#define SPEED_ACK 4 +#define SPEED_MIN 5 +#define SPEED_NOBUF 6 +#define P2P_SIGNAL 10 + +/** + * Which test are we running? + */ +static int test; + +/** + * String with test name + */ +char *test_name; + +/** + * Flag to send traffic leaf->root in speed tests to test BCK_ACK logic. + */ +static int test_backwards = GNUNET_NO; + +/** + * How many events have happened + */ +static int ok; + + /** + * Each peer is supposed to generate the following callbacks: + * 1 incoming tunnel (@dest) + * 1 connected peer (@orig) + * 1 received data packet (@dest) + * 1 received data packet (@orig) + * 1 received tunnel destroy (@dest) + * _________________________________ + * 5 x ok expected per peer + */ +int ok_goal; + + +/** + * Size of each test packet + */ +size_t size_payload = sizeof (struct GNUNET_MessageHeader) + sizeof (uint32_t); + +/** + * Operation to get peer ids. + */ +struct GNUNET_TESTBED_Operation *t_op[3]; + +/** + * Peer ids. + */ +struct GNUNET_PeerIdentity *p_id[3]; + +/** + * Peer ids counter. + */ +unsigned int p_ids; + +/** + * Is the setup initialized? + */ +static int initialized; + +/** + * Peers that have been connected + */ +static int peers_in_tunnel; + +/** + * Peers that have responded + */ +static int peers_responded; + +/** + * Number of payload packes sent + */ +static int data_sent; + +/** + * Number of payload packets received + */ +static int data_received; + +/** + * Number of payload packed explicitly (app level) acknowledged + */ +static int data_ack; + +/** + * Total number of currently running peers. + */ +static unsigned long long peers_running; + +/** + * Test context (to shut down). + */ +struct GNUNET_MESH_TEST_Context *test_ctx; + +/** + * Task called to disconnect peers. + */ +static GNUNET_SCHEDULER_TaskIdentifier disconnect_task; + +/** + * Task To perform tests + */ +static GNUNET_SCHEDULER_TaskIdentifier test_task; + +/** + * Task called to shutdown test. + */ +static GNUNET_SCHEDULER_TaskIdentifier shutdown_handle; + +/** + * Mesh handle for the root peer + */ +static struct GNUNET_MESH_Handle *h1; + +/** + * Mesh handle for the first leaf peer + */ +static struct GNUNET_MESH_Handle *h2; + +/** + * Mesh handle for the second leaf peer + */ +static struct GNUNET_MESH_Handle *h3; + +/** + * Tunnel handle for the root peer + */ +static struct GNUNET_MESH_Tunnel *t; + +/** + * Tunnel handle for the first leaf peer + */ +static struct GNUNET_MESH_Tunnel *incoming_t; + +/** + * Tunnel handle for the second leaf peer + */ +static struct GNUNET_MESH_Tunnel *incoming_t2; + +/** + * Time we started the data transmission (after tunnel has been established + * and initilized). + */ +static struct GNUNET_TIME_Absolute start_time; + + +/** + * 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 %llu ms\n", + (unsigned long long) total_time.rel_value); + FPRINTF (stderr, "Test bandwidth: %f kb/s\n", + 4 * TOTAL_PACKETS * 1.0 / total_time.rel_value); // 4bytes * ms + FPRINTF (stderr, "Test throughput: %f packets/s\n\n", + TOTAL_PACKETS * 1000.0 / total_time.rel_value); // packets * ms + GAUGER ("MESH", test_name, + TOTAL_PACKETS * 1000.0 / total_time.rel_value, + "packets/s"); +} + + +/** + * Shut down peergroup, clean up. + * + * @param cls Closure (unused). + * @param tc Task Context. + */ +static void +shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending test.\n"); + shutdown_handle = GNUNET_SCHEDULER_NO_TASK; +} + + +/** + * Disconnect from mesh services af all peers, call shutdown. + * + * @param cls Closure (unused). + * @param tc Task Context. + */ +static void +disconnect_mesh_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + long line = (long) cls; + unsigned int i; + + for (i = 0; i < 3; i++) + if (NULL != t_op[i]) + { + GNUNET_TESTBED_operation_done (t_op[i]); + t_op[i] = NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "disconnecting mesh service of peers, called from line %ld\n", + line); + disconnect_task = GNUNET_SCHEDULER_NO_TASK; + if (NULL != t) + { + GNUNET_MESH_tunnel_destroy (t); + t = NULL; + } + if (NULL != incoming_t) + { + GNUNET_MESH_tunnel_destroy (incoming_t); + incoming_t = NULL; + } + if (NULL != incoming_t2) + { + GNUNET_MESH_tunnel_destroy (incoming_t2); + incoming_t2 = NULL; + } + GNUNET_MESH_TEST_cleanup (test_ctx); + if (GNUNET_SCHEDULER_NO_TASK != shutdown_handle) + { + GNUNET_SCHEDULER_cancel (shutdown_handle); + } + shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); +} + + +/** + * Abort test: schedule disconnect and shutdown immediately + * + * @param line Line in the code the abort is requested from (__LINE__). + */ +void +abort_test (long line) +{ + if (disconnect_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (disconnect_task); + } + disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME, + &disconnect_mesh_peers, + (void *) line); +} + +/** + * Transmit ready callback. + * + * @param cls Closure (message type). + * @param size Size of the tranmist buffer. + * @param buf Pointer to the beginning of the buffer. + * + * @return Number of bytes written to buf. + */ +static size_t +tmt_rdy (void *cls, size_t size, void *buf); + + +/** + * Task to schedule a new data transmission. + * + * @param cls Closure (peer #). + * @param tc Task Context. + */ +static void +data_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_MESH_TransmitHandle *th; + struct GNUNET_MESH_Tunnel *tunnel; + struct GNUNET_PeerIdentity *destination; + + if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0) + return; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Data task\n"); + if (GNUNET_YES == test_backwards) + { + tunnel = incoming_t; + destination = p_id[0]; + } + else + { + tunnel = t; + destination = p_id[2]; + } + th = GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO, + GNUNET_TIME_UNIT_FOREVER_REL, + destination, + size_payload, + &tmt_rdy, (void *) 1L); + if (NULL == th) + { + unsigned long i = (unsigned long) cls; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Retransmission\n"); + if (0 == i) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, " in 1 ms\n"); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS, + &data_task, (void *)1UL); + } + else + { + i++; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "in %u ms\n", i); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply( + GNUNET_TIME_UNIT_MILLISECONDS, + i), + &data_task, (void *)i); + } + } +} + + +/** + * Transmit ready callback + * + * @param cls Closure (message type). + * @param size Size of the buffer we have. + * @param buf Buffer to copy data to. + */ +size_t +tmt_rdy (void *cls, size_t size, void *buf) +{ + struct GNUNET_MessageHeader *msg = buf; + uint32_t *data; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " tmt_rdy called\n"); + if (size < size_payload || NULL == buf) + { + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "size %u, buf %p, data_sent %u, data_received %u\n", + size, + buf, + data_sent, + data_received); + return 0; + } + msg->size = htons (size); + msg->type = htons ((long) cls); + data = (uint32_t *) &msg[1]; + *data = htonl (data_sent); + if (SPEED == test && GNUNET_YES == initialized) + { + data_sent++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " Sent packet %d\n", data_sent); + if (data_sent < TOTAL_PACKETS) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + " Scheduling packet %d\n", data_sent + 1); + GNUNET_SCHEDULER_add_now(&data_task, NULL); + } + } + return size_payload; +} + + +/** + * Function is called whenever a message is received. + * + * @param cls closure (set from GNUNET_MESH_connect) + * @param tunnel connection to the other end + * @param tunnel_ctx place to store local state associated with the tunnel + * @param sender who sent the message + * @param message the actual message + * @param atsi performance data for the connection + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +int +data_callback (void *cls, struct GNUNET_MESH_Tunnel *tunnel, void **tunnel_ctx, + const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi) +{ + long client = (long) cls; + long expected_target_client; + uint32_t *data; + + ok++; + + if ((ok % 20) == 0) + { + if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) + { + GNUNET_SCHEDULER_cancel (disconnect_task); + } + disconnect_task = + GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, + (void *) __LINE__); + } + + switch (client) + { + case 0L: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Root client got a message!\n"); + peers_responded++; + break; + case 3L: + case 4L: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Leaf client %li got a message.\n", + client); + client = 4L; + break; + default: + GNUNET_assert (0); + break; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: (%d/%d)\n", ok, ok_goal); + data = (uint32_t *) &message[1]; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, " payload: (%u)\n", ntohl (*data)); + if (SPEED == test && GNUNET_YES == test_backwards) + { + expected_target_client = 0L; + } + else + { + expected_target_client = 4L; + } + + if (GNUNET_NO == initialized) + { + initialized = GNUNET_YES; + start_time = GNUNET_TIME_absolute_get (); + if (SPEED == test) + { + GNUNET_assert (4L == client); + GNUNET_SCHEDULER_add_now (&data_task, NULL); + return GNUNET_OK; + } + } + + if (client == expected_target_client) // Normally 3 or 4 + { + data_received++; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + " received data %u\n", data_received); + if (SPEED != test || (ok_goal - 2) == ok) + { + GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO, + GNUNET_TIME_UNIT_FOREVER_REL, sender, + size_payload, + &tmt_rdy, (void *) 1L); + return GNUNET_OK; + } + else + { + if (data_received < TOTAL_PACKETS) + return GNUNET_OK; + } + } + else // Normally 0 + { + if (test == SPEED_ACK || test == SPEED) + { + data_ack++; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + " received ack %u\n", data_ack); + GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO, + GNUNET_TIME_UNIT_FOREVER_REL, sender, + size_payload, + &tmt_rdy, (void *) 1L); + if (data_ack < TOTAL_PACKETS && SPEED != test) + return GNUNET_OK; + if (ok == 2 && SPEED == test) + return GNUNET_OK; + show_end_data(); + } + if (test == P2P_SIGNAL) + { + GNUNET_MESH_tunnel_destroy (incoming_t); + incoming_t = NULL; + } + else + { + GNUNET_MESH_tunnel_destroy (t); + t = NULL; + } + } + + if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) + { + GNUNET_SCHEDULER_cancel (disconnect_task); + } + disconnect_task = + GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, + (void *) __LINE__); + + return GNUNET_OK; +} + + +/** + * Handlers, for diverse services + */ +static struct GNUNET_MESH_MessageHandler handlers[] = { + {&data_callback, 1, sizeof (struct GNUNET_MessageHeader)}, + {NULL, 0, 0} +}; + + +/** + * Method called whenever another peer has added us to a tunnel + * the other peer initiated. + * + * @param cls closure + * @param tunnel new handle to the tunnel + * @param initiator peer that started the tunnel + * @param atsi performance information for the tunnel + * @return initial tunnel context for the tunnel + * (can be NULL -- that's not an error) + */ +static void * +incoming_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel, + const struct GNUNET_PeerIdentity *initiator, + const struct GNUNET_ATS_Information *atsi) +{ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Incoming tunnel from %s to peer %d\n", + GNUNET_i2s (initiator), (long) cls); + ok++; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok); + if ((long) cls == 4L) + incoming_t = tunnel; + else if ((long) cls == 3L) + incoming_t2 = tunnel; + else + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Incoming tunnel for unknown client %lu\n", (long) cls); + GNUNET_break(0); + } + if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) + { + GNUNET_SCHEDULER_cancel (disconnect_task); + } + disconnect_task = + GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, + (void *) __LINE__); + + return NULL; +} + +/** + * Function called whenever an inbound tunnel is destroyed. Should clean up + * any associated state. + * + * @param cls closure (set from GNUNET_MESH_connect) + * @param tunnel connection to the other end (henceforth invalid) + * @param tunnel_ctx place where local state associated + * with the tunnel is stored + */ +static void +tunnel_cleaner (void *cls, const struct GNUNET_MESH_Tunnel *tunnel, + void *tunnel_ctx) +{ + long i = (long) cls; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Incoming tunnel disconnected at peer %d\n", + i); + if (4L == i) + { + ok++; + incoming_t = NULL; + } + else if (3L == i) + { + ok++; + incoming_t2 = NULL; + } + else + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unknown peer! %d\n", i); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok); + peers_in_tunnel--; + if (peers_in_tunnel > 0) + return; + + if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) + { + GNUNET_SCHEDULER_cancel (disconnect_task); + } + disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_mesh_peers, + (void *) __LINE__); + + return; +} + + +/** + * Method called whenever a tunnel falls apart. + * + * @param cls closure + * @param peer peer identity the tunnel stopped working with + */ +static void +dh (void *cls, const struct GNUNET_PeerIdentity *peer) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "peer %s disconnected\n", + GNUNET_i2s (peer)); + if (P2P_SIGNAL == test) + { + ok ++; + if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) + { + GNUNET_SCHEDULER_cancel (disconnect_task); + } + disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_mesh_peers, + (void *) __LINE__); + } + return; +} + + +/** + * Method called whenever a peer connects to a tunnel. + * + * @param cls closure + * @param peer peer identity the tunnel was created to, NULL on timeout + * @param atsi performance data for the connection + */ +static void +ch (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *atsi) +{ + long i = (long) cls; + + struct GNUNET_PeerIdentity *dest; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "%ld peer %s connected\n", i, GNUNET_i2s (peer)); + + if (0 == memcmp (p_id[2], peer, sizeof (struct GNUNET_PeerIdentity)) && + i == 0L) + { + ok++; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok); + switch (test) + { + case UNICAST: + case P2P_SIGNAL: + case SPEED: + case SPEED_ACK: + // incoming_t is NULL unless we send a relevant data packet + dest = p_id[2]; + break; + default: + GNUNET_assert (0); + return; + } + if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) + { + GNUNET_SCHEDULER_cancel (disconnect_task); + disconnect_task = + GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, + (void *) __LINE__); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending data initializer...\n"); + peers_responded = 0; + data_ack = 0; + data_received = 0; + data_sent = 0; + GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO, + GNUNET_TIME_UNIT_FOREVER_REL, dest, + size_payload, + &tmt_rdy, (void *) 1L); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Disconnect already run?\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Aborting...\n"); + } + return; +} + + +/** + * START THE TESTCASE ITSELF, AS WE ARE CONNECTED TO THE MESH SERVICES. + * + * Testcase continues when the root receives confirmation of connected peers, + * on callback funtion ch. + * + * @param cls Closure (unsued). + * @param tc Task Context. + */ +static void +do_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test_task\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "add peer 2\n"); + GNUNET_MESH_peer_request_connect_add (t, p_id[2]); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "schedule timeout in TIMEOUT\n"); + if (GNUNET_SCHEDULER_NO_TASK != disconnect_task) + { + GNUNET_SCHEDULER_cancel (disconnect_task); + } + disconnect_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + &disconnect_mesh_peers, + (void *) __LINE__); +} + +/** + * 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 + */ +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_delayed (GNUNET_TIME_UNIT_SECONDS, + &do_test, NULL); +} + +/** + * test main: start test when all peers are connected + * + * @param cls Closure. + * @param ctx Argument to give to GNUNET_MESH_TEST_cleanup on test end. + * @param num_peers Number of peers that are running. + * @param peers Array of peers. + * @param meshes Handle to each of the MESHs of the peers. + */ +static void +tmain (void *cls, + struct GNUNET_MESH_TEST_Context *ctx, + unsigned int num_peers, + struct GNUNET_TESTBED_Peer **peers, + struct GNUNET_MESH_Handle **meshes) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test main\n"); + ok = 0; + test_ctx = ctx; + peers_running = num_peers; + h1 = meshes[0]; + h2 = meshes[num_peers - 1]; + t = GNUNET_MESH_tunnel_create (h1, NULL, &ch, &dh, (void *) 0L); + if (SPEED_MIN == test) + { + GNUNET_MESH_tunnel_speed_min(t); + test = SPEED; + } + if (SPEED_NOBUF == test) + { + GNUNET_MESH_tunnel_buffer(t, GNUNET_NO); + test = SPEED; + } + peers_in_tunnel = 0; + disconnect_task = GNUNET_SCHEDULER_add_delayed (SHORT_TIME, + &disconnect_mesh_peers, + (void *) __LINE__); + shutdown_handle = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, + &shutdown_task, NULL); + t_op[0] = GNUNET_TESTBED_peer_get_information (peers[0], + GNUNET_TESTBED_PIT_IDENTITY, + &pi_cb, (void *) 0L); + t_op[2] = GNUNET_TESTBED_peer_get_information (peers[num_peers - 1], + GNUNET_TESTBED_PIT_IDENTITY, + &pi_cb, (void *) 2L); + t_op[1] = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "requested peer ids\n"); +} + + +/** + * Main: start test + */ +int +main (int argc, char *argv[]) +{ + initialized = GNUNET_NO; + + GNUNET_log_setup ("test", "DEBUG", NULL); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start\n"); + if (strstr (argv[0], "test_mesh2_small_forward") != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "UNICAST\n"); + test = UNICAST; + test_name = "unicast"; + ok_goal = 5; + } + else if (strstr (argv[0], "test_mesh_small_signal") != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SIGNAL\n"); + test = P2P_SIGNAL; + test_name = "signal"; + ok_goal = 5; + } + else if (strstr (argv[0], "test_mesh_small_speed_ack") != NULL) + { + /* Each peer is supposed to generate the following callbacks: + * 1 incoming tunnel (@dest) + * 1 connected peer (@orig) + * TOTAL_PACKETS received data packet (@dest) + * TOTAL_PACKETS received data packet (@orig) + * 1 received tunnel destroy (@dest) + * _________________________________ + * 5 x ok expected per peer + */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED_ACK\n"); + test = SPEED_ACK; + test_name = "speed ack"; + ok_goal = TOTAL_PACKETS * 2 + 3; + } + else if (strstr (argv[0], "test_mesh_small_speed") != NULL) + { + /* Each peer is supposed to generate the following callbacks: + * 1 incoming tunnel (@dest) + * 1 connected peer (@orig) + * 1 initial packet (@dest) + * TOTAL_PACKETS received data packet (@dest) + * 1 received data packet (@orig) + * 1 received tunnel destroy (@dest) + * _________________________________ + */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED\n"); + ok_goal = TOTAL_PACKETS + 5; + if (strstr (argv[0], "_min") != NULL) + { + test = SPEED_MIN; + test_name = "speed min"; + } + else if (strstr (argv[0], "_nobuf") != NULL) + { + test = SPEED_NOBUF; + test_name = "speed nobuf"; + } + else + { + test = SPEED; + test_name = "speed"; + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "UNKNOWN\n"); + test = SETUP; + ok_goal = 0; + } + + if (strstr (argv[0], "backwards") != NULL) + { + char *aux; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "BACKWARDS (LEAF TO ROOT)\n"); + test_backwards = GNUNET_YES; + aux = GNUNET_malloc (32); + sprintf (aux, "backwards %s", test_name); + test_name = aux; + } + + p_ids = 0; + GNUNET_MESH_TEST_run ("test_mesh_small", + "test_mesh_small.conf", + 5, + &tmain, + NULL, + &incoming_tunnel, + &tunnel_cleaner, + handlers, + NULL); + + if (ok_goal > ok) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "FAILED! (%d/%d)\n", ok, ok_goal); + return 1; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "success\n"); + return 0; +} + +/* end of test_mesh_small.c */