From: sairoop-elear Date: Thu, 25 Apr 2019 05:19:43 +0000 (+0530) Subject: Add meta-connection attempt callback feature X-Git-Url: https://git.meshlink.io/?a=commitdiff_plain;h=2fd608c4111ef4d48a649401d918f5981856cc44;p=meshlink Add meta-connection attempt callback feature --- diff --git a/src/meshlink++.h b/src/meshlink++.h index 8f4a9b00..48eabf3f 100644 --- a/src/meshlink++.h +++ b/src/meshlink++.h @@ -43,6 +43,13 @@ typedef meshlink_errno_t errno_t; */ typedef void (*receive_cb_t)(mesh *mesh, node *source, const void *data, size_t len); +/// A callback reporting the meta-connection attempt made by the host node to an another node. +/** @param mesh A handle which represents an instance of MeshLink. + * @param node A pointer to a meshlink_node_t describing the node to whom meta-connection is being tried. + * This pointer is valid until meshlink_close() is called. + */ +typedef void (*connection_try_cb_t)(mesh *mesh, node *node); + /// A callback reporting node status changes. /** @param mesh A handle which represents an instance of MeshLink. * @param node A pointer to a meshlink::node describing the node whose status changed. @@ -213,6 +220,12 @@ public: (void)message; } + /// This functions is called whenever MeshLink a meta-connection attempt is made. + virtual void connection_try(node *peer) { + /* do nothing */ + (void)peer; + } + /// This functions is called whenever another node attempts to open a channel to the local node. /** * If the channel is accepted, the poll_callback will be set to channel_poll and can be @@ -287,6 +300,7 @@ public: meshlink_set_node_duplicate_cb(handle, &node_duplicate_trampoline); meshlink_set_log_cb(handle, MESHLINK_DEBUG, &log_trampoline); meshlink_set_channel_accept_cb(handle, &channel_accept_trampoline); + meshlink_set_connection_try_cb(handle, &connection_try_trampoline); return meshlink_start(handle); } @@ -779,6 +793,15 @@ private: that->log(level, message); } + static void connection_try_trampoline(meshlink_handle_t *handle, meshlink_node_t *peer) { + if(!(handle->priv)) { + return; + } + + meshlink::mesh *that = static_cast(handle->priv); + that->connection_try(static_cast(peer)); + } + static bool channel_accept_trampoline(meshlink_handle_t *handle, meshlink_channel *channel, uint16_t port, const void *data, size_t len) { if(!(handle->priv)) { return false; diff --git a/src/meshlink.c b/src/meshlink.c index cd23771a..1dbefd0e 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -1674,6 +1674,17 @@ void meshlink_set_receive_cb(meshlink_handle_t *mesh, meshlink_receive_cb_t cb) pthread_mutex_unlock(&(mesh->mesh_mutex)); } +void meshlink_set_connection_try_cb(meshlink_handle_t *mesh, meshlink_connection_try_cb_t cb) { + if(!mesh) { + meshlink_errno = MESHLINK_EINVAL; + return; + } + + pthread_mutex_lock(&(mesh->mesh_mutex)); + mesh->connection_try_cb = cb; + pthread_mutex_unlock(&(mesh->mesh_mutex)); +} + void meshlink_set_node_status_cb(meshlink_handle_t *mesh, meshlink_node_status_cb_t cb) { if(!mesh) { meshlink_errno = MESHLINK_EINVAL; diff --git a/src/meshlink.h b/src/meshlink.h index a672118b..8ec7b508 100644 --- a/src/meshlink.h +++ b/src/meshlink.h @@ -300,6 +300,26 @@ typedef void (*meshlink_receive_cb_t)(meshlink_handle_t *mesh, meshlink_node_t * */ extern void meshlink_set_receive_cb(meshlink_handle_t *mesh, meshlink_receive_cb_t cb); +/// A callback reporting the meta-connection attempt made by the host node to an another node. +/** @param mesh A handle which represents an instance of MeshLink. + * @param node A pointer to a meshlink_node_t describing the node to whom meta-connection is being tried. + * This pointer is valid until meshlink_close() is called. + */ +typedef void (*meshlink_connection_try_cb_t)(meshlink_handle_t *mesh, meshlink_node_t *node); + +/// Set the meta-connection try callback. +/** This functions sets the callback that is called whenever a connection attempt is happened to another node. + * The callback is run in MeshLink's own thread. + * It is therefore important that the callback uses apprioriate methods (queues, pipes, locking, etc.) + * to hand the data over to the application's thread. + * The callback should also not block itself and return as quickly as possible. + * + * @param mesh A handle which represents an instance of MeshLink. + * @param cb A pointer to the function which will be called when host node attempts to make + * the connection to another node. If a NULL pointer is given, the callback will be disabled. + */ +extern void meshlink_set_connection_try_cb(meshlink_handle_t *mesh, meshlink_connection_try_cb_t cb); + /// A callback reporting node status changes. /** @param mesh A handle which represents an instance of MeshLink. * @param node A pointer to a meshlink_node_t describing the node whose status changed. diff --git a/src/meshlink_internal.h b/src/meshlink_internal.h index a650083a..a40de535 100644 --- a/src/meshlink_internal.h +++ b/src/meshlink_internal.h @@ -88,6 +88,7 @@ struct meshlink_handle { meshlink_channel_accept_cb_t channel_accept_cb; meshlink_node_duplicate_cb_t node_duplicate_cb; + meshlink_connection_try_cb_t connection_try_cb; pthread_t thread; bool threadstarted; diff --git a/src/net_socket.c b/src/net_socket.c index d1a5df99..3047ccbd 100644 --- a/src/net_socket.c +++ b/src/net_socket.c @@ -637,6 +637,11 @@ void setup_outgoing_connection(meshlink_handle_t *mesh, outgoing_t *outgoing) { return; } + if(mesh->connection_try_cb) { + node_t *n = lookup_node(mesh, outgoing->name); + mesh->connection_try_cb(mesh, (struct meshlink_node *)n); + } + do_outgoing_connection(mesh, outgoing); } diff --git a/test/blackbox/common/tcpdump.c b/test/blackbox/common/tcpdump.c index 7839a422..422c397e 100644 --- a/test/blackbox/common/tcpdump.c +++ b/test/blackbox/common/tcpdump.c @@ -25,7 +25,6 @@ #include #include #include "common_handlers.h" -#include "containers.h" #include "tcpdump.h" pid_t tcpdump_start(char *interface) { diff --git a/test/blackbox/run_blackbox_tests/Makefile.am b/test/blackbox/run_blackbox_tests/Makefile.am index 0fa653b1..89dc8382 100644 --- a/test/blackbox/run_blackbox_tests/Makefile.am +++ b/test/blackbox/run_blackbox_tests/Makefile.am @@ -61,7 +61,8 @@ run_blackbox_tests_SOURCES = \ test_cases_submesh02.c \ test_cases_submesh03.c \ test_cases_submesh04.c \ - test_cases_autoconnect.c + test_cases_autoconnect.c \ + test_cases_set_connection_try_cb.c run_blackbox_tests_LDADD = ../../../src/libmeshlink.la $(LXC_LIBS) $(CMOCKA_LIBS) run_blackbox_tests_CFLAGS = -D_GNU_SOURCE $(LXC_CFLAGS) $(CMOCKA_CFLAGS) diff --git a/test/blackbox/run_blackbox_tests/run_blackbox_tests.c b/test/blackbox/run_blackbox_tests/run_blackbox_tests.c index 29c662bb..b174ab7e 100644 --- a/test/blackbox/run_blackbox_tests/run_blackbox_tests.c +++ b/test/blackbox/run_blackbox_tests/run_blackbox_tests.c @@ -1,7 +1,7 @@ /* run_blackbox_tests.c -- Implementation of Black Box Test Execution for meshlink - Copyright (C) 2018 Guus Sliepen + Copyright (C) 2019 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -70,8 +70,8 @@ #include "test_cases_submesh02.h" #include "test_cases_submesh03.h" #include "test_cases_submesh04.h" - #include "test_cases_autoconnect.h" +#include "test_cases_set_connection_try_cb.h" #include "../common/containers.h" #include "../common/common_handlers.h" @@ -91,7 +91,6 @@ int main(int argc, char *argv[]) { int failed_tests = 0; - /* failed_tests += test_meta_conn(); failed_tests += test_meshlink_set_status_cb(); failed_tests += test_meshlink_join(); @@ -134,7 +133,6 @@ int main(int argc, char *argv[]) { failed_tests += test_meshlink_channel_close(); failed_tests += test_meshlink_channel_conn(); - */ failed_tests += test_optimal_pmtu(); failed_tests += test_cases_submesh01(); @@ -143,6 +141,7 @@ int main(int argc, char *argv[]) { failed_tests += test_cases_submesh04(); failed_tests += test_meshlink_autoconnect(); + failed_tests += test_cases_connection_try(); failed_tests += test_optimal_pmtu(); diff --git a/test/blackbox/run_blackbox_tests/test_cases_set_connection_try_cb.c b/test/blackbox/run_blackbox_tests/test_cases_set_connection_try_cb.c new file mode 100644 index 00000000..42ac08f5 --- /dev/null +++ b/test/blackbox/run_blackbox_tests/test_cases_set_connection_try_cb.c @@ -0,0 +1,146 @@ +/* + test_cases_set_connection_try_cb.c -- Execution of specific meshlink black box test cases + Copyright (C) 2019 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "execute_tests.h" +#include "test_cases_set_connection_try_cb.h" +#include "../common/test_step.h" +#include "../common/common_handlers.h" +#include "../../utils.h" +#include +#include +#include +#include + +static void test_case_set_connection_try_cb_01(void **state); +static bool test_set_connection_try_cb_01(void); + +static bool bar_reachable; +static int connection_attempts; +static struct sync_flag status_changed_cond = {.mutex = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER}; +static struct sync_flag connection_attempt_cond = {.mutex = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER}; + +static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *source, bool reachable) { + if(!strcmp(mesh->name, "foo") && !strcmp(source->name, "bar")) { + bar_reachable = reachable; + set_sync_flag(&status_changed_cond, true); + } +} + +/* Meta-connection try callback handler */ +static void connection_try_cb(meshlink_handle_t *mesh, meshlink_node_t *source) { + if(!strcmp(mesh->name, "foo")) { + ++connection_attempts; + + if(connection_attempts > 3) { + set_sync_flag(&connection_attempt_cond, true); + } + } +} + +/* Execute set meta connection try callback Test Case # 1 */ +static void test_case_set_connection_try_cb_01(void **state) { + execute_test(test_set_connection_try_cb_01, state); +} + +/* Test Steps for meshlink_set_connection_try_cb Test Case # 1 + + Test Steps: + 1. Run foo and bar nodes after exporting and importing node's keys and addresses mutually. + 2. Close bar node. Wait for connection attempts and cleanup. + + Expected Result: + Connection try callback should be invoked initially when foo and bar forms a meta-connection. + After closing bar node it should invoke 3 connection try callbacks in span of about 30 seconds. +*/ +static bool test_set_connection_try_cb_01(void) { + meshlink_destroy("meshlink_conf.1"); + meshlink_destroy("meshlink_conf.2"); + + // Opening foo and bar nodes + meshlink_handle_t *mesh1 = meshlink_open("meshlink_conf.1", "foo", "test", DEV_CLASS_STATIONARY); + assert(mesh1 != NULL); + meshlink_set_log_cb(mesh1, MESHLINK_DEBUG, meshlink_callback_logger); + meshlink_enable_discovery(mesh1, false); + meshlink_handle_t *mesh2 = meshlink_open("meshlink_conf.2", "bar", "test", DEV_CLASS_STATIONARY); + assert(mesh2 != NULL); + + // Set up callback for node status + meshlink_set_node_status_cb(mesh1, node_status_cb); + meshlink_set_connection_try_cb(mesh1, connection_try_cb); + + // Exporting and Importing mutually + char *exp1 = meshlink_export(mesh1); + assert(exp1 != NULL); + char *exp2 = meshlink_export(mesh2); + assert(exp2 != NULL); + bool imp1 = meshlink_import(mesh1, exp2); + bool imp2 = meshlink_import(mesh2, exp1); + free(exp1); + free(exp2); + + assert(meshlink_start(mesh1)); + assert(meshlink_start(mesh2)); + + // Wait for foo and bar nodes to join + assert(wait_sync_flag(&status_changed_cond, 5)); + assert(bar_reachable); + + // Joining should in this case raise one connection try callback + assert_int_equal(connection_attempts, 1); + + // Close the bar node + set_sync_flag(&status_changed_cond, false); + meshlink_close(mesh2); + assert(wait_sync_flag(&status_changed_cond, 5)); + assert(!bar_reachable); + + // Wait for additional 3 connection try callbacks + time_t attempt_time_start = time(NULL); + assert(attempt_time_start != -1); + assert_int_equal(wait_sync_flag(&connection_attempt_cond, 60), true); + + // Close bar node and assert on number of callbacks invoked and the time taken. + meshlink_close(mesh1); + time_t attempt_time_stop = time(NULL); + assert(attempt_time_stop != -1); + assert_int_equal(connection_attempts, 4); + assert_in_range(attempt_time_stop - attempt_time_start, 25, 45); + + // Cleanup + meshlink_destroy("meshlink_conf.1"); + meshlink_destroy("meshlink_conf.2"); + + return true; +} + +int test_cases_connection_try(void) { + black_box_state_t test_case_set_connection_try_cb_01_state = { + .test_case_name = "test_case_set_connection_try_cb_01", + }; + + const struct CMUnitTest blackbox_connection_try_tests[] = { + cmocka_unit_test_prestate_setup_teardown(test_case_set_connection_try_cb_01, NULL, NULL, + (void *)&test_case_set_connection_try_cb_01_state), + }; + total_tests += sizeof(blackbox_connection_try_tests) / sizeof(blackbox_connection_try_tests[0]); + + int failed = cmocka_run_group_tests(blackbox_connection_try_tests, NULL, NULL); + + return failed; +} diff --git a/test/blackbox/run_blackbox_tests/test_cases_set_connection_try_cb.h b/test/blackbox/run_blackbox_tests/test_cases_set_connection_try_cb.h new file mode 100644 index 00000000..8a3c0dac --- /dev/null +++ b/test/blackbox/run_blackbox_tests/test_cases_set_connection_try_cb.h @@ -0,0 +1,26 @@ +#ifndef TEST_CASES_SET_CONNECTION_TRY_CB_H +#define TEST_CASES_SET_CONNECTION_TRY_CB_H + +/* + test_cases_set_connection_try_cb.h -- Declarations for Individual Test Case implementation functions + Copyright (C) 2019 Guus Sliepen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +extern int test_cases_connection_try(void); +extern int total_tests; + +#endif // TEST_CASES_SET_CONNECTION_TRY_CB_H