]> git.meshlink.io Git - meshlink/commitdiff
Port the blackbox meta-connections test using network namespaces.
authorGuus Sliepen <guus@meshlink.io>
Wed, 22 Jul 2020 18:36:18 +0000 (20:36 +0200)
committerGuus Sliepen <guus@meshlink.io>
Wed, 22 Jul 2020 19:06:08 +0000 (21:06 +0200)
test/Makefile.am
test/meta-connections.c [new file with mode: 0644]
test/netns_utils.c [new file with mode: 0644]
test/netns_utils.h [new file with mode: 0644]

index 28f92560071fbe800eeb29fc61fec23d9b85b3b6..4d6d93683ce47925c772254324bf01452b6c36da 100644 (file)
@@ -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 (file)
index 0000000..22d7c21
--- /dev/null
@@ -0,0 +1,89 @@
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#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 (file)
index 0000000..d957d5b
--- /dev/null
@@ -0,0 +1,148 @@
+#define _GNU_SOURCE 1
+
+#ifndef NDEBUG
+#undef NDEBUG
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#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 (file)
index 0000000..7bbe51a
--- /dev/null
@@ -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