]> git.meshlink.io Git - meshlink/commitdiff
Add the blackbox channel connection tests.
authorGuus Sliepen <guus@meshlink.io>
Sun, 18 Nov 2018 13:50:08 +0000 (14:50 +0100)
committerGuus Sliepen <guus@meshlink.io>
Sun, 18 Nov 2018 13:50:08 +0000 (14:50 +0100)
39 files changed:
configure.ac
test/blackbox/Makefile.am
test/blackbox/common/common_handlers.c
test/blackbox/common/common_handlers.h
test/blackbox/common/containers.c
test/blackbox/common/containers.h
test/blackbox/run_blackbox_tests/Makefile.am
test/blackbox/run_blackbox_tests/execute_tests.c
test/blackbox/run_blackbox_tests/run_blackbox_tests.c
test/blackbox/run_blackbox_tests/test_cases_channel_conn.c [new file with mode: 0644]
test/blackbox/run_blackbox_tests/test_cases_channel_conn.h
test/blackbox/test_case_channel_conn_01/Makefile.am [new file with mode: 0644]
test/blackbox/test_case_channel_conn_01/node_sim_nut.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_01/node_sim_peer.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_02/Makefile.am [new file with mode: 0644]
test/blackbox/test_case_channel_conn_02/node_sim_nut.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_02/node_sim_peer.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_03/Makefile.am [new file with mode: 0644]
test/blackbox/test_case_channel_conn_03/node_sim_nut.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_03/node_sim_peer.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_04/Makefile.am [new file with mode: 0644]
test/blackbox/test_case_channel_conn_04/node_sim_nut.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_04/node_sim_peer.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_05/Makefile.am [new file with mode: 0644]
test/blackbox/test_case_channel_conn_05/node_sim_nut.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_05/node_sim_peer.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_05/node_sim_relay.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_06/Makefile.am [new file with mode: 0644]
test/blackbox/test_case_channel_conn_06/node_sim_nut.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_06/node_sim_peer.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_06/node_sim_relay.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_07/Makefile.am [new file with mode: 0644]
test/blackbox/test_case_channel_conn_07/node_sim_nut.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_07/node_sim_peer.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_07/node_sim_relay.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_08/Makefile.am [new file with mode: 0644]
test/blackbox/test_case_channel_conn_08/node_sim_nut.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_08/node_sim_peer.c [new file with mode: 0644]
test/blackbox/test_case_channel_conn_08/node_sim_relay.c [new file with mode: 0644]

