From: Guus Sliepen Date: Wed, 22 Jul 2020 18:36:18 +0000 (+0200) Subject: Port the blackbox meta-connections test using network namespaces. X-Git-Url: https://git.meshlink.io/?a=commitdiff_plain;h=e38737be28c742b241a6b0ffdbad541c3c0b13b2;p=meshlink Port the blackbox meta-connections test using network namespaces. --- diff --git a/test/Makefile.am b/test/Makefile.am index 28f92560..4d6d9368 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -17,6 +17,7 @@ TESTS = \ get-all-nodes \ import-export \ invite-join \ + meta-connections \ sign-verify \ trio \ trio2 \ @@ -52,6 +53,7 @@ check_PROGRAMS = \ get-all-nodes \ import-export \ invite-join \ + meta-connections \ sign-verify \ stream \ trio \ @@ -118,6 +120,9 @@ import_export_LDADD = $(top_builddir)/src/libmeshlink.la invite_join_SOURCES = invite-join.c utils.c utils.h invite_join_LDADD = $(top_builddir)/src/libmeshlink.la +meta_connections_SOURCES = meta-connections.c netns_utils.c netns_utils.h utils.c utils.h +meta_connections_LDADD = $(top_builddir)/src/libmeshlink.la + sign_verify_SOURCES = sign-verify.c utils.c utils.h sign_verify_LDADD = $(top_builddir)/src/libmeshlink.la diff --git a/test/meta-connections.c b/test/meta-connections.c new file mode 100644 index 00000000..22d7c21d --- /dev/null +++ b/test/meta-connections.c @@ -0,0 +1,89 @@ +#ifdef NDEBUG +#undef NDEBUG +#endif + +#include +#include +#include + +#include "meshlink.h" +#include "devtools.h" +#include "netns_utils.h" +#include "utils.h" + +static struct sync_flag peer_reachable; +static struct sync_flag peer_unreachable; + +static void nut_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) { + (void)mesh; + + if(!strcmp(node->name, "peer")) { + set_sync_flag(reachable ? &peer_reachable : &peer_unreachable, true); + } +} + +int main(void) { + meshlink_set_log_cb(NULL, MESHLINK_DEBUG, log_cb); + + init_sync_flag(&peer_reachable); + init_sync_flag(&peer_unreachable); + + // Set up relay, peer and NUT + peer_config_t *peers = setup_relay_peer_nut("metaconn"); + + // Wait for peer to connect to NUT + devtool_set_meta_status_cb(peers[2].mesh, nut_status_cb); + + for(int i = 0; i < 3; i++) { + assert(meshlink_start(peers[i].mesh)); + } + + assert(wait_sync_flag(&peer_reachable, 5)); + + // Test case #1: re-connection to peer after disconnection when connected to the relay node + + // Stop the peer and wait for it to become unreachable + reset_sync_flag(&peer_unreachable); + meshlink_stop(peers[1].mesh); + assert(wait_sync_flag(&peer_unreachable, 5)); + + // Restart the peer and wait for it to become reachable + reset_sync_flag(&peer_reachable); + assert(meshlink_start(peers[1].mesh)); + assert(wait_sync_flag(&peer_reachable, 5)); + + // Test case #2: re-connection to peer after changing peer and NUT's IP address simultaneously, + // while connected to the relay + + reset_sync_flag(&peer_reachable); + reset_sync_flag(&peer_unreachable); + + for(int i = 1; i < 3; i++) { + change_peer_ip(&peers[i]); + } + + for(int i = 1; i < 3; i++) { + meshlink_reset_timers(peers[i].mesh); + } + + assert(wait_sync_flag(&peer_unreachable, 75)); + assert(wait_sync_flag(&peer_reachable, 15)); + + // Test case #3: re-connect to peer after stopping NUT and changing peer's IP address, no relay + reset_sync_flag(&peer_unreachable); + + for(int i = 0; i < 2; i++) { + meshlink_stop(peers[i].mesh); + } + + change_peer_ip(&peers[1]); + assert(wait_sync_flag(&peer_unreachable, 15)); + + reset_sync_flag(&peer_reachable); + assert(meshlink_start(peers[1].mesh)); + assert(wait_sync_flag(&peer_reachable, 60)); + + // Done. + + close_relay_peer_nut(peers); +} diff --git a/test/netns_utils.c b/test/netns_utils.c new file mode 100644 index 00000000..d957d5b6 --- /dev/null +++ b/test/netns_utils.c @@ -0,0 +1,148 @@ +#define _GNU_SOURCE 1 + +#ifndef NDEBUG +#undef NDEBUG +#endif + +#include +#include +#include +#include +#include + +#include "../src/meshlink.h" +#include "netns_utils.h" +#include "utils.h" + +static int ip = 1; + +/// Create meshlink instances and network namespaces for a list of peers +static void create_peers(peer_config_t *peers, int npeers, const char *prefix) { + // We require root for network namespaces + if(getuid() != 0) { + exit(77); + } + + for(int i = 0; i < npeers; i++) { + assert(asprintf(&peers[i].netns_name, "%s%d", prefix, i) > 0); + char *command = NULL; + assert(asprintf(&command, + "/bin/ip netns delete %1$s 2>/dev/null || true;" + "/bin/ip netns add %1$s;" + "/bin/ip netns exec %1$s ip link set dev lo up;", + peers[i].netns_name)); + assert(command); + assert(system(command) == 0); + free(command); + + char *netns_path = NULL; + assert(asprintf(&netns_path, "/run/netns/%s", peers[i].netns_name)); + assert(netns_path); + peers[i].netns = open(netns_path, O_RDONLY); + assert(peers[i].netns != -1); + free(netns_path); + + char *conf_path = NULL; + assert(asprintf(&conf_path, "%s_conf.%d", prefix, i + 1) > 0); + assert(conf_path); + assert(meshlink_destroy(conf_path)); + + meshlink_open_params_t *params = meshlink_open_params_init(conf_path, peers[i].name, prefix, peers[i].devclass); + assert(params); + assert(meshlink_open_params_set_netns(params, peers[i].netns)); + + peers[i].mesh = meshlink_open_ex(params); + assert(peers[i].mesh); + free(params); + free(conf_path); + + meshlink_enable_discovery(peers[i].mesh, false); + } +} + +/// Set up a LAN topology where all peers can see each other directly +static void setup_lan_topology(peer_config_t *peers, int npeers) { + // Set up the LAN bridge + { + char *command = NULL; + assert(asprintf(&command, + "/bin/ip netns exec %1$s /bin/ip link add eth0 type bridge;" + "/bin/ip netns exec %1$s /bin/ip link set eth0 up;", + peers[0].netns_name)); + assert(command); + assert(system(command) == 0); + } + + // Add an interface to each peer that is connected to the bridge + for(int i = 1; i < npeers; i++) { + char *command = NULL; + assert(asprintf(&command, + "/bin/ip netns exec %1$s /bin/ip link add eth0 type veth peer eth%3$d netns %2$s;" + "/bin/ip netns exec %1$s /bin/ip link set dev eth0 up;" + "/bin/ip netns exec %2$s /bin/ip link set dev eth%3$d master eth0 up;", + peers[i].netns_name, peers[0].netns_name, i)); + assert(command); + assert(system(command) == 0); + free(command); + } + + // Configure addresses + for(int i = 0; i < npeers; i++) { + change_peer_ip(&peers[i]); + } +} + +/// Give a peer a unique IP address +void change_peer_ip(peer_config_t *peer) { + char *command = NULL; + assert(asprintf(&command, + "/bin/ip netns exec %1$s ip addr flush dev eth0;" + "/bin/ip netns exec %1$s ip addr add 203.0.113.%2$d/24 dev eth0;", + peer->netns_name, ip)); + ip++; + assert(command); + assert(system(command) == 0); + free(command); +} + +/// Let the first peer in a list invite all the subsequent peers +static void invite_peers(peer_config_t *peers, int npeers) { + assert(meshlink_start(peers[0].mesh)); + + for(int i = 1; i < npeers; i++) { + char *invitation = meshlink_invite_ex(peers[0].mesh, NULL, peers[i].name, MESHLINK_INVITE_LOCAL | MESHLINK_INVITE_NUMERIC); + assert(invitation); + assert(meshlink_join(peers[i].mesh, invitation)); + free(invitation); + } + + meshlink_stop(peers[0].mesh); +} + +/// Close meshlink instances and clean up +static void close_peers(peer_config_t *peers, int npeers) { + for(int i = 0; i < npeers; i++) { + meshlink_close(peers[i].mesh); + close(peers[i].netns); + free(peers[i].netns_name); + } +} + +/// Set up relay, peer and NUT that are directly connected +peer_config_t *setup_relay_peer_nut(const char *prefix) { + static peer_config_t peers[] = { + {"relay", DEV_CLASS_BACKBONE}, + {"peer", DEV_CLASS_STATIONARY}, + {"nut", DEV_CLASS_STATIONARY}, + }; + + create_peers(peers, 3, prefix); + setup_lan_topology(peers, 3); + invite_peers(peers, 3); + + return peers; +} + +void close_relay_peer_nut(peer_config_t *peers) { + close_peers(peers, 3); +} diff --git a/test/netns_utils.h b/test/netns_utils.h new file mode 100644 index 00000000..7bbe51a2 --- /dev/null +++ b/test/netns_utils.h @@ -0,0 +1,17 @@ +#ifndef MESHLINK_TEST_NETNS_UTILS_H +#define MESHLINK_TEST_NETNS_UTILS_H + +typedef struct peer_config { + const char *name; + const dev_class_t devclass; + + char *netns_name; + int netns; + meshlink_handle_t *mesh; +} peer_config_t; + +extern void change_peer_ip(peer_config_t *peer); +extern peer_config_t *setup_relay_peer_nut(const char *prefix); +extern void close_relay_peer_nut(peer_config_t *peers); + +#endif