index 363676eec55e21d22a19f2c0c332d8ca0aacb31a..ed193cd42a17563f9de0db2bbe2b6cc03b176b76 100644 (file)
@@ -145,6 +145,14 @@ AC_CONFIG_FILES([
         test/Makefile
         test/blackbox/Makefile
         test/blackbox/run_blackbox_tests/Makefile
+        test/blackbox/test_case_channel_conn_01/Makefile
+        test/blackbox/test_case_channel_conn_02/Makefile
+        test/blackbox/test_case_channel_conn_03/Makefile
+        test/blackbox/test_case_channel_conn_04/Makefile
+        test/blackbox/test_case_channel_conn_05/Makefile
+        test/blackbox/test_case_channel_conn_06/Makefile
+        test/blackbox/test_case_channel_conn_07/Makefile
+        test/blackbox/test_case_channel_conn_08/Makefile
         test/blackbox/test_case_meta_conn_01/Makefile
         test/blackbox/test_case_meta_conn_02/Makefile
         test/blackbox/test_case_meta_conn_03/Makefile
index 3afae4b1c943b71f02fd0136da63ad1077c4da5b..3d0e6005d6c812509adb19b384dbd7a8d0be3ac0 100644 (file)
@@ -1,13 +1,21 @@
 check_PROGRAMS = gen_invite
-
 SUBDIRS = \
        run_blackbox_tests \
+       test_case_channel_conn_01 \
+       test_case_channel_conn_02 \
+       test_case_channel_conn_03 \
+       test_case_channel_conn_04 \
+       test_case_channel_conn_05 \
+       test_case_channel_conn_06 \
+       test_case_channel_conn_07 \
+       test_case_channel_conn_08 \
        test_case_meta_conn_01 \
        test_case_meta_conn_02 \
        test_case_meta_conn_03 \
        test_case_meta_conn_04 \
        test_case_meta_conn_05
 
+
 gen_invite_SOURCES = util/gen_invite.c common/common_handlers.c common/test_step.c common/mesh_event_handler.c
 gen_invite_LDADD = ../../src/libmeshlink.la
 gen_invite_CFLAGS = -D_GNU_SOURCE
index ad32a223e7d867e7b278f8030e237772b129f423..d6ef7756933ad179d73f7c54d753090bc03080b1 100644 (file)
 #include "test_step.h"
 #include "common_handlers.h"
 
+#define GET_IP_FAMILY AF_INET
+
 char *lxc_bridge = NULL;
 black_box_state_t *state_ptr = NULL;
 
 bool meta_conn_status[10];
+bool node_reachable_status[10];
 
 bool test_running;
 
-static int meshlink_get_node_in_container(const char *name) {
-       int i;
-
-       for(i = 0; i < state_ptr->num_nodes; i++) {
-               if(!strcasecmp(state_ptr->node_names[i], name)) {
-                       return i;
-                       break;
-               }
-       }
-
-       return -1;
-}
-
 void mesh_close_signal_handler(int a) {
        test_running = false;
 
        exit(EXIT_SUCCESS);
 }
 
+void mesh_stop_start_signal_handler(int a) {
+       /* Stop the Mesh if it is running, otherwise start it again */
+       (mesh_started) ? execute_stop() : execute_start();
+}
+
 void setup_signals(void) {
        test_running = true;
        signal(SIGTERM, mesh_close_signal_handler);
+       signal(SIGINT, mesh_stop_start_signal_handler);
 }
 
 /* Return the IP Address of the Interface 'if_name'
@@ -82,8 +78,8 @@ char *get_ip(const char *if_name) {
 
                family = ifa->ifa_addr->sa_family;
 
-               if(family == AF_INET && !strcmp(ifa->ifa_name, if_name)) {
-                       assert(!getnameinfo(ifa->ifa_addr, (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6), ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST));
+               if(family == GET_IP_FAMILY && !strcmp(ifa->ifa_name, if_name)) {
+                       assert(!getnameinfo(ifa->ifa_addr, (family == GET_IP_FAMILY) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6), ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST));
                        break;
                }
        }
@@ -109,8 +105,8 @@ char *get_netmask(const char *if_name) {
 
                family = ifa->ifa_addr->sa_family;
 
-               if(family == AF_INET && !strcmp(ifa->ifa_name, if_name)) {
-                       assert(!getnameinfo(ifa->ifa_netmask, (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6), ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST));
+               if(family == GET_IP_FAMILY && !strcmp(ifa->ifa_name, if_name)) {
+                       assert(!getnameinfo(ifa->ifa_netmask, (family == GET_IP_FAMILY) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6), ip, NI_MAXHOST, NULL, 0, NI_NUMERICHOST));
                        break;
                }
        }
@@ -149,35 +145,60 @@ void start_nw_intf(const char *if_name) {
 
 void meshlink_callback_node_status(meshlink_handle_t *mesh, meshlink_node_t *node,
                                    bool reachable) {
+       int i;
+
        (void)mesh;
        fprintf(stderr, "Node %s became %s\n", node->name, (reachable) ? "reachable" : "unreachable");
+
+       if(state_ptr)
+               for(i = 0; i < state_ptr->num_nodes; i++)
+                       if(strcmp(node->name, state_ptr->node_names[i]) == 0) {
+                               node_reachable_status[i] = reachable;
+                       }
 }
 
 void meshlink_callback_logger(meshlink_handle_t *mesh, meshlink_log_level_t level,
                               const char *text) {
+       int i;
+       char connection_match_msg[100];
        (void)mesh;
        (void)level;
 
        fprintf(stderr, "meshlink>> %s\n", text);
 
-       if(state_ptr) {
-               bool status;
-               char name[100];
-
-               if(sscanf(text, "Connection with %s activated", name) == 1) {
-                       status = true;
-               } else if(sscanf(text, "Already connected to %s", name) == 1) {
-                       status = true;
-               } else if(sscanf(text, "Connection closed by %s", name) == 1) {
-                       status = false;
-               } else if(sscanf(text, "Closing connection with %s", name) == 1) {
-                       status = false;
-               } else {
-                       return;
-               }
+       if(state_ptr && (strstr(text, "Connection") || strstr(text, "connection"))) {
+               for(i = 0; i < state_ptr->num_nodes; i++) {
+                       assert(snprintf(connection_match_msg, sizeof(connection_match_msg),
+                                       "Connection with %s", state_ptr->node_names[i]) >= 0);
+
+                       if(strstr(text, connection_match_msg) && strstr(text, "activated")) {
+                               meta_conn_status[i] = true;
+                               continue;
+                       }
+
+                       assert(snprintf(connection_match_msg, sizeof(connection_match_msg),
+                                       "Already connected to %s", state_ptr->node_names[i]) >= 0);
 
-               int i = meshlink_get_node_in_container(name);
-               assert(i != -1);
-               meta_conn_status[i] = status;
+                       if(strstr(text, connection_match_msg)) {
+                               meta_conn_status[i] = true;
+                               continue;
+                       }
+
+                       assert(snprintf(connection_match_msg, sizeof(connection_match_msg),
+                                       "Connection closed by %s", state_ptr->node_names[i]) >= 0);
+
+                       if(strstr(text, connection_match_msg)) {
+                               meta_conn_status[i] = false;
+                               continue;
+                       }
+
+                       assert(snprintf(connection_match_msg, sizeof(connection_match_msg),
+                                       "Closing connection with %s", state_ptr->node_names[i]) >= 0);
+
+                       if(strstr(text, connection_match_msg)) {
+                               meta_conn_status[i] = false;
+                               continue;
+                       }
+               }
        }
 }
index 7c5fbd23ce7584e82359c23780a093af31ddc793..e878f3b78dd263b8c349f74ac8a88f6f87d15d76 100644 (file)
@@ -50,4 +50,24 @@ void meshlink_callback_node_status(meshlink_handle_t *mesh, meshlink_node_t *nod
 void meshlink_callback_logger(meshlink_handle_t *mesh, meshlink_log_level_t level,
                               const char *text);
 
+void change_ip(int node);
+void create_bridge(const char *bridgeName);
+void add_interface(const char *bridgeName, const char *interfaceName);
+void add_veth_pair(const char *vethName1, const char *vethName2);
+void bring_if_up(const char *bridgeName);
+void replaceAll(char *str, const char *oldWord, const char *newWord);
+void switch_bridge(const char *containerName, const char *currentBridge, const char *newBridge);
+void bring_if_down(const char *bridgeName);
+void del_interface(const char *bridgeName, const char *interfaceName);
+void delete_bridge(const char *bridgeName);
+void create_container_on_bridge(const char *containerName, const char *bridgeName, const char *ifName);
+void config_dnsmasq(const char *containerName, const char *ifName, const char *listenAddress, const char *dhcpRange);
+void config_nat(const char *containerName, const char *listenAddress);
+void create_nat_layer(const char *containerName, const char *bridgeName, const char *ifName, const char *listenAddress, char *dhcpRange);
+void destroy_nat_layer(const char *containerName, const char *bridgeName);
+void incoming_firewall_ipv4(const char *packetType, int portNumber);
+void incoming_firewall_ipv6(const char *packetType, int portNumber);
+void outgoing_firewall_ipv4(const char *packetType, int portNumber);
+void outgoing_firewall_ipv6(const char *packetType, int portNumber);
+
 #endif // COMMON_HANDLERS_H
index 263cbf976ddd878da19d27be9c956ce0ee821095..d96b1a3715c4b987389f26dc1bf78deda1fa6a2c 100644 (file)
@@ -302,7 +302,7 @@ void destroy_containers(void) {
                        test_containers[i]->stop(test_containers[i]);
                        /* Destroy the Container */
                        test_containers[i]->destroy(test_containers[i]);
-                       /* call destroy_with_snapshots() in case destroy() fails
+                       /* Call destroy_with_snapshots() in case destroy() fails
                            one of these two calls will always succeed */
                        test_containers[i]->destroy_with_snapshots(test_containers[i]);
                }
@@ -356,7 +356,7 @@ void node_sim_in_container(const char *node, const char *device_class, const cha
        PRINT_TEST_CASE_MSG("node_sim_%s started in Container\n", node);
 }
 
-/* Run the node_sim_<nodename> program inside the 'node''s container with event handling capable*/
+/* Run the node_sim_<nodename> program inside the 'node''s container with event handling capable */
 void node_sim_in_container_event(const char *node, const char *device_class,
                                  const char *invite_url, const char *clientId, const char *import) {
        char node_sim_command[200];
@@ -368,8 +368,6 @@ void node_sim_in_container_event(const char *node, const char *device_class,
        run_in_container(node_sim_command, node, true);
        PRINT_TEST_CASE_MSG("node_sim_%s(Client Id :%s) started in Container with event handling\n",
                            node, clientId);
-       PRINT_TEST_CASE_MSG("node_sim_%s mesh event import string : %s\n",
-                           node, import);
 }
 
 /* Run the node_step.sh script inside the 'node''s container to send the 'sig' signal to the
@@ -453,3 +451,443 @@ void change_ip(int node) {
        PRINT_TEST_CASE_MSG("Node '%s' IP Address changed to %s\n", state_ptr->node_names[node],
                            container_ips[node]);
 }
+
+/* Return container's IP address */
+char *get_container_ip(const char *node_name) {
+       char *ip;
+       int n, node = -1, i;
+
+       for(i = 0; i < state_ptr->num_nodes; i++) {
+               if(!strcasecmp(state_ptr->node_names[i], node_name)) {
+                       node = i;
+                       break;
+               }
+       }
+
+       if(i == state_ptr->num_nodes) {
+               return NULL;
+       }
+
+       n = strlen(container_ips[node]) + 1;
+       ip = malloc(n);
+       assert(ip);
+       strncpy(ip, container_ips[node], n);
+
+       return ip;
+}
+
+/* Install an app in a container */
+void install_in_container(const char *node, const char *app) {
+       char install_cmd[100];
+
+       assert(snprintf(install_cmd, sizeof(install_cmd),
+                       "apt-get install %s -y >> /dev/null", app) >= 0);
+       char *ret = run_in_container(install_cmd, node, false);
+       // TODO: Check in container whether app has installed or not with a timeout
+       sleep(10);
+}
+
+/* Simulate a network failure by adding NAT rule in the container with it's IP address */
+void block_node_ip(const char *node) {
+       char block_cmd[100];
+       char *node_ip;
+
+       node_ip = get_container_ip(node);
+       assert(node_ip);
+       assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A OUTPUT -p all -s %s -j DROP", node_ip) >= 0);
+       run_in_container(block_cmd, node, false);
+
+       assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A INPUT -p all -s %s -j DROP", node_ip) >= 0);
+       run_in_container(block_cmd, node, false);
+
+       assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A FORWARD -p all -s %s -j DROP", node_ip) >= 0);
+       run_in_container(block_cmd, node, false);
+
+       free(node_ip);
+}
+
+void accept_port_rule(const char *node, const char *chain, const char *protocol, int port) {
+       char block_cmd[100];
+
+       assert(port >= 0 && port < 65536);
+       assert(!strcmp(chain, "INPUT") || !strcmp(chain, "FORWARD") || !strcmp(chain, "OUTPUT"));
+       assert(!strcmp(protocol, "all") || !strcmp(protocol, "tcp") || !strcmp(protocol, "udp"));
+       assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A %s -p %s --dport %d -j ACCEPT", chain, protocol, port) >= 0);
+       run_in_container(block_cmd, node, false);
+}
+
+/* Restore the network that was blocked before by the NAT rule with it's own IP address */
+void unblock_node_ip(const char *node) {
+       char unblock_cmd[100];
+       char *node_ip;
+
+       node_ip = get_container_ip(node);
+       assert(node_ip);
+       assert(snprintf(unblock_cmd, sizeof(unblock_cmd), "iptables -D OUTPUT -p all -s %s -j DROP", node_ip) >= 0);
+       run_in_container(unblock_cmd, node, false);
+
+       assert(snprintf(unblock_cmd, sizeof(unblock_cmd), "iptables -D INPUT -p all -s %s -j DROP", node_ip) >= 0);
+       run_in_container(unblock_cmd, node, false);
+
+       assert(snprintf(unblock_cmd, sizeof(unblock_cmd), "iptables -D FORWARD -p all -s %s -j DROP", node_ip) >= 0);
+       run_in_container(unblock_cmd, node, false);
+}
+
+/* Takes bridgeName as input parameter and creates a bridge */
+void create_bridge(const char *bridgeName) {
+       char command[100] = "brctl addbr ";
+       strcat(command, bridgeName);
+       int create_bridge_status = system(command);
+       assert(create_bridge_status == 0);
+       PRINT_TEST_CASE_MSG("%s bridge created\n", bridgeName);
+}
+
+/* Add interface for the bridge created */
+void add_interface(const char *bridgeName, const char *interfaceName) {
+       char command[100] = "brctl addif ";
+       char cmd[100] = "dhclient ";
+
+       strcat(command, bridgeName);
+       strcat(command, " ");
+       strcat(command, interfaceName);
+       int addif_status = system(command);
+       assert(addif_status == 0);
+       strcat(cmd, bridgeName);
+       int dhclient_status = system(cmd);
+       assert(dhclient_status == 0);
+       PRINT_TEST_CASE_MSG("Added interface for %s\n", bridgeName);
+}
+
+/* Create a veth pair and bring them up */
+void add_veth_pair(const char *vethName1, const char *vethName2) {
+       char command[100] = "ip link add ";
+       char upCommand1[100] = "ip link set ";
+       char upCommand2[100] = "ip link set ";
+
+       strcat(command, vethName1);
+       strcat(command, " type veth peer name ");
+       strcat(command, vethName2);
+       int link_add_status = system(command);
+       assert(link_add_status == 0);
+       strcat(upCommand1, vethName1);
+       strcat(upCommand1, " up");
+       int link_set_veth1_status = system(upCommand1);
+       assert(link_set_veth1_status == 0);
+       strcat(upCommand2, vethName2);
+       strcat(upCommand2, " up");
+       int link_set_veth2_status = system(upCommand2);
+       assert(link_set_veth2_status == 0);
+       PRINT_TEST_CASE_MSG("Added veth pairs %s and %s\n", vethName1, vethName2);
+}
+
+/* Bring the interface up for the bridge created */
+void bring_if_up(const char *bridgeName) {
+       char command[300] = "ifconfig ";
+       char dhcommand[300] = "dhclient ";
+       strcat(command, bridgeName);
+       strcat(command, " up");
+       int if_up_status = system(command);
+       assert(if_up_status == 0);
+       sleep(2);
+       PRINT_TEST_CASE_MSG("Interface brought up for %s created\n", bridgeName);
+}
+
+/**
+ * Replace all occurrences of a given a word in string.
+ */
+void replaceAll(char *str, const char *oldWord, const char *newWord) {
+       char *pos, temp[BUFSIZ];
+       int index = 0;
+       int owlen;
+       owlen = strlen(oldWord);
+
+       while((pos = strstr(str, oldWord)) != NULL) {
+               strcpy(temp, str);
+               index = pos - str;
+               str[index] = '\0';
+               strcat(str, newWord);
+               strcat(str, temp + index + owlen);
+       }
+}
+
+/* Switches the bridge for a given container */
+void switch_bridge(const char *containerName, const char *currentBridge, const char *newBridge) {
+       char command[100] = "lxc-stop -n ";
+       char command_start[100] = "lxc-start -n ";
+       PRINT_TEST_CASE_MSG("Switching %s container to %s\n", containerName, newBridge);
+       strcat(command, containerName);
+       strcat(command_start, containerName);
+       int container_stop_status = system(command);
+       assert(container_stop_status == 0);
+       sleep(2);
+       FILE *fPtr;
+       FILE *fTemp;
+       char path[300] = "/var/lib/lxc/";
+       strcat(path, containerName);
+       strcat(path, "/config");
+
+       char buffer[BUFSIZ];
+       /*  Open all required files */
+       fPtr  = fopen(path, "r");
+       fTemp = fopen("replace.tmp", "w");
+
+       if(fPtr == NULL || fTemp == NULL) {
+               PRINT_TEST_CASE_MSG("\nUnable to open file.\n");
+               PRINT_TEST_CASE_MSG("Please check whether file exists and you have read/write privilege.\n");
+               exit(EXIT_SUCCESS);
+       }
+
+       while((fgets(buffer, BUFSIZ, fPtr)) != NULL) {
+               replaceAll(buffer, currentBridge, newBridge);
+               fputs(buffer, fTemp);
+       }
+
+       fclose(fPtr);
+       fclose(fTemp);
+       remove(path);
+       rename("replace.tmp", path);
+       PRINT_TEST_CASE_MSG("Switching procedure done successfully\n");
+       int container_start_status = system(command_start);
+       assert(container_start_status == 0);
+       sleep(2);
+}
+
+/* Bring the interface down for the bridge created */
+void bring_if_down(const char *bridgeName) {
+       char command[300] = "ip link set dev ";
+       strcat(command, bridgeName);
+       strcat(command, " down");
+       int if_down_status = system(command);
+       assert(if_down_status == 0);
+       PRINT_TEST_CASE_MSG("Interface brought down for %s created\n", bridgeName);
+}
+
+/* Delete interface for the bridge created */
+void del_interface(const char *bridgeName, const char *interfaceName) {
+       char command[300] = "brctl delif ";
+       strcat(command, bridgeName);
+       strcat(command, interfaceName);
+       int if_delete_status = system(command);
+       assert(if_delete_status == 0);
+       PRINT_TEST_CASE_MSG("Deleted interface for %s\n", bridgeName);
+}
+
+/* Takes bridgeName as input parameter and deletes a bridge */
+void delete_bridge(const char *bridgeName) {
+       bring_if_down(bridgeName);
+       char command[300] = "brctl delbr ";
+       strcat(command, bridgeName);
+       int bridge_delete = system(command);
+       assert(bridge_delete == 0);
+       PRINT_TEST_CASE_MSG("%s bridge deleted\n", bridgeName);
+       sleep(2);
+}
+
+/* Creates container on a specified bridge with added interface */
+void create_container_on_bridge(const char *containerName, const char *bridgeName, const char *ifName) {
+       char command[100] = "lxc-create -t download -n ";
+       char cmd[100] = " -- -d ubuntu -r trusty -a ";
+       char start[100] = "lxc-start -n ";
+       FILE *fPtr;
+       char path[300] = "/var/lib/lxc/";
+       strcat(path, containerName);
+       strcat(path, "/config");
+       strcat(command, containerName);
+       strcat(command, cmd);
+       strcat(command, choose_arch);
+       int container_create_status = system(command);
+       assert(container_create_status == 0);
+       sleep(3);
+       assert(fPtr = fopen(path, "a+"));
+       fprintf(fPtr, "lxc.net.0.name = eth0\n");
+       fprintf(fPtr, "\n");
+       fprintf(fPtr, "lxc.net.1.type = veth\n");
+       fprintf(fPtr, "lxc.net.1.flags = up\n");
+       fprintf(fPtr, "lxc.net.1.link = %s\n", bridgeName);
+       fprintf(fPtr, "lxc.net.1.name = %s\n", ifName);
+       fprintf(fPtr, "lxc.net.1.hwaddr = 00:16:3e:ab:xx:xx\n");
+       fclose(fPtr);
+       strcat(start, containerName);
+       int container_start_status = system(start);
+       assert(container_start_status == 0);
+       sleep(3);
+       PRINT_TEST_CASE_MSG("Created %s on %s with interface name %s\n", containerName, bridgeName, ifName);
+}
+
+/* Configures dnsmasq and iptables for the specified container with inputs of listen address and dhcp range */
+void config_dnsmasq(const char *containerName, const char *ifName, const char *listenAddress, const char *dhcpRange) {
+       char command[500] = "echo \"apt-get install dnsmasq iptables -y\" | lxc-attach -n ";
+       strcat(command, containerName);
+       strcat(command, " --");
+       int iptables_install_status = system(command);
+       assert(iptables_install_status == 0);
+       sleep(5);
+       char com1[300] = "echo \"echo \"interface=eth1\" >> /etc/dnsmasq.conf\" | lxc-attach -n ";
+       strcat(com1, containerName);
+       strcat(com1, " --");
+       int dnsmasq_status = system(com1);
+       assert(dnsmasq_status == 0);
+       sleep(5);
+       char com2[300] = "echo \"echo \"bind-interfaces\" >> /etc/dnsmasq.conf\" | lxc-attach -n ";
+       strcat(com2, containerName);
+       strcat(com2, " --");
+       dnsmasq_status = system(com2);
+       assert(dnsmasq_status == 0);
+       sleep(5);
+       char com3[300] = "echo \"echo \"listen-address=";
+       strcat(com3, listenAddress);
+       strcat(com3, "\" >> /etc/dnsmasq.conf\" | lxc-attach -n ");
+       strcat(com3, containerName);
+       strcat(com3, " --");
+       dnsmasq_status = system(com3);
+       assert(dnsmasq_status == 0);
+       sleep(5);
+       char com4[300] = "echo \"echo \"dhcp-range=";
+       strcat(com4, dhcpRange);
+       strcat(com4, "\" >> /etc/dnsmasq.conf\" | lxc-attach -n ");
+       strcat(com4, containerName);
+       strcat(com4, " --");
+       dnsmasq_status = system(com4);
+       assert(dnsmasq_status == 0);
+       sleep(5);
+       char cmd[300] = "echo \"ifconfig ";
+       strcat(cmd, ifName);
+       strcat(cmd, " ");
+       strcat(cmd, listenAddress);
+       strcat(cmd, " netmask 255.255.255.0 up\" | lxc-attach -n ");
+       strcat(cmd, containerName);
+       strcat(cmd, " --");
+       dnsmasq_status = system(cmd);
+       assert(dnsmasq_status == 0);
+       sleep(2);
+       char com[500] = "echo \"service dnsmasq restart >> /dev/null\" | lxc-attach -n ";
+       strcat(com, containerName);
+       strcat(com, " --");
+       dnsmasq_status = system(com);
+       assert(dnsmasq_status == 0);
+       sleep(2);
+       PRINT_TEST_CASE_MSG("Configured dnsmasq in %s with interface name %s, listen-address = %s, dhcp-range = %s\n", containerName, ifName, listenAddress, dhcpRange);
+}
+
+/* Configure the NAT rules inside the container */
+void config_nat(const char *containerName, const char *listenAddress) {
+       char *last_dot_in_ip;
+       int last_ip_byte = 0;
+       char new_ip[300] = {0};
+       strncpy(new_ip, listenAddress, sizeof(new_ip));
+       assert(last_dot_in_ip = strrchr(new_ip, '.'));
+       assert(snprintf(last_dot_in_ip + 1, 4, "%d", last_ip_byte) >= 0);
+       char comd[300] = "echo \"iptables -t nat -A POSTROUTING -s ";
+       strcat(comd, new_ip);
+       strcat(comd, "/24 ! -d ");
+       strcat(comd, new_ip);
+       strcat(comd, "/24 -j MASQUERADE\" | lxc-attach -n ");
+       strcat(comd, containerName);
+       strcat(comd, " --");
+       int conf_nat_status = system(comd);
+       assert(conf_nat_status == 0);
+       sleep(2);
+       PRINT_TEST_CASE_MSG("Configured NAT on %s\n", containerName);
+}
+
+/* Creates a NAT layer on a specified bridge with certain dhcp range to allocate ips for nodes */
+void create_nat_layer(const char *containerName, const char *bridgeName, const char *ifName, const char *listenAddress, char *dhcpRange) {
+       create_bridge(bridgeName);
+       bring_if_up(bridgeName);
+       create_container_on_bridge(containerName, bridgeName, ifName);
+       config_dnsmasq(containerName, ifName, listenAddress, dhcpRange);
+       config_nat(containerName, listenAddress);
+       PRINT_TEST_CASE_MSG("NAT layer created with %s\n", containerName);
+}
+
+/* Destroys the NAT layer created */
+void destroy_nat_layer(const char *containerName, const char *bridgeName) {
+       bring_if_down(bridgeName);
+       delete_bridge(bridgeName);
+       char command[100] = "lxc-stop -n ";
+       strcat(command, containerName);
+       int container_stop_status = system(command);
+       assert(container_stop_status == 0);
+       char destroy[100] = "lxc-destroy -n ";
+       strcat(destroy, containerName);
+       strcat(destroy, " -s");
+       int container_destroy_status = system(destroy);
+       assert(container_destroy_status == 0);
+       PRINT_TEST_CASE_MSG("NAT layer destroyed with %s\n", containerName);
+}
+
+/* Add incoming firewall rules for ipv4 addresses with packet type and port number */
+void incoming_firewall_ipv4(const char *packetType, int portNumber) {
+       char buf[5];
+       snprintf(buf, sizeof(buf), "%d", portNumber);
+       assert(system("iptables -F") == 0);
+       assert(system("iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") == 0);
+       assert(system("iptables -A INPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT") == 0);
+       char command[100] = "iptables -A INPUT -p ";
+       strcat(command, packetType);
+       strcat(command, " --dport ");
+       strcat(command, buf);
+       strcat(command, " -j ACCEPT");
+       assert(system(command) == 0);
+       sleep(2);
+       assert(system("iptables -A INPUT -j DROP") == 0);
+       PRINT_TEST_CASE_MSG("Firewall for incoming requests added on IPv4");
+       assert(system("iptables -L") == 0);
+}
+
+/* Add incoming firewall rules for ipv6 addresses with packet type and port number */
+void incoming_firewall_ipv6(const char *packetType, int portNumber) {
+       char buf[5];
+       snprintf(buf, sizeof(buf), "%d", portNumber);
+       assert(system("ip6tables -F") == 0);
+       assert(system("ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") == 0);
+       assert(system("ip6tables -A INPUT -s ::1 -d ::1 -j ACCEPT") == 0);
+       char command[100] = "ip6tables -A INPUT -p ";
+       strcat(command, packetType);
+       strcat(command, " --dport ");
+       strcat(command, buf);
+       strcat(command, " -j ACCEPT");
+       assert(system(command) == 0);
+       sleep(2);
+       assert(system("ip6tables -A INPUT -j DROP") == 0);
+       PRINT_TEST_CASE_MSG("Firewall for incoming requests added on IPv6");
+       assert(system("ip6tables -L") == 0);
+}
+
+/* Add outgoing firewall rules for ipv4 addresses with packet type and port number */
+void outgoing_firewall_ipv4(const char *packetType, int portNumber) {
+       char buf[5];
+       snprintf(buf, sizeof(buf), "%d", portNumber);
+       assert(system("iptables -F") == 0);
+       assert(system("iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") == 0);
+       assert(system("iptables -A OUTPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT") == 0);
+       char command[100] = "iptables -A OUTPUT -p ";
+       strcat(command, packetType);
+       strcat(command, " --dport ");
+       strcat(command, buf);
+       strcat(command, " -j ACCEPT");
+       assert(system(command) == 0);
+       sleep(2);
+       assert(system("iptables -A OUTPUT -j DROP") == 0);
+       PRINT_TEST_CASE_MSG("Firewall for outgoing requests added on IPv4");
+       assert(system("iptables -L") == 0);
+}
+
+/* Add outgoing firewall rules for ipv6 addresses with packet type and port number */
+void outgoing_firewall_ipv6(const char *packetType, int portNumber) {
+       char buf[5];
+       snprintf(buf, sizeof(buf), "%d", portNumber);
+       assert(system("ip6tables -F") == 0);
+       assert(system("ip6tables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") == 0);
+       assert(system("ip6tables -A OUTPUT -s ::1 -d ::1 -j ACCEPT") == 0);
+       char command[100] = "ip6tables -A OUTPUT -p ";
+       strcat(command, packetType);
+       strcat(command, " --dport ");
+       strcat(command, buf);
+       strcat(command, " -j ACCEPT");
+       assert(system(command) == 0);
+       sleep(2);
+       assert(system("ip6tables -A OUTPUT -j DROP") == 0);
+       PRINT_TEST_CASE_MSG("Firewall for outgoing requests added on IPv6");
+       assert(system("ip6tables -L") == 0);
+}
index 49cbb7c3c4d21e5d48196b9b0a87be7000b35050..66b6c51dfedf55c8cfdb220cf818e53e850daf44 100644 (file)
 
 extern char *lxc_path;
 
-struct lxc_container *find_container(const char *name);
-void rename_container(const char *old_name, const char *new_name);
-char *run_in_container(const char *cmd, const char *node, bool daemonize);
-void container_wait_ip(int node);
-void create_containers(const char *node_names[], int num_nodes);
-void setup_containers(void **state);
-void destroy_containers(void);
-void restart_all_containers(void);
-char *invite_in_container(const char *inviter, const char *invitee);
-void node_sim_in_container(const char *node, const char *device_class, const char *invite_url);
-void node_sim_in_container_event(const char *node, const char *device_class,
-                                 const char *invite_url, const char *clientId, const char *import);
-void node_step_in_container(const char *node, const char *sig);
-void change_ip(int node);
-
-char *get_container_ip(int node);
+extern struct lxc_container *find_container(const char *name);
+extern void rename_container(const char *old_name, const char *new_name);
+extern char *run_in_container(const char *cmd, const char *node, bool daemonize);
+extern void container_wait_ip(int node);
+extern void create_containers(const char *node_names[], int num_nodes);
+extern void setup_containers(void **state);
+extern void destroy_containers(void);
+extern void restart_all_containers(void);
+extern char *invite_in_container(const char *inviter, const char *invitee);
+extern void node_sim_in_container(const char *node, const char *device_class, const char *invite_url);
+extern void node_sim_in_container_event(const char *node, const char *device_class,
+                                        const char *invite_url, const char *clientId, const char *import);
+extern void node_step_in_container(const char *node, const char *sig);
+extern void change_ip(int node);
+
+extern char *get_container_ip(const char *node_name);
+extern void install_in_container(const char *node, const char *app);
+extern void unblock_node_ip(const char *node);
+extern void block_node_ip(const char *node);
+void accept_port_rule(const char *node, const char *chain, const char *protocol, int port);
 
 #endif // CONTAINERS_H
index defd4b5782ae56d5e38a15e631fd347ea9765c1a..5b6877dfa2504072b9fb902ad81ccded8712199d 100644 (file)
@@ -44,7 +44,8 @@ run_blackbox_tests_SOURCES = \
        test_cases_channel_open.c \
        test_cases_channel_close.c \
        test_cases_channel_send.c \
-       test_cases_channel_shutdown.c
+       test_cases_channel_shutdown.c \
+       test_cases_channel_conn.c
 
 run_blackbox_tests_LDADD = ../../../src/libmeshlink.la -llxc -lcmocka
 run_blackbox_tests_CFLAGS = -D_GNU_SOURCE
index 2f9730e13dc76e3291626e839b27fdc643be2d34..b9fd1fa91a75637e2d4b8ad1349cd7e52caea94d 100644 (file)
@@ -35,6 +35,7 @@ int setup_test(void **state) {
 
        for(i = 0; i < state_ptr->num_nodes; i++) {
                meta_conn_status[i] = false;
+               node_reachable_status[i] = false;
        }
 
        setup_containers(state);
index 3f6e081a77a1825c84e38cb6175746ce084eb2f8..691ac21534927231fbd75007d80cf1ba5ff094e3 100644 (file)
@@ -62,6 +62,9 @@
 #include "test_cases_channel_set_poll_cb.h"
 #include "test_cases_channel_set_receive_cb.h"
 #include "test_cases_hint_address.h"
+
+#include "test_cases_channel_conn.h"
+
 #include "../common/containers.h"
 #include "../common/common_handlers.h"
 
@@ -120,6 +123,8 @@ int main(int argc, char *argv[]) {
        failed_tests += test_meshlink_channel_open();
        failed_tests += test_meshlink_channel_close();
 
+       failed_tests += test_meshlink_channel_conn();
+
        printf("[ PASSED ] %d test(s).\n", total_tests - failed_tests);
        printf("[ FAILED ] %d test(s).\n", failed_tests);
 
diff --git a/test/blackbox/run_blackbox_tests/test_cases_channel_conn.c b/test/blackbox/run_blackbox_tests/test_cases_channel_conn.c
new file mode 100644 (file)
index 0000000..4bccf88
--- /dev/null
@@ -0,0 +1,767 @@
+/*
+    test_cases_channel_conn.c -- Execution of specific meshlink black box test cases
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <setjmp.h>
+#include <pthread.h>
+#include <cmocka.h>
+#include "execute_tests.h"
+#include "test_cases_channel_conn.h"
+#include "../common/containers.h"
+#include "../common/test_step.h"
+#include "../common/common_handlers.h"
+#include "../common/mesh_event_handler.h"
+
+#define PEER_ID "0"
+#define NUT_ID  "1"
+#define RELAY_ID  "2"
+
+static void test_case_channel_conn_01(void **state);
+static bool test_steps_channel_conn_01(void);
+static void test_case_channel_conn_02(void **state);
+static bool test_steps_channel_conn_02(void);
+static void test_case_channel_conn_03(void **state);
+static bool test_steps_channel_conn_03(void);
+static void test_case_channel_conn_04(void **state);
+static bool test_steps_channel_conn_04(void);
+static void test_case_channel_conn_05(void **state);
+static bool test_steps_channel_conn_05(void);
+static void test_case_channel_conn_06(void **state);
+static bool test_steps_channel_conn_06(void);
+static void test_case_channel_conn_07(void **state);
+static bool test_steps_channel_conn_07(void);
+static void test_case_channel_conn_08(void **state);
+static bool test_steps_channel_conn_08(void);
+
+static char *test_channel_conn_2_nodes[] = { "peer", "nut" };
+static char *test_channel_conn_3_nodes[] = { "peer", "nut", "relay" };
+
+static black_box_state_t test_case_channel_conn_01_state = {
+       .test_case_name = "test_case_channel_conn_01",
+       .node_names = test_channel_conn_2_nodes,
+       .num_nodes = 2,
+};
+static black_box_state_t test_case_channel_conn_02_state = {
+       .test_case_name = "test_case_channel_conn_02",
+       .node_names = test_channel_conn_2_nodes,
+       .num_nodes = 2,
+};
+static black_box_state_t test_case_channel_conn_03_state = {
+       .test_case_name = "test_case_channel_conn_03",
+       .node_names = test_channel_conn_2_nodes,
+       .num_nodes = 2,
+};
+static black_box_state_t test_case_channel_conn_04_state = {
+       .test_case_name = "test_case_channel_conn_04",
+       .node_names = test_channel_conn_2_nodes,
+       .num_nodes = 2,
+};
+static black_box_state_t test_case_channel_conn_05_state = {
+       .test_case_name = "test_case_channel_conn_05",
+       .node_names = test_channel_conn_3_nodes,
+       .num_nodes = 3,
+};
+static black_box_state_t test_case_channel_conn_06_state = {
+       .test_case_name = "test_case_channel_conn_06",
+       .node_names = test_channel_conn_3_nodes,
+       .num_nodes = 3,
+};
+static black_box_state_t test_case_channel_conn_07_state = {
+       .test_case_name = "test_case_channel_conn_07",
+       .node_names = test_channel_conn_3_nodes,
+       .num_nodes = 3,
+};
+static black_box_state_t test_case_channel_conn_08_state = {
+       .test_case_name = "test_case_channel_conn_08",
+       .node_names = test_channel_conn_3_nodes,
+       .num_nodes = 3,
+};
+
+static bool joined;
+static bool channel_opened;
+static bool node_restarted;
+static bool received_error;
+static bool channel_received;
+static bool node_reachable;
+static bool node_unreachable;
+
+/* Callback function for handling channel connection test cases mesh events */
+static void channel_conn_cb(mesh_event_payload_t payload) {
+       switch(payload.mesh_event) {
+       case NODE_JOINED            :
+               joined = true;
+               break;
+
+       case CHANNEL_OPENED         :
+               channel_opened = true;
+               break;
+
+       case NODE_RESTARTED         :
+               node_restarted = true;
+               break;
+
+       case ERR_NETWORK            :
+               received_error = true;
+               break;
+
+       case CHANNEL_DATA_RECIEVED  :
+               channel_received = true;
+               break;
+
+       case NODE_UNREACHABLE       :
+               node_unreachable = true;
+               break;
+
+       case NODE_REACHABLE         :
+               node_reachable = true;
+               break;
+
+       default                     :
+               PRINT_TEST_CASE_MSG("Undefined event occurred\n");
+       }
+
+       return;
+}
+
+/* Execute channel connections Test Case # 1 - simulate a temporary network
+    failure of about 30 seconds, messages sent while the network was down
+    should be received by the other side after the network comes up again. */
+static void test_case_channel_conn_01(void **state) {
+       execute_test(test_steps_channel_conn_01, state);
+       return;
+}
+
+/* Test Steps for channel connections Test Case # 1
+
+    Test Steps:
+    1. Run NUT & peer node instances and open a channel between them
+    2. Simulate a network failure in NUT's container for about 30 secs,
+        meanwhile send data via channel from NUT to peer.
+    3. After restoring network, peer node receive's data via channel.
+
+    Expected Result:
+    Peer node receives data via channel without any error after restoring network.
+*/
+static bool test_steps_channel_conn_01(void) {
+       char *invite_nut;
+       char *import;
+
+       joined = false;
+       channel_opened = false;
+       channel_received = false;
+
+       // Setup Containers
+
+       install_in_container("nut", "iptables");
+       accept_port_rule("nut", "OUTPUT", "udp", 9000);
+       import = mesh_event_sock_create(eth_if_name);
+       invite_nut = invite_in_container("peer", "nut");
+       assert(invite_nut);
+
+       // Run node instances in containers & open a channel
+
+       node_sim_in_container_event("peer", "1", NULL, PEER_ID, import);
+       node_sim_in_container_event("nut", "1", invite_nut, NUT_ID, import);
+
+       wait_for_event(channel_conn_cb, 30);
+       assert_int_equal(joined, true);
+
+       wait_for_event(channel_conn_cb, 30);
+       assert_int_equal(channel_opened, true);
+
+       // Simulate network failure in NUT's LXC container with it's IP address as NAT rule
+
+       block_node_ip("nut");
+       sleep(2);
+
+       // Sending SIGUSR1 signal to node-under-test indicating the network failure
+
+       node_step_in_container("nut", "SIGUSR1");
+       sleep(30);
+
+       // Restore NUT's network
+
+       unblock_node_ip("nut");
+
+       // Wait for peer node to receive data via channel from NUT
+
+       wait_for_event(channel_conn_cb, 60);
+       assert_int_equal(channel_received, true);
+
+       free(invite_nut);
+       free(import);
+       return true;
+}
+
+/* Execute channel connections Test Case # 2 - a simulated network failure
+    of more than 1 minute, and sending messages over the channel during the
+    failure. Then after about 1 minute, the channel should receive an error */
+static void test_case_channel_conn_02(void **state) {
+       execute_test(test_steps_channel_conn_02, state);
+       return;
+}
+
+/* Test Steps for channel connections Test Case # 2
+
+    Test Steps:
+    1. Run NUT and peer node instances in containers and open a channel between them.
+    2. Create a network failure for about 90 secs in NUT container
+        and signal NUT node about the network failure.
+    3. Meanwhile NUT sends data to peer via channel and restore the network after
+        90 secs.
+
+    Expected Result:
+      Peer node should receive error closing the channel after channel timeout(60 secs).
+*/
+static bool test_steps_channel_conn_02(void) {
+       char *invite_nut;
+       char *import;
+
+       joined = false;
+       channel_opened = false;
+       received_error = false;
+
+       // Setup containers
+
+       install_in_container("nut", "iptables");
+       accept_port_rule("nut", "OUTPUT", "udp", 9000);
+       import = mesh_event_sock_create(eth_if_name);
+       invite_nut = invite_in_container("peer", "nut");
+       assert(invite_nut);
+
+       // Run NUT and peer node instances in containers & open a channel
+
+       node_sim_in_container_event("peer", "1", NULL, PEER_ID, import);
+       node_sim_in_container_event("nut", "1", invite_nut, NUT_ID, import);
+
+       wait_for_event(channel_conn_cb, 30);
+       assert_int_equal(joined, true);
+
+       wait_for_event(channel_conn_cb, 10);
+       assert_int_equal(channel_opened, true);
+
+       // Simulate network failure in NUT's LXC container with it's IP address as NAT rule
+
+       block_node_ip("nut");
+
+       // Sending SIGUSR1 signal to node-under-test indicating the network failure
+
+       node_step_in_container("nut", "SIGUSR1");
+       sleep(90);
+
+       // Restore NUT containers network after 90 secs
+
+       unblock_node_ip("nut");
+
+       // Wait for peer node to send the event about the channel error occurred with length = 0
+
+       wait_for_event(channel_conn_cb, 90);
+       assert_int_equal(received_error, true);
+
+       free(invite_nut);
+       free(import);
+       return true;
+}
+
+/* Execute channel connections Test Case # 3 - a simulated network failure
+    once node instance is made offline restore the network and send data via
+    channel  */
+static void test_case_channel_conn_03(void **state) {
+       execute_test(test_steps_channel_conn_03, state);
+       return;
+}
+
+/* Test Steps for channel connections Test Case # 3
+
+    Test Steps:
+    1. Run NUT and peer node instances and open a channel between them.
+    2. Create a network failure in NUT container, bring NUT node offline
+        and receive the status at test driver and restore the network
+    3. After peer node instance is reachable to NUT node send data via channel
+
+    Expected Result:
+    Peer node should receive data from NUT without any error.
+*/
+static bool test_steps_channel_conn_03(void) {
+       char *invite_nut;
+       char *import;
+
+       joined = false;
+       channel_opened = false;
+       node_unreachable = false;
+       node_reachable = false;
+       channel_received = false;
+
+       // Setup containers
+
+       install_in_container("nut", "iptables");
+       accept_port_rule("nut", "OUTPUT", "udp", 9000);
+       import = mesh_event_sock_create(eth_if_name);
+       invite_nut = invite_in_container("peer", "nut");
+       assert(invite_nut);
+
+       // Run NUT and peer node instances in containers & open a channel
+
+       node_sim_in_container_event("peer", "1", NULL, PEER_ID, import);
+       node_sim_in_container_event("nut", "1", invite_nut, NUT_ID, import);
+
+       wait_for_event(channel_conn_cb, 30);
+       assert_int_equal(joined, true);
+
+       wait_for_event(channel_conn_cb, 10);
+       assert_int_equal(channel_opened, true);
+
+       // Simulate network failure in NUT's LXC container with it's IP address as NAT rule
+
+       node_reachable = false;
+       block_node_ip("nut");
+
+       // Sending SIGUSR1 signal to node-under-test indicating the network failure
+
+       node_step_in_container("nut", "SIGUSR1");
+
+       // Wait for the node status to become unreachable
+
+       wait_for_event(channel_conn_cb, 100);
+       assert_int_equal(node_unreachable, true);
+
+       // Restore NUT container's network
+
+       unblock_node_ip("nut");
+
+       // Wait for the node status to become reachable
+
+       wait_for_event(channel_conn_cb, 100);
+       assert_int_equal(node_reachable, true);
+
+       // Wait for data to be received at peer via channel from NUT after restoring n/w
+
+       wait_for_event(channel_conn_cb, 90);
+       assert_int_equal(channel_received, true);
+
+       free(invite_nut);
+       free(import);
+       return true;
+}
+
+/* Execute channel connections Test Case # 4 - receiving an error when node-under-test
+    tries to send data on channel to peer node after peer node stops and starts the
+    node instance */
+static void test_case_channel_conn_04(void **state) {
+       execute_test(test_steps_channel_conn_04, state);
+       return;
+}
+
+/* Test Steps for Meta-connections Test Case # 4
+
+    Test Steps:
+    1. Run peer and NUT node instances in containers and open a channel between them.
+    2. Stop and start the NUT node instance and wait for about > 60 secs.
+    3. Send data via channel from Peer node and wait for event in test driver.
+
+    Expected Result:
+    Peer node should receive error(as length = 0) in receive callback of peer node's instance.
+*/
+static bool test_steps_channel_conn_04(void) {
+       char *invite_nut;
+       char *import;
+
+       joined = false;
+       channel_opened = false;
+       node_restarted = false;
+       received_error = false;
+       import = mesh_event_sock_create(eth_if_name);
+       invite_nut = invite_in_container("peer", "nut");
+       assert(invite_nut);
+
+       // Run NUT and peer node instances in containers and open a channel
+
+       node_sim_in_container_event("peer", "1", NULL, PEER_ID, import);
+       node_sim_in_container_event("nut", "1", invite_nut, NUT_ID, import);
+
+       wait_for_event(channel_conn_cb, 10);
+       assert_int_equal(joined, true);
+
+       wait_for_event(channel_conn_cb, 10);
+       assert_int_equal(channel_opened, true);
+
+       // Wait for NUT node instance to stop and start
+
+       wait_for_event(channel_conn_cb, 60);
+       assert_int_equal(node_restarted, true);
+
+       sleep(60);
+
+       // After 1 min the channel between NUT and peer should result in error
+
+       wait_for_event(channel_conn_cb, 10);
+       assert_int_equal(received_error, true);
+
+       return true;
+}
+
+/* Execute channel connections Test Case # 5 - simulate a temporary network
+    failure of about 30 seconds, messages sent while the network was down
+    should be received by the other side after the network comes up again. */
+static void test_case_channel_conn_05(void **state) {
+       execute_test(test_steps_channel_conn_05, state);
+       return;
+}
+
+/* Test Steps for channel connections Test Case # 5
+
+    Test Steps:
+    1. Run NUT, relay & peer node instances with relay inviting NUT and peer
+        and open a channel between them
+    2. Simulate a network failure in NUT's container for about 30 secs,
+        meanwhile send data via channel from NUT to peer.
+    3. After restoring network, peer node receive's data via channel.
+
+    Expected Result:
+    Peer node receives data via channel without any error after restoring network.
+*/
+static bool test_steps_channel_conn_05(void) {
+       char *invite_nut, *invite_peer;
+       char *import;
+
+       joined = false;
+       channel_opened = false;
+       channel_received = false;
+
+       // Setup containers
+
+       install_in_container("nut", "iptables");
+       accept_port_rule("nut", "OUTPUT", "udp", 9000);
+       import = mesh_event_sock_create(eth_if_name);
+       invite_peer = invite_in_container("relay", "peer");
+       invite_nut = invite_in_container("relay", "nut");
+       assert(invite_nut);
+       assert(invite_peer);
+
+       // Run node instances and open a channel between NUT and peer nodes
+
+       node_sim_in_container_event("relay", "1", NULL, RELAY_ID, import);
+       node_sim_in_container_event("peer", "1", invite_peer, PEER_ID, import);
+       node_sim_in_container_event("nut", "1", invite_nut, NUT_ID, import);
+
+       wait_for_event(channel_conn_cb, 30);
+       assert_int_equal(joined, true);
+
+       wait_for_event(channel_conn_cb, 30);
+       assert_int_equal(channel_opened, true);
+
+       // Create a network failure in NUT node's container with it's IP address
+
+       block_node_ip("nut");
+
+       // Sending SIGUSR1 signal to node-under-test indicating the network failure
+
+       node_step_in_container("nut", "SIGUSR1");
+       sleep(30);
+
+       // Restore the network
+
+       unblock_node_ip("nut");
+
+       // Wait for peer to get data from NUT node via channel after restoring network in < 60 secs
+
+       wait_for_event(channel_conn_cb, 60);
+       assert_int_equal(channel_received, true);
+
+       free(invite_nut);
+       free(invite_peer);
+       free(import);
+       return true;
+}
+
+/* Execute channel connections Test Case # 6 - a simulated network failure
+    of more than 1 minute, and sending messages over the channel during the
+    failure. Then after about 1 minute, the channel should receive an error */
+static void test_case_channel_conn_06(void **state) {
+       execute_test(test_steps_channel_conn_06, state);
+       return;
+}
+
+/* Test Steps for channel connections Test Case # 6
+
+    Test Steps:
+    1. Run NUT, relay & peer node instances with relay inviting NUT and peer
+        and open a channel between them
+    2. Create a network failure for about 90 secs in NUT container
+        and signal NUT node about the network failure.
+    3. Meanwhile NUT sends data to peer via channel and restore the network after
+        90 secs.
+
+    Expected Result:
+      Peer node should receive error closing the channel after channel timeout(60 secs).
+*/
+static bool test_steps_channel_conn_06(void) {
+       char *invite_nut, *invite_peer;
+       char *import;
+
+       joined = false;
+       channel_opened = false;
+       received_error = false;
+
+       // Setup containers
+
+       install_in_container("nut", "iptables");
+       accept_port_rule("nut", "OUTPUT", "udp", 9000);
+       import = mesh_event_sock_create(eth_if_name);
+       invite_peer = invite_in_container("relay", "peer");
+       assert(invite_peer);
+       invite_nut = invite_in_container("relay", "nut");
+       assert(invite_nut);
+
+       // Run nodes in containers and open a channel between NUt and peer
+
+       node_sim_in_container_event("relay", "1", NULL, RELAY_ID, import);
+       node_sim_in_container_event("peer", "1", invite_peer, PEER_ID, import);
+       node_sim_in_container_event("nut", "1", invite_nut, NUT_ID, import);
+
+       wait_for_event(channel_conn_cb, 30);
+       assert_int_equal(joined, true);
+
+       wait_for_event(channel_conn_cb, 10);
+       assert_int_equal(channel_opened, true);
+
+       // Simulate a network failure in NUT's container for > 60 secs
+
+       block_node_ip("nut");
+
+       // Sending SIGUSR1 signal to node-under-test indicating the network failure
+
+       node_step_in_container("nut", "SIGUSR1");
+       sleep(90);
+
+       // Restore the network after 90 secs
+
+       unblock_node_ip("nut");
+
+       // Wait for channel to receive error and receive the event
+
+       wait_for_event(channel_conn_cb, 90);
+       assert_int_equal(received_error, true);
+
+       free(invite_nut);
+       free(invite_peer);
+       free(import);
+       return true;
+}
+
+/* Execute channel connections Test Case # 7 - a simulated network failure
+    once node instance is made offline restore the network and send data via
+    channel  */
+static void test_case_channel_conn_07(void **state) {
+       execute_test(test_steps_channel_conn_07, state);
+       return;
+}
+
+/* Test Steps for channel connections Test Case # 7
+
+    Test Steps:
+    1. Run NUT, relay & peer node instances with relay inviting NUT and peer
+        and open a channel between them
+    2. Create a network failure in NUT container, bring NUT node offline
+        and receive the status at test driver and restore the network
+    3. After peer node instance is reachable to NUT node send data via channel
+
+    Expected Result:
+    Peer node should receive data from NUT without any error.
+*/
+static bool test_steps_channel_conn_07(void) {
+       char *invite_nut, *invite_peer;
+       char *import;
+
+       joined = false;
+       channel_opened = false;
+       node_unreachable = false;
+       node_reachable = false;
+       channel_received = false;
+
+       // Setup containers
+
+       install_in_container("nut", "iptables");
+       accept_port_rule("nut", "OUTPUT", "udp", 9000);
+       import = mesh_event_sock_create(eth_if_name);
+       invite_peer = invite_in_container("relay", "peer");
+       invite_nut = invite_in_container("relay", "nut");
+       assert(invite_nut);
+       assert(invite_peer);
+
+       // Run nodes and open a channel
+
+       node_sim_in_container_event("relay", "1", NULL, RELAY_ID, import);
+       node_sim_in_container_event("peer", "1", invite_peer, PEER_ID, import);
+       node_sim_in_container_event("nut", "1", invite_nut, NUT_ID, import);
+
+       wait_for_event(channel_conn_cb, 30);
+       assert_int_equal(joined, true);
+
+       wait_for_event(channel_conn_cb, 15);
+       assert_int_equal(channel_opened, true);
+
+       // Simulate a network failure
+
+       node_reachable = false;
+       block_node_ip("nut");
+
+       // Sending SIGUSR1 signal to node-under-test indicating the network failure
+
+       node_step_in_container("nut", "SIGUSR1");
+
+       // Wait for node to become unreachable
+
+       wait_for_event(channel_conn_cb, 100);
+       assert_int_equal(node_unreachable, true);
+
+       // Restore the network
+
+       unblock_node_ip("nut");
+
+       // Wait for node to become reachable after restoring n/w
+
+       wait_for_event(channel_conn_cb, 100);
+       assert_int_equal(node_reachable, true);
+
+       // Wait for peer node to receive data via channel without any error
+
+       wait_for_event(channel_conn_cb, 90);
+       assert_int_equal(channel_received, true);
+
+       free(invite_nut);
+       free(invite_peer);
+       free(import);
+       return true;
+}
+
+/* Execute channel connections Test Case # 8 - receiving an error when node-under-test
+    tries to send data on channel to peer node after peer node stops and starts the
+    node instance */
+static void test_case_channel_conn_08(void **state) {
+       execute_test(test_steps_channel_conn_08, state);
+       return;
+}
+
+/* Test Steps for Meta-connections Test Case # 8
+
+    Test Steps:
+    1. Run NUT, relay & peer node instances with relay inviting NUT and peer
+        and open a channel between them
+    2. Stop and start the NUT node instance and wait for about > 60 secs.
+    3. Send data via channel from Peer node and wait for event in test driver.
+
+    Expected Result:
+    Peer node should receive error(as length = 0) in receive callback of peer node's instance.
+*/
+static bool test_steps_channel_conn_08(void) {
+       char *invite_nut, *invite_peer;
+       char *import;
+
+       joined = false;
+       channel_opened = false;
+       node_restarted = false;
+       received_error = false;
+
+       // Setup containers
+
+       import = mesh_event_sock_create(eth_if_name);
+       invite_peer = invite_in_container("relay", "peer");
+       invite_nut = invite_in_container("relay", "nut");
+       assert(invite_nut);
+       assert(invite_peer);
+
+       // Run nodes and open a channel between NUT and peer
+
+       node_sim_in_container_event("relay", "1", NULL, RELAY_ID, import);
+       node_sim_in_container_event("peer", "1", invite_peer, PEER_ID, import);
+       node_sim_in_container_event("nut", "1", invite_nut, NUT_ID, import);
+
+       wait_for_event(channel_conn_cb, 10);
+       assert_int_equal(joined, true);
+
+       wait_for_event(channel_conn_cb, 10);
+       assert_int_equal(channel_opened, true);
+
+       // Wait for NUT node to restart it's instance
+
+       wait_for_event(channel_conn_cb, 60);
+       assert_int_equal(node_restarted, true);
+
+       sleep(60);
+
+       // Signal peer to send data to NUT node via channel
+
+       node_step_in_container("peer", "SIGUSR1");
+
+       // Wait for peer to receive channel error
+
+       wait_for_event(channel_conn_cb, 10);
+       assert_int_equal(received_error, true);
+
+       free(invite_nut);
+       free(invite_peer);
+       free(import);
+       return true;
+}
+
+static int black_box_group_setup(void **state) {
+       const char *nodes[] = { "peer", "nut", "relay" };
+       int num_nodes = sizeof(nodes) / sizeof(nodes[0]);
+
+       printf("Creating Containers\n");
+       destroy_containers();
+       create_containers(nodes, num_nodes);
+
+       return 0;
+}
+
+static int black_box_group_teardown(void **state) {
+       printf("Destroying Containers\n");
+       destroy_containers();
+
+       return 0;
+}
+
+int test_meshlink_channel_conn(void) {
+       const struct CMUnitTest blackbox_group0_tests[] = {
+               cmocka_unit_test_prestate_setup_teardown(test_case_channel_conn_01, setup_test, teardown_test,
+               (void *)&test_case_channel_conn_01_state),
+               cmocka_unit_test_prestate_setup_teardown(test_case_channel_conn_02, setup_test, teardown_test,
+               (void *)&test_case_channel_conn_02_state),
+               cmocka_unit_test_prestate_setup_teardown(test_case_channel_conn_03, setup_test, teardown_test,
+               (void *)&test_case_channel_conn_03_state),
+               cmocka_unit_test_prestate_setup_teardown(test_case_channel_conn_04, setup_test, teardown_test,
+               (void *)&test_case_channel_conn_04_state),
+               cmocka_unit_test_prestate_setup_teardown(test_case_channel_conn_05, setup_test, teardown_test,
+               (void *)&test_case_channel_conn_05_state),
+               cmocka_unit_test_prestate_setup_teardown(test_case_channel_conn_06, setup_test, teardown_test,
+               (void *)&test_case_channel_conn_06_state),
+               cmocka_unit_test_prestate_setup_teardown(test_case_channel_conn_07, setup_test, teardown_test,
+               (void *)&test_case_channel_conn_07_state),
+               cmocka_unit_test_prestate_setup_teardown(test_case_channel_conn_08, setup_test, teardown_test,
+               (void *)&test_case_channel_conn_08_state)
+       };
+       total_tests += sizeof(blackbox_group0_tests) / sizeof(blackbox_group0_tests[0]);
+
+       return cmocka_run_group_tests(blackbox_group0_tests, black_box_group_setup, black_box_group_teardown);
+}
index 9efed2f4cc44cad274c064ea57d8de0d93c11bb5..f05e1655ff9b47665fa24d00281a8411cc64b8d5 100644 (file)
@@ -24,6 +24,6 @@
 #include <stdbool.h>
 
 extern int total_tests;
-extern int test_case_channel_conn(void);
+extern int test_meshlink_channel_conn(void);
 
 #endif // TEST_CASES_CHANNEL_CONN_H
diff --git a/test/blackbox/test_case_channel_conn_01/Makefile.am b/test/blackbox/test_case_channel_conn_01/Makefile.am
new file mode 100644 (file)
index 0000000..4cca831
--- /dev/null
@@ -0,0 +1,9 @@
+check_PROGRAMS = node_sim_peer node_sim_nut
+
+node_sim_peer_SOURCES = node_sim_peer.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_peer_LDADD = ../../../src/libmeshlink.la
+node_sim_peer_CFLAGS = -D_GNU_SOURCE
+
+node_sim_nut_SOURCES = node_sim_nut.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_nut_LDADD = ../../../src/libmeshlink.la
+node_sim_nut_CFLAGS = -D_GNU_SOURCE
diff --git a/test/blackbox/test_case_channel_conn_01/node_sim_nut.c b/test/blackbox/test_case_channel_conn_01/node_sim_nut.c
new file mode 100644 (file)
index 0000000..bded500
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+    node_sim_nut.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static int client_id = -1;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag sigusr_received = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag channel_received = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+
+static void send_event(mesh_event_t event);
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable);
+static void mesh_siguser1_signal_handler(int sig_num);
+
+static void mesh_siguser1_signal_handler(int sig_num) {
+       set_sync_flag(&sigusr_received, true);
+
+       return;
+}
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "peer") && reachable) {
+               set_sync_flag(&peer_reachable, true);
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       assert(meshlink_channel_send(mesh, channel, "test", 5) >= 0);
+       return;
+}
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       if(len == 0) {
+               send_event(ERR_NETWORK);
+               assert(false);
+       }
+
+       if(!strcmp(channel->node->name, "peer")) {
+               if(len == 5 && !memcmp(dat, "reply", 5)) {
+                       set_sync_flag(&channel_opened, true);
+               }
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       struct timespec timeout = {0};
+       int i;
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGUSR1, mesh_siguser1_signal_handler);
+
+       // Execute test steps
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 30));
+       send_event(NODE_JOINED);
+
+       // Open a channel to peer node
+
+       meshlink_node_t *peer_node = meshlink_get_node(mesh, "peer");
+       assert(peer_node);
+       meshlink_channel_t *channel = meshlink_channel_open(mesh, peer_node, CHANNEL_PORT,
+                                     channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+
+       assert(wait_sync_flag(&channel_opened, 10));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&sigusr_received, 10));
+
+       sleep(10);
+
+       assert(meshlink_channel_send(mesh, channel, "after", 6) >= 0);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+}
diff --git a/test/blackbox/test_case_channel_conn_01/node_sim_peer.c b/test/blackbox/test_case_channel_conn_01/node_sim_peer.c
new file mode 100644 (file)
index 0000000..7cc095d
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static int client_id = -1;
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       if(!strcmp(channel->node->name, "nut")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       (void)mesh;
+       (void)channel;
+       (void)dat;
+       (void)len;
+
+       if(len == 0) {
+               mesh_event_sock_send(client_id, ERR_NETWORK, NULL, 0);
+               assert(false);
+       }
+
+       if(!strcmp(channel->node->name, "nut")) {
+               if(!memcmp(dat, "test", 5)) {
+                       assert(meshlink_channel_send(mesh, channel, "reply", 5) >= 0);
+               } else if(!memcmp(dat, "after", 6)) {
+                       assert(mesh_event_sock_send(client_id, CHANNEL_DATA_RECIEVED, NULL, 0));
+               }
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+
+       // Run peer node instance
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
diff --git a/test/blackbox/test_case_channel_conn_02/Makefile.am b/test/blackbox/test_case_channel_conn_02/Makefile.am
new file mode 100644 (file)
index 0000000..4cca831
--- /dev/null
@@ -0,0 +1,9 @@
+check_PROGRAMS = node_sim_peer node_sim_nut
+
+node_sim_peer_SOURCES = node_sim_peer.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_peer_LDADD = ../../../src/libmeshlink.la
+node_sim_peer_CFLAGS = -D_GNU_SOURCE
+
+node_sim_nut_SOURCES = node_sim_nut.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_nut_LDADD = ../../../src/libmeshlink.la
+node_sim_nut_CFLAGS = -D_GNU_SOURCE
diff --git a/test/blackbox/test_case_channel_conn_02/node_sim_nut.c b/test/blackbox/test_case_channel_conn_02/node_sim_nut.c
new file mode 100644 (file)
index 0000000..d8af092
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+    node_sim_nut.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static int client_id = -1;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag channel_closed = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag sigusr_received = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+
+static void send_event(mesh_event_t event);
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable);
+static void mesh_siguser1_signal_handler(int sig_num);
+
+static void mesh_siguser1_signal_handler(int sig_num) {
+       set_sync_flag(&sigusr_received, true);
+       return;
+}
+
+static void send_event(mesh_event_t event) {
+       bool send_ret = false;
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               send_ret = mesh_event_sock_send(client_id, event, NULL, 0);
+
+               if(send_ret) {
+                       break;
+               }
+       }
+
+       return;
+}
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "peer") && reachable) {
+               set_sync_flag(&peer_reachable, true);
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       assert(meshlink_channel_send(mesh, channel, "test", 5) >= 0);
+       return;
+}
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       if(len == 0) {
+               set_sync_flag(&channel_closed, true);
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       if(!strcmp(channel->node->name, "peer")) {
+               if(len == 5 && !memcmp(dat, "reply", 5)) {
+                       set_sync_flag(&channel_opened, true);
+               }
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       struct timespec timeout = {0};
+       int i;
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGUSR1, mesh_siguser1_signal_handler);
+
+       // Execute test steps
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 30));
+       send_event(NODE_JOINED);
+
+       // Open a channel to peer node
+
+       meshlink_node_t *peer_node = meshlink_get_node(mesh, "peer");
+       assert(peer_node);
+       meshlink_channel_t *channel = meshlink_channel_open(mesh, peer_node, CHANNEL_PORT,
+                                     channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+
+       assert(wait_sync_flag(&channel_opened, 10));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&sigusr_received, 10));
+
+       assert(meshlink_channel_send(mesh, channel, "after", 6) >= 0);
+
+       assert(wait_sync_flag(&channel_closed, 180));
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+}
diff --git a/test/blackbox/test_case_channel_conn_02/node_sim_peer.c b/test/blackbox/test_case_channel_conn_02/node_sim_peer.c
new file mode 100644 (file)
index 0000000..ac94066
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static int client_id = -1;
+static bool channel_rec;
+static bool channel_reply;
+static pthread_mutex_t reply_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t reply_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t recv_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t recv_cond = PTHREAD_COND_INITIALIZER;
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       if(!strcmp(channel->node->name, "nut")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       return false;
+}
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       if(len == 0) {
+               assert(mesh_event_sock_send(client_id, ERR_NETWORK, NULL, 0));
+               return;
+       }
+
+       if(!strcmp(channel->node->name, "nut")) {
+               if(!memcmp(dat, "test", 5)) {
+                       assert(meshlink_channel_send(mesh, channel, "reply", 5) >= 0);
+               }
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       struct timespec timeout = {0};
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Run peer node instance
+
+       setup_signals();
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
diff --git a/test/blackbox/test_case_channel_conn_03/Makefile.am b/test/blackbox/test_case_channel_conn_03/Makefile.am
new file mode 100644 (file)
index 0000000..4cca831
--- /dev/null
@@ -0,0 +1,9 @@
+check_PROGRAMS = node_sim_peer node_sim_nut
+
+node_sim_peer_SOURCES = node_sim_peer.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_peer_LDADD = ../../../src/libmeshlink.la
+node_sim_peer_CFLAGS = -D_GNU_SOURCE
+
+node_sim_nut_SOURCES = node_sim_nut.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_nut_LDADD = ../../../src/libmeshlink.la
+node_sim_nut_CFLAGS = -D_GNU_SOURCE
diff --git a/test/blackbox/test_case_channel_conn_03/node_sim_nut.c b/test/blackbox/test_case_channel_conn_03/node_sim_nut.c
new file mode 100644 (file)
index 0000000..2f1e7e6
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+    node_sim_nut.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static int client_id = -1;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag peer_unreachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag sigusr_received = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+
+static void send_event(mesh_event_t event);
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable);
+static void mesh_siguser1_signal_handler(int sig_num);
+
+static void mesh_siguser1_signal_handler(int sig_num) {
+       set_sync_flag(&sigusr_received, true);
+       return;
+}
+
+static void send_event(mesh_event_t event) {
+       bool send_ret = false;
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "peer")) {
+               if(reachable) {
+                       set_sync_flag(&peer_reachable, true);
+               } else {
+                       set_sync_flag(&peer_unreachable, true);
+               }
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       assert(meshlink_channel_send(mesh, channel, "test", 5) >= 0);
+       return;
+}
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       if(!strcmp(channel->node->name, "peer")) {
+               if(len == 5 && !memcmp(dat, "reply", 5)) {
+                       set_sync_flag(&channel_opened, true);
+               }
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       struct timespec timeout = {0};
+       int i;
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGUSR1, mesh_siguser1_signal_handler);
+
+       // Execute test steps
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 30));
+       send_event(NODE_JOINED);
+
+       // Open a channel to peer node
+
+       meshlink_node_t *peer_node = meshlink_get_node(mesh, "peer");
+       assert(peer_node);
+       meshlink_channel_t *channel = meshlink_channel_open(mesh, peer_node, CHANNEL_PORT,
+                                     channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+
+       assert(wait_sync_flag(&channel_opened, 10));
+       send_event(CHANNEL_OPENED);
+
+       peer_unreachable.flag = false;
+       peer_reachable.flag = false;
+       assert(wait_sync_flag(&sigusr_received, 10));
+
+       assert(wait_sync_flag(&peer_unreachable, 100));
+       send_event(NODE_UNREACHABLE);
+
+       assert(wait_sync_flag(&peer_reachable, 100));
+       send_event(NODE_REACHABLE);
+
+       assert(meshlink_channel_send(mesh, channel, "after", 6) >= 0);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+}
diff --git a/test/blackbox/test_case_channel_conn_03/node_sim_peer.c b/test/blackbox/test_case_channel_conn_03/node_sim_peer.c
new file mode 100644 (file)
index 0000000..ae8dadc
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static int client_id = -1;
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       if(!strcmp(channel->node->name, "nut")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       return false;
+}
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       if(len == 0) {
+               assert(mesh_event_sock_send(client_id, ERR_NETWORK, NULL, 0));
+               return;
+       }
+
+       if(!strcmp(channel->node->name, "nut")) {
+               if(!memcmp(dat, "test", 5)) {
+                       assert(meshlink_channel_send(mesh, channel, "reply", 5) >= 0);
+               } else if(!memcmp(dat, "after", 5)) {
+                       assert(mesh_event_sock_send(client_id, CHANNEL_DATA_RECIEVED, NULL, 0));
+               }
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       struct timespec timeout = {0};
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Run peer node instance
+
+       setup_signals();
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
diff --git a/test/blackbox/test_case_channel_conn_04/Makefile.am b/test/blackbox/test_case_channel_conn_04/Makefile.am
new file mode 100644 (file)
index 0000000..4cca831
--- /dev/null
@@ -0,0 +1,9 @@
+check_PROGRAMS = node_sim_peer node_sim_nut
+
+node_sim_peer_SOURCES = node_sim_peer.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_peer_LDADD = ../../../src/libmeshlink.la
+node_sim_peer_CFLAGS = -D_GNU_SOURCE
+
+node_sim_nut_SOURCES = node_sim_nut.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_nut_LDADD = ../../../src/libmeshlink.la
+node_sim_nut_CFLAGS = -D_GNU_SOURCE
diff --git a/test/blackbox/test_case_channel_conn_04/node_sim_nut.c b/test/blackbox/test_case_channel_conn_04/node_sim_nut.c
new file mode 100644 (file)
index 0000000..fe33870
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+    node_sim_nut.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static int client_id = -1;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag channel_closed = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+
+static void send_event(mesh_event_t event);
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable);
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "peer")) {
+               if(reachable) {
+                       set_sync_flag(&peer_reachable, true);
+               } else {
+                       peer_reachable.flag = false;
+               }
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       assert(meshlink_channel_send(mesh, channel, "test", 5) >= 0);
+       return;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       if(len == 0) {
+               //send_event(ERR_NETWORK);
+               return;
+       }
+
+       if(!strcmp(channel->node->name, "peer")) {
+               if(!memcmp(dat, "reply", 5)) {
+                       set_sync_flag(&channel_opened, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 5, 0 };
+       int i;
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       setup_signals();
+
+       // Execute test steps
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 10));
+       send_event(NODE_JOINED);
+
+       // Open a channel to peer node
+
+       meshlink_node_t *peer_node = meshlink_get_node(mesh, "peer");
+       assert(peer_node);
+       meshlink_channel_t *channel = meshlink_channel_open(mesh, peer_node, CHANNEL_PORT,
+                                     channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+
+       assert(wait_sync_flag(&channel_opened, 10));
+       send_event(CHANNEL_OPENED);
+
+       // Restarting the node instance
+
+       meshlink_stop(mesh);
+       meshlink_start(mesh);
+
+       assert(wait_sync_flag(&peer_reachable, 60));
+       send_event(NODE_RESTARTED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+}
diff --git a/test/blackbox/test_case_channel_conn_04/node_sim_peer.c b/test/blackbox/test_case_channel_conn_04/node_sim_peer.c
new file mode 100644 (file)
index 0000000..0128978
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static struct sync_flag sigusr = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static int client_id = -1;
+
+static void mesh_siguser1_signal_handler(int sig_num) {
+       set_sync_flag(&sigusr, true);
+
+       return;
+}
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       if(!strcmp(channel->node->name, "nut")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       (void)mesh;
+       (void)channel;
+       (void)dat;
+       (void)len;
+
+       if(len == 0) {
+               mesh_event_sock_send(client_id, ERR_NETWORK, NULL, 0);
+               return;
+       }
+
+       if(!strcmp(channel->node->name, "nut") && !memcmp(dat, "test", 5)) {
+               assert(meshlink_channel_send(mesh, channel, "reply", 5) >= 0);
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGUSR1, mesh_siguser1_signal_handler);
+
+       // Run peer node instance
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       assert(wait_sync_flag(&sigusr, 140));
+       meshlink_channel_t *channel = mesh->priv;
+       assert(meshlink_channel_send(mesh, channel, "failure", 7));
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
diff --git a/test/blackbox/test_case_channel_conn_05/Makefile.am b/test/blackbox/test_case_channel_conn_05/Makefile.am
new file mode 100644 (file)
index 0000000..8bde53d
--- /dev/null
@@ -0,0 +1,13 @@
+check_PROGRAMS = node_sim_peer node_sim_nut node_sim_relay
+
+node_sim_peer_SOURCES = node_sim_peer.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_peer_LDADD = ../../../src/libmeshlink.la
+node_sim_peer_CFLAGS = -D_GNU_SOURCE
+
+node_sim_nut_SOURCES = node_sim_nut.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_nut_LDADD = ../../../src/libmeshlink.la
+node_sim_nut_CFLAGS = -D_GNU_SOURCE
+
+node_sim_relay_SOURCES = node_sim_relay.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_relay_LDADD = ../../../src/libmeshlink.la
+node_sim_relay_CFLAGS = -D_GNU_SOURCE
diff --git a/test/blackbox/test_case_channel_conn_05/node_sim_nut.c b/test/blackbox/test_case_channel_conn_05/node_sim_nut.c
new file mode 100644 (file)
index 0000000..bded500
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+    node_sim_nut.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static int client_id = -1;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag sigusr_received = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag channel_received = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+
+static void send_event(mesh_event_t event);
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable);
+static void mesh_siguser1_signal_handler(int sig_num);
+
+static void mesh_siguser1_signal_handler(int sig_num) {
+       set_sync_flag(&sigusr_received, true);
+
+       return;
+}
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "peer") && reachable) {
+               set_sync_flag(&peer_reachable, true);
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       assert(meshlink_channel_send(mesh, channel, "test", 5) >= 0);
+       return;
+}
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       if(len == 0) {
+               send_event(ERR_NETWORK);
+               assert(false);
+       }
+
+       if(!strcmp(channel->node->name, "peer")) {
+               if(len == 5 && !memcmp(dat, "reply", 5)) {
+                       set_sync_flag(&channel_opened, true);
+               }
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       struct timespec timeout = {0};
+       int i;
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGUSR1, mesh_siguser1_signal_handler);
+
+       // Execute test steps
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 30));
+       send_event(NODE_JOINED);
+
+       // Open a channel to peer node
+
+       meshlink_node_t *peer_node = meshlink_get_node(mesh, "peer");
+       assert(peer_node);
+       meshlink_channel_t *channel = meshlink_channel_open(mesh, peer_node, CHANNEL_PORT,
+                                     channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+
+       assert(wait_sync_flag(&channel_opened, 10));
+       send_event(CHANNEL_OPENED);
+
+       assert(wait_sync_flag(&sigusr_received, 10));
+
+       sleep(10);
+
+       assert(meshlink_channel_send(mesh, channel, "after", 6) >= 0);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+}
diff --git a/test/blackbox/test_case_channel_conn_05/node_sim_peer.c b/test/blackbox/test_case_channel_conn_05/node_sim_peer.c
new file mode 100644 (file)
index 0000000..7cc095d
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static int client_id = -1;
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       if(!strcmp(channel->node->name, "nut")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       (void)mesh;
+       (void)channel;
+       (void)dat;
+       (void)len;
+
+       if(len == 0) {
+               mesh_event_sock_send(client_id, ERR_NETWORK, NULL, 0);
+               assert(false);
+       }
+
+       if(!strcmp(channel->node->name, "nut")) {
+               if(!memcmp(dat, "test", 5)) {
+                       assert(meshlink_channel_send(mesh, channel, "reply", 5) >= 0);
+               } else if(!memcmp(dat, "after", 6)) {
+                       assert(mesh_event_sock_send(client_id, CHANNEL_DATA_RECIEVED, NULL, 0));
+               }
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+
+       // Run peer node instance
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
diff --git a/test/blackbox/test_case_channel_conn_05/node_sim_relay.c b/test/blackbox/test_case_channel_conn_05/node_sim_relay.c
new file mode 100644 (file)
index 0000000..46fbf16
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+    node_sim_relay.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 5, 0 };
+
+       // Setup required signals
+
+       setup_signals();
+
+       // Run relay node instance
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+
+       meshlink_start(mesh);
+
+       /* All test steps executed - wait for signals to stop/start or close the mesh */
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return 0;
+}
diff --git a/test/blackbox/test_case_channel_conn_06/Makefile.am b/test/blackbox/test_case_channel_conn_06/Makefile.am
new file mode 100644 (file)
index 0000000..8bde53d
--- /dev/null
@@ -0,0 +1,13 @@
+check_PROGRAMS = node_sim_peer node_sim_nut node_sim_relay
+
+node_sim_peer_SOURCES = node_sim_peer.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_peer_LDADD = ../../../src/libmeshlink.la
+node_sim_peer_CFLAGS = -D_GNU_SOURCE
+
+node_sim_nut_SOURCES = node_sim_nut.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_nut_LDADD = ../../../src/libmeshlink.la
+node_sim_nut_CFLAGS = -D_GNU_SOURCE
+
+node_sim_relay_SOURCES = node_sim_relay.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_relay_LDADD = ../../../src/libmeshlink.la
+node_sim_relay_CFLAGS = -D_GNU_SOURCE
diff --git a/test/blackbox/test_case_channel_conn_06/node_sim_nut.c b/test/blackbox/test_case_channel_conn_06/node_sim_nut.c
new file mode 100644 (file)
index 0000000..858e48d
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+    node_sim_nut.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static int client_id = -1;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag channel_closed = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag sigusr_received = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+
+static void send_event(mesh_event_t event);
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable);
+static void mesh_siguser1_signal_handler(int sig_num);
+
+static void mesh_siguser1_signal_handler(int sig_num) {
+       set_sync_flag(&sigusr_received, true);
+       return;
+}
+
+static void send_event(mesh_event_t event) {
+       bool send_ret = false;
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               send_ret = mesh_event_sock_send(client_id, event, NULL, 0);
+
+               if(send_ret) {
+                       break;
+               }
+       }
+
+       return;
+}
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       fprintf(stderr, "\n\n\n NODE STATUS CB : %s is %d\n\n\n\n", node->name, reachable);
+
+       if(!strcasecmp(node->name, "peer") && reachable) {
+               set_sync_flag(&peer_reachable, true);
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       assert(meshlink_channel_send(mesh, channel, "test", 5) >= 0);
+       return;
+}
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       fprintf(stderr, "\n\n\n LEN = %u & DATA = %s in RECV CB\n\n\n\n", len, (char *)dat);
+
+       if(len == 0) {
+               set_sync_flag(&channel_closed, true);
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       if(!strcmp(channel->node->name, "peer")) {
+               if(len == 5 && !memcmp(dat, "reply", 5)) {
+                       set_sync_flag(&channel_opened, true);
+               }
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       struct timespec timeout = {0};
+       int i;
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGUSR1, mesh_siguser1_signal_handler);
+
+       // Execute test steps
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 30));
+       send_event(NODE_JOINED);
+
+       // Open a channel to peer node
+
+       meshlink_node_t *peer_node = meshlink_get_node(mesh, "peer");
+       assert(peer_node);
+       meshlink_channel_t *channel = meshlink_channel_open(mesh, peer_node, CHANNEL_PORT,
+                                     channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+
+       assert(wait_sync_flag(&channel_opened, 10));
+       send_event(CHANNEL_OPENED);
+       fprintf(stderr, "\n\n\nChannel opened, Waiting for SIGUSR1\n\n\n\n");
+       assert(wait_sync_flag(&sigusr_received, 10));
+
+       fprintf(stderr, "\n\n\nChannel sending, got SIGUSR1\n\n\n\n");
+       sleep(40);
+       assert(meshlink_channel_send(mesh, channel, "after", 6) >= 0);
+
+       fprintf(stderr, "\n\n\nWaiting for close\n\n\n\n");
+
+       assert(wait_sync_flag(&channel_closed, 140));
+
+       fprintf(stderr, "\n\n\nCHANNEL CLOSED\n\n\n\n");
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+}
diff --git a/test/blackbox/test_case_channel_conn_06/node_sim_peer.c b/test/blackbox/test_case_channel_conn_06/node_sim_peer.c
new file mode 100644 (file)
index 0000000..a0be623
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static int client_id = -1;
+static bool channel_rec;
+static bool channel_reply;
+static pthread_mutex_t reply_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t reply_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t recv_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t recv_cond = PTHREAD_COND_INITIALIZER;
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       if(!strcmp(channel->node->name, "nut")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       return false;
+}
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       fprintf(stderr, "\n\n\n LEN = %u & DATA = %s in RECV CB\n\n\n\n", len, (char *)dat);
+
+       if(len == 0) {
+               assert(mesh_event_sock_send(client_id, ERR_NETWORK, NULL, 0));
+               return;
+       }
+
+       if(!strcmp(channel->node->name, "nut")) {
+               if(!memcmp(dat, "test", 5)) {
+                       assert(meshlink_channel_send(mesh, channel, "reply", 5) >= 0);
+               }
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 5, 0 };
+       struct timespec timeout = {0};
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Run peer node instance
+
+       setup_signals();
+
+       meshlink_set_log_cb(NULL, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
diff --git a/test/blackbox/test_case_channel_conn_06/node_sim_relay.c b/test/blackbox/test_case_channel_conn_06/node_sim_relay.c
new file mode 100644 (file)
index 0000000..46fbf16
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+    node_sim_relay.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 5, 0 };
+
+       // Setup required signals
+
+       setup_signals();
+
+       // Run relay node instance
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+
+       meshlink_start(mesh);
+
+       /* All test steps executed - wait for signals to stop/start or close the mesh */
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return 0;
+}
diff --git a/test/blackbox/test_case_channel_conn_07/Makefile.am b/test/blackbox/test_case_channel_conn_07/Makefile.am
new file mode 100644 (file)
index 0000000..8bde53d
--- /dev/null
@@ -0,0 +1,13 @@
+check_PROGRAMS = node_sim_peer node_sim_nut node_sim_relay
+
+node_sim_peer_SOURCES = node_sim_peer.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_peer_LDADD = ../../../src/libmeshlink.la
+node_sim_peer_CFLAGS = -D_GNU_SOURCE
+
+node_sim_nut_SOURCES = node_sim_nut.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_nut_LDADD = ../../../src/libmeshlink.la
+node_sim_nut_CFLAGS = -D_GNU_SOURCE
+
+node_sim_relay_SOURCES = node_sim_relay.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_relay_LDADD = ../../../src/libmeshlink.la
+node_sim_relay_CFLAGS = -D_GNU_SOURCE
diff --git a/test/blackbox/test_case_channel_conn_07/node_sim_nut.c b/test/blackbox/test_case_channel_conn_07/node_sim_nut.c
new file mode 100644 (file)
index 0000000..b47c000
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+    node_sim_nut.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static int client_id = -1;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag peer_unreachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag sigusr_received = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+
+static void send_event(mesh_event_t event);
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable);
+static void mesh_siguser1_signal_handler(int sig_num);
+
+static void mesh_siguser1_signal_handler(int sig_num) {
+       set_sync_flag(&sigusr_received, true);
+       return;
+}
+
+static void send_event(mesh_event_t event) {
+       bool send_ret = false;
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+
+       if(!strcasecmp(node->name, "peer")) {
+               if(reachable) {
+                       set_sync_flag(&peer_reachable, true);
+               } else {
+                       set_sync_flag(&peer_unreachable, true);
+               }
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       assert(meshlink_channel_send(mesh, channel, "test", 5) >= 0);
+       return;
+}
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       if(len == 0) {
+               send_event(ERR_NETWORK);
+               return;
+       }
+
+       if(!strcmp(channel->node->name, "peer")) {
+               if(len == 5 && !memcmp(dat, "reply", 5)) {
+                       set_sync_flag(&channel_opened, true);
+               }
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       struct timespec timeout = {0};
+       int i;
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGUSR1, mesh_siguser1_signal_handler);
+
+       // Execute test steps
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       // Wait for peer node to join
+       assert(wait_sync_flag(&peer_reachable, 30));
+       send_event(NODE_JOINED);
+
+       // Open a channel to peer node
+
+       meshlink_node_t *peer_node = meshlink_get_node(mesh, "peer");
+       assert(peer_node);
+       meshlink_channel_t *channel = meshlink_channel_open(mesh, peer_node, CHANNEL_PORT,
+                                     channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+
+       assert(wait_sync_flag(&channel_opened, 10));
+       send_event(CHANNEL_OPENED);
+
+       peer_unreachable.flag = false;
+       peer_reachable.flag = false;
+       assert(wait_sync_flag(&sigusr_received, 10));
+
+       assert(wait_sync_flag(&peer_unreachable, 100));
+       send_event(NODE_UNREACHABLE);
+
+       assert(wait_sync_flag(&peer_reachable, 100));
+       send_event(NODE_REACHABLE);
+
+       assert(meshlink_channel_send(mesh, channel, "after", 6) >= 0);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+}
diff --git a/test/blackbox/test_case_channel_conn_07/node_sim_peer.c b/test/blackbox/test_case_channel_conn_07/node_sim_peer.c
new file mode 100644 (file)
index 0000000..ae8dadc
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static int client_id = -1;
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       if(!strcmp(channel->node->name, "nut")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       return false;
+}
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       if(len == 0) {
+               assert(mesh_event_sock_send(client_id, ERR_NETWORK, NULL, 0));
+               return;
+       }
+
+       if(!strcmp(channel->node->name, "nut")) {
+               if(!memcmp(dat, "test", 5)) {
+                       assert(meshlink_channel_send(mesh, channel, "reply", 5) >= 0);
+               } else if(!memcmp(dat, "after", 5)) {
+                       assert(mesh_event_sock_send(client_id, CHANNEL_DATA_RECIEVED, NULL, 0));
+               }
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+       struct timespec timeout = {0};
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Run peer node instance
+
+       setup_signals();
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
diff --git a/test/blackbox/test_case_channel_conn_07/node_sim_relay.c b/test/blackbox/test_case_channel_conn_07/node_sim_relay.c
new file mode 100644 (file)
index 0000000..46fbf16
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+    node_sim_relay.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 5, 0 };
+
+       // Setup required signals
+
+       setup_signals();
+
+       // Run relay node instance
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+
+       meshlink_start(mesh);
+
+       /* All test steps executed - wait for signals to stop/start or close the mesh */
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return 0;
+}
diff --git a/test/blackbox/test_case_channel_conn_08/Makefile.am b/test/blackbox/test_case_channel_conn_08/Makefile.am
new file mode 100644 (file)
index 0000000..8bde53d
--- /dev/null
@@ -0,0 +1,13 @@
+check_PROGRAMS = node_sim_peer node_sim_nut node_sim_relay
+
+node_sim_peer_SOURCES = node_sim_peer.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_peer_LDADD = ../../../src/libmeshlink.la
+node_sim_peer_CFLAGS = -D_GNU_SOURCE
+
+node_sim_nut_SOURCES = node_sim_nut.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_nut_LDADD = ../../../src/libmeshlink.la
+node_sim_nut_CFLAGS = -D_GNU_SOURCE
+
+node_sim_relay_SOURCES = node_sim_relay.c ../common/common_handlers.c ../common/test_step.c ../common/mesh_event_handler.c ../../utils.c
+node_sim_relay_LDADD = ../../../src/libmeshlink.la
+node_sim_relay_CFLAGS = -D_GNU_SOURCE
diff --git a/test/blackbox/test_case_channel_conn_08/node_sim_nut.c b/test/blackbox/test_case_channel_conn_08/node_sim_nut.c
new file mode 100644 (file)
index 0000000..fe33870
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+    node_sim_nut.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static int client_id = -1;
+
+static struct sync_flag peer_reachable = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag channel_opened = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static struct sync_flag channel_closed = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+
+static void send_event(mesh_event_t event);
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable);
+
+static void send_event(mesh_event_t event) {
+       int attempts;
+
+       for(attempts = 0; attempts < 5; attempts += 1) {
+               if(mesh_event_sock_send(client_id, event, NULL, 0)) {
+                       break;
+               }
+       }
+
+       assert(attempts < 5);
+
+       return;
+}
+
+static void node_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node,
+                           bool reachable) {
+       if(!strcasecmp(node->name, "peer")) {
+               if(reachable) {
+                       set_sync_flag(&peer_reachable, true);
+               } else {
+                       peer_reachable.flag = false;
+               }
+       }
+
+       return;
+}
+
+static void poll_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, size_t len) {
+       (void)len;
+       meshlink_set_channel_poll_cb(mesh, channel, NULL);
+       assert(meshlink_channel_send(mesh, channel, "test", 5) >= 0);
+       return;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       if(len == 0) {
+               //send_event(ERR_NETWORK);
+               return;
+       }
+
+       if(!strcmp(channel->node->name, "peer")) {
+               if(!memcmp(dat, "reply", 5)) {
+                       set_sync_flag(&channel_opened, true);
+               } else if(!memcmp(dat, "failure", 7)) {
+                       assert(false);
+               }
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 5, 0 };
+       int i;
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       setup_signals();
+
+       // Execute test steps
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_node_status_cb(mesh, node_status_cb);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       // Wait for peer node to join
+
+       assert(wait_sync_flag(&peer_reachable, 10));
+       send_event(NODE_JOINED);
+
+       // Open a channel to peer node
+
+       meshlink_node_t *peer_node = meshlink_get_node(mesh, "peer");
+       assert(peer_node);
+       meshlink_channel_t *channel = meshlink_channel_open(mesh, peer_node, CHANNEL_PORT,
+                                     channel_receive_cb, NULL, 0);
+       meshlink_set_channel_poll_cb(mesh, channel, poll_cb);
+
+       assert(wait_sync_flag(&channel_opened, 10));
+       send_event(CHANNEL_OPENED);
+
+       // Restarting the node instance
+
+       meshlink_stop(mesh);
+       meshlink_start(mesh);
+
+       assert(wait_sync_flag(&peer_reachable, 60));
+       send_event(NODE_RESTARTED);
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+}
diff --git a/test/blackbox/test_case_channel_conn_08/node_sim_peer.c b/test/blackbox/test_case_channel_conn_08/node_sim_peer.c
new file mode 100644 (file)
index 0000000..0128978
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+    node_sim_peer.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <assert.h>
+#include <signal.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+#include "../../utils.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+#define CHANNEL_PORT 1234
+
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len);
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len);
+
+static struct sync_flag sigusr = {.mutex  = PTHREAD_MUTEX_INITIALIZER, .cond = PTHREAD_COND_INITIALIZER};
+static int client_id = -1;
+
+static void mesh_siguser1_signal_handler(int sig_num) {
+       set_sync_flag(&sigusr, true);
+
+       return;
+}
+
+static bool channel_accept(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint16_t port, const void *dat, size_t len) {
+       (void)dat;
+       (void)len;
+
+       assert(port == CHANNEL_PORT);
+
+       if(!strcmp(channel->node->name, "nut")) {
+               meshlink_set_channel_receive_cb(mesh, channel, channel_receive_cb);
+               mesh->priv = channel;
+
+               return true;
+       }
+
+       return false;
+}
+
+/* channel receive callback */
+static void channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *dat, size_t len) {
+       (void)mesh;
+       (void)channel;
+       (void)dat;
+       (void)len;
+
+       if(len == 0) {
+               mesh_event_sock_send(client_id, ERR_NETWORK, NULL, 0);
+               return;
+       }
+
+       if(!strcmp(channel->node->name, "nut") && !memcmp(dat, "test", 5)) {
+               assert(meshlink_channel_send(mesh, channel, "reply", 5) >= 0);
+       }
+
+       return;
+}
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 2, 0 };
+
+       // Import mesh event handler
+
+       if((argv[CMD_LINE_ARG_CLIENTID]) && (argv[CMD_LINE_ARG_IMPORTSTR])) {
+               client_id = atoi(argv[CMD_LINE_ARG_CLIENTID]);
+               mesh_event_sock_connect(argv[CMD_LINE_ARG_IMPORTSTR]);
+       }
+
+       // Setup required signals
+
+       setup_signals();
+       signal(SIGUSR1, mesh_siguser1_signal_handler);
+
+       // Run peer node instance
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+       meshlink_set_channel_accept_cb(mesh, channel_accept);
+
+       if(argv[CMD_LINE_ARG_INVITEURL]) {
+               assert(meshlink_join(mesh, argv[CMD_LINE_ARG_INVITEURL]));
+       }
+
+       assert(meshlink_start(mesh));
+
+       assert(wait_sync_flag(&sigusr, 140));
+       meshlink_channel_t *channel = mesh->priv;
+       assert(meshlink_channel_send(mesh, channel, "failure", 7));
+
+       // All test steps executed - wait for signals to stop/start or close the mesh
+
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return EXIT_SUCCESS;
+}
diff --git a/test/blackbox/test_case_channel_conn_08/node_sim_relay.c b/test/blackbox/test_case_channel_conn_08/node_sim_relay.c
new file mode 100644 (file)
index 0000000..46fbf16
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+    node_sim_relay.c -- Implementation of Node Simulation for Meshlink Testing
+                    for meta connection test case 01 - re-connection of
+                    two nodes when relay node goes down
+    Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
+
+    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 <stdio.h>
+#include <stdlib.h>
+#include "../common/common_handlers.h"
+#include "../common/test_step.h"
+#include "../common/mesh_event_handler.h"
+
+#define CMD_LINE_ARG_NODENAME   1
+#define CMD_LINE_ARG_DEVCLASS   2
+#define CMD_LINE_ARG_CLIENTID   3
+#define CMD_LINE_ARG_IMPORTSTR  4
+#define CMD_LINE_ARG_INVITEURL  5
+
+int main(int argc, char *argv[]) {
+       struct timeval main_loop_wait = { 5, 0 };
+
+       // Setup required signals
+
+       setup_signals();
+
+       // Run relay node instance
+
+       meshlink_handle_t *mesh = meshlink_open("testconf", argv[CMD_LINE_ARG_NODENAME],
+                                               "test_channel_conn", atoi(argv[CMD_LINE_ARG_DEVCLASS]));
+       assert(mesh);
+       meshlink_set_log_cb(mesh, MESHLINK_DEBUG, meshlink_callback_logger);
+
+       meshlink_start(mesh);
+
+       /* All test steps executed - wait for signals to stop/start or close the mesh */
+       while(test_running) {
+               select(1, NULL, NULL, NULL, &main_loop_wait);
+       }
+
+       meshlink_close(mesh);
+
+       return 0;
+}