]> git.meshlink.io Git - meshlink/blob - test/blackbox/common/containers.c
Add the blackbox channel connection tests.
[meshlink] / test / blackbox / common / containers.c
1 /*
2     containers.h -- Container Management API
3     Copyright (C) 2018  Guus Sliepen <guus@meshlink.io>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <stdlib.h>
21 #include <assert.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <pthread.h>
25 #include "containers.h"
26 #include "common_handlers.h"
27
28 char *lxc_path = NULL;
29 char *choose_arch;
30 static char container_ips[10][100];
31
32 /* Return the handle to an existing container after finding it by container name */
33 struct lxc_container *find_container(const char *name) {
34         struct lxc_container **test_containers;
35         char **container_names;
36         int num_containers, i;
37
38         assert((num_containers = list_all_containers(lxc_path, &container_names,
39                                  &test_containers)) != -1);
40
41         for(i = 0; i < num_containers; i++) {
42                 if(strcmp(container_names[i], name) == 0) {
43                         return test_containers[i];
44                 }
45         }
46
47         return NULL;
48 }
49
50 /* Rename a Container */
51 void rename_container(const char *old_name, const char *new_name) {
52         char rename_command[200];
53         int rename_status;
54         struct lxc_container *old_container;
55
56         /* Stop the old container if its still running */
57         assert(old_container = find_container(old_name));
58         old_container->shutdown(old_container, CONTAINER_SHUTDOWN_TIMEOUT);
59         /* Call stop() in case shutdown() fails - one of these two will always succeed */
60         old_container->stop(old_container);
61         /* Rename the Container */
62         /* TO DO: Perform this operation using the LXC API - currently does not work via the API,
63             need to investigate and determine why it doesn't work, and make it work */
64         assert(snprintf(rename_command, sizeof(rename_command),
65                         "%s/" LXC_UTIL_REL_PATH "/" LXC_RENAME_SCRIPT " %s %s %s", meshlink_root_path, lxc_path,
66                         old_name, new_name) >= 0);
67         rename_status = system(rename_command);
68         PRINT_TEST_CASE_MSG("Container '%s' rename status: %d\n", old_name, rename_status);
69         assert(rename_status == 0);
70 }
71
72 /* Run 'cmd' inside the Container created for 'node' and return the first line of the output
73     or NULL if there is no output - useful when, for example, a meshlink invite is generated
74     by a node running inside a Container
75     'cmd' is run as a daemon if 'daemonize' is true - this mode is useful for running node
76     simulations in Containers
77     The caller is responsible for freeing the returned string */
78 char *run_in_container(const char *cmd, const char *node, bool daemonize) {
79         char attach_command[400];
80         char *attach_argv[4];
81         char container_find_name[100];
82         struct lxc_container *container;
83         FILE *attach_fp;
84         char *output = NULL;
85         size_t output_len;
86         int i;
87
88         assert(snprintf(container_find_name, sizeof(container_find_name), "%s_%s",
89                         state_ptr->test_case_name, node) >= 0);
90         assert(container = find_container(container_find_name));
91
92         /* Run the command within the Container, either as a daemon or foreground process */
93         /* TO DO: Perform this operation using the LXC API - currently does not work using the API
94             Need to determine why it doesn't work, and make it work */
95         if(daemonize) {
96                 for(i = 0; i < 3; i++) {
97                         assert(attach_argv[i] = malloc(DAEMON_ARGV_LEN));
98                 }
99
100                 assert(snprintf(attach_argv[0], DAEMON_ARGV_LEN, "%s/" LXC_UTIL_REL_PATH "/" LXC_RUN_SCRIPT,
101                                 meshlink_root_path) >= 0);
102                 strncpy(attach_argv[1], cmd, DAEMON_ARGV_LEN);
103                 strncpy(attach_argv[2], container->name, DAEMON_ARGV_LEN);
104                 attach_argv[3] = NULL;
105
106                 /* To daemonize, create a child process and detach it from its parent (this program) */
107                 if(fork() == 0) {
108                         assert(daemon(1, 0) != -1);    // Detach from the parent process
109                         assert(execv(attach_argv[0], attach_argv) != -1);   // Run exec() in the child process
110                 }
111
112                 for(i = 0; i < 3; i++) {
113                         free(attach_argv[i]);
114                 }
115         } else {
116                 assert(snprintf(attach_command, sizeof(attach_command),
117                                 "%s/" LXC_UTIL_REL_PATH "/" LXC_RUN_SCRIPT " \"%s\" %s", meshlink_root_path, cmd,
118                                 container->name) >= 0);
119                 assert(attach_fp = popen(attach_command, "r"));
120                 /* If the command has an output, strip out any trailing carriage returns or newlines and
121                     return it, otherwise return NULL */
122                 assert(output = malloc(100));
123                 output_len = sizeof(output);
124
125                 if(getline(&output, &output_len, attach_fp) != -1) {
126                         i = strlen(output) - 1;
127
128                         while(output[i] == '\n' || output[i] == '\r') {
129                                 i--;
130                         }
131
132                         output[i + 1] = '\0';
133                 } else {
134                         free(output);
135                         output = NULL;
136                 }
137
138                 assert(pclose(attach_fp) != -1);
139         }
140
141         return output;
142 }
143
144 /* Wait for a starting Container to obtain an IP Address, then save that IP for future use */
145 void container_wait_ip(int node) {
146         char container_name[100], lxcls_command[200];
147         struct lxc_container *test_container;
148         char *ip;
149         size_t ip_len;
150         int i;
151         bool ip_found;
152         FILE *lxcls_fp;
153
154         assert(snprintf(container_name, sizeof(container_name), "%s_%s", state_ptr->test_case_name,
155                         state_ptr->node_names[node]) >= 0);
156         assert(test_container = find_container(container_name));
157         assert(snprintf(lxcls_command, sizeof(lxcls_command),
158                         "lxc-ls -f | grep %s | tr -s ' ' | cut -d ' ' -f 5", test_container->name) >= 0);
159         PRINT_TEST_CASE_MSG("Waiting for Container '%s' to acquire IP\n", test_container->name);
160         assert(ip = malloc(20));
161         ip_len = sizeof(ip);
162         ip_found = false;
163
164         while(!ip_found) {
165                 assert(lxcls_fp = popen(lxcls_command, "r"));   // Run command
166                 assert(getline((char **)&ip, &ip_len, lxcls_fp) != -1); // Read its output
167                 /* Strip newlines and carriage returns from output */
168                 i = strlen(ip) - 1;
169
170                 while(ip[i] == '\n' || ip[i] == '\r') {
171                         i--;
172                 }
173
174                 ip[i + 1] = '\0';
175                 ip_found = (strcmp(ip, "-") != 0);  // If the output is not "-", IP has been acquired
176                 assert(pclose(lxcls_fp) != -1);
177                 sleep(1);
178         }
179
180         strncpy(container_ips[node], ip, sizeof(container_ips[node])); // Save the IP for future use
181         PRINT_TEST_CASE_MSG("Node '%s' has IP Address %s\n", state_ptr->node_names[node],
182                             container_ips[node]);
183
184         free(ip);
185 }
186
187 /* Create all required test containers */
188 void create_containers(const char *node_names[], int num_nodes) {
189         int i;
190         char container_name[100];
191         int create_status, snapshot_status, snap_restore_status;
192         struct lxc_container *first_container;
193
194         for(i = 0; i < num_nodes; i++) {
195                 assert(snprintf(container_name, sizeof(container_name), "run_%s", node_names[i]) >= 0);
196
197                 /* If this is the first Container, create it otherwise restore the snapshot saved
198                     for the first Container to create an additional Container */
199                 if(i == 0) {
200                         assert(first_container = lxc_container_new(container_name, NULL));
201                         assert(!first_container->is_defined(first_container));
202                         create_status = first_container->createl(first_container, "download", NULL, NULL,
203                                         LXC_CREATE_QUIET, "-d", "ubuntu", "-r", "trusty", "-a", choose_arch, NULL);
204                         fprintf(stderr, "Container '%s' create status: %d - %s\n", container_name,
205                                 first_container->error_num, first_container->error_string);
206                         assert(create_status);
207                         snapshot_status = first_container->snapshot(first_container, NULL);
208                         fprintf(stderr, "Container '%s' snapshot status: %d - %s\n", container_name,
209                                 first_container->error_num, first_container->error_string);
210                         assert(snapshot_status != -1);
211                 } else {
212                         snap_restore_status = first_container->snapshot_restore(first_container, "snap0",
213                                               container_name);
214                         fprintf(stderr, "Snapshot restore to Container '%s' status: %d - %s\n", container_name,
215                                 first_container->error_num, first_container->error_string);
216                         assert(snap_restore_status);
217                 }
218         }
219 }
220
221 /* Setup Containers required for a test
222     This function should always be invoked in a CMocka context
223     after setting the state of the test case to an instance of black_box_state_t */
224 void setup_containers(void **state) {
225         black_box_state_t *test_state = (black_box_state_t *)(*state);
226         int i, confbase_del_status;
227         char build_command[200];
228         struct lxc_container *test_container, *new_container;
229         char container_find_name[100];
230         char container_new_name[100];
231         int create_status, build_status;
232
233         PRINT_TEST_CASE_HEADER();
234
235         for(i = 0; i < test_state->num_nodes; i++) {
236                 /* Find the run_<node-name> Container or create it if it doesn't exist */
237                 assert(snprintf(container_find_name, sizeof(container_find_name), "run_%s",
238                                 test_state->node_names[i]) >= 0);
239
240                 if(!(test_container = find_container(container_find_name))) {
241                         assert(test_container = lxc_container_new(container_find_name, NULL));
242                         assert(!test_container->is_defined(test_container));
243                         create_status = test_container->createl(test_container, "download", NULL, NULL,
244                                                                 LXC_CREATE_QUIET, "-d", "ubuntu", "-r", "trusty", "-a", choose_arch, NULL);
245                         PRINT_TEST_CASE_MSG("Container '%s' create status: %d - %s\n", container_find_name,
246                                             test_container->error_num, test_container->error_string);
247                         assert(create_status);
248                 }
249
250                 /* Stop the Container if it's running */
251                 test_container->shutdown(test_container, CONTAINER_SHUTDOWN_TIMEOUT);
252                 /* Call stop() in case shutdown() fails
253                     One of these two calls will always succeed */
254                 test_container->stop(test_container);
255                 /* Rename the Container to make it specific to this test case,
256                     if a Container with the target name already exists, skip this step */
257                 assert(snprintf(container_new_name, sizeof(container_new_name), "%s_%s",
258                                 test_state->test_case_name, test_state->node_names[i]) >= 0);
259
260                 if(!(new_container = find_container(container_new_name))) {
261                         rename_container(test_container->name, container_new_name);
262                         assert(new_container = find_container(container_new_name));
263                 }
264
265                 /* Start the Container */
266                 assert(new_container->start(new_container, 0, NULL));
267                 /* Build the Container by copying required files into it */
268                 assert(snprintf(build_command, sizeof(build_command),
269                                 "%s/" LXC_UTIL_REL_PATH "/" LXC_BUILD_SCRIPT " %s %s %s +x >/dev/null",
270                                 meshlink_root_path, test_state->test_case_name, test_state->node_names[i],
271                                 meshlink_root_path) >= 0);
272                 build_status = system(build_command);
273                 PRINT_TEST_CASE_MSG("Container '%s' build Status: %d\n", new_container->name,
274                                     build_status);
275                 assert(build_status == 0);
276                 /* Restart the Container after building it and wait for it to acquire an IP */
277                 new_container->shutdown(new_container, CONTAINER_SHUTDOWN_TIMEOUT);
278                 new_container->stop(new_container);
279                 new_container->start(new_container, 0, NULL);
280                 container_wait_ip(i);
281         }
282 }
283
284 /* Destroy all Containers with names containing 'run_' - Containers saved for debugging will
285     have names beginning with test_case_ ; 'run_' is reserved for temporary Containers
286     intended to be re-used for the next test */
287 void destroy_containers(void) {
288         struct lxc_container **test_containers;
289         char **container_names;
290         int num_containers, i;
291
292         assert((num_containers = list_all_containers(lxc_path, &container_names,
293                                  &test_containers)) != -1);
294
295         for(i = 0; i < num_containers; i++) {
296                 if(strstr(container_names[i], "run_")) {
297                         fprintf(stderr, "Destroying Container '%s'\n", container_names[i]);
298                         /* Stop the Container - it cannot be destroyed till it is stopped */
299                         test_containers[i]->shutdown(test_containers[i], CONTAINER_SHUTDOWN_TIMEOUT);
300                         /* Call stop() in case shutdown() fails
301                             One of these two calls will always succeed */
302                         test_containers[i]->stop(test_containers[i]);
303                         /* Destroy the Container */
304                         test_containers[i]->destroy(test_containers[i]);
305                         /* Call destroy_with_snapshots() in case destroy() fails
306                             one of these two calls will always succeed */
307                         test_containers[i]->destroy_with_snapshots(test_containers[i]);
308                 }
309         }
310 }
311
312 /* Restart all the Containers being used in the current test case i.e. Containers with
313     names beginning with <test-case-name>_<node-name> */
314 void restart_all_containers(void) {
315         char container_name[100];
316         struct lxc_container *test_container;
317         int i;
318
319         for(i = 0; i < state_ptr->num_nodes; i++) {
320                 /* Shutdown, then start the Container, then wait for it to acquire an IP Address */
321                 assert(snprintf(container_name, sizeof(container_name), "%s_%s", state_ptr->test_case_name,
322                                 state_ptr->node_names[i]) >= 0);
323                 assert(test_container = find_container(container_name));
324                 test_container->shutdown(test_container, CONTAINER_SHUTDOWN_TIMEOUT);
325                 test_container->stop(test_container);
326                 test_container->start(test_container, 0, NULL);
327                 container_wait_ip(i);
328         }
329 }
330
331 /* Run the gen_invite command inside the 'inviter' container to generate an invite
332     for 'invitee', and return the generated invite which is output on the terminal */
333 char *invite_in_container(const char *inviter, const char *invitee) {
334         char invite_command[200];
335         char *invite_url;
336
337         assert(snprintf(invite_command, sizeof(invite_command),
338                         "LD_LIBRARY_PATH=/home/ubuntu/test/.libs /home/ubuntu/test/gen_invite %s %s "
339                         "2> gen_invite.log", inviter, invitee) >= 0);
340         assert(invite_url = run_in_container(invite_command, inviter, false));
341         PRINT_TEST_CASE_MSG("Invite Generated from '%s' to '%s': %s\n", inviter,
342                             invitee, invite_url);
343
344         return invite_url;
345 }
346
347 /* Run the node_sim_<nodename> program inside the 'node''s container */
348 void node_sim_in_container(const char *node, const char *device_class, const char *invite_url) {
349         char node_sim_command[200];
350
351         assert(snprintf(node_sim_command, sizeof(node_sim_command),
352                         "LD_LIBRARY_PATH=/home/ubuntu/test/.libs /home/ubuntu/test/node_sim_%s %s %s %s "
353                         "1>&2 2>> node_sim_%s.log", node, node, device_class,
354                         (invite_url) ? invite_url : "", node) >= 0);
355         run_in_container(node_sim_command, node, true);
356         PRINT_TEST_CASE_MSG("node_sim_%s started in Container\n", node);
357 }
358
359 /* Run the node_sim_<nodename> program inside the 'node''s container with event handling capable */
360 void node_sim_in_container_event(const char *node, const char *device_class,
361                                  const char *invite_url, const char *clientId, const char *import) {
362         char node_sim_command[200];
363
364         assert(snprintf(node_sim_command, sizeof(node_sim_command),
365                         "LD_LIBRARY_PATH=/home/ubuntu/test/.libs /home/ubuntu/test/node_sim_%s %s %s %s %s %s "
366                         "1>&2 2>> node_sim_%s.log", node, node, device_class,
367                         clientId, import, (invite_url) ? invite_url : "", node) >= 0);
368         run_in_container(node_sim_command, node, true);
369         PRINT_TEST_CASE_MSG("node_sim_%s(Client Id :%s) started in Container with event handling\n",
370                             node, clientId);
371 }
372
373 /* Run the node_step.sh script inside the 'node''s container to send the 'sig' signal to the
374     node_sim program in the container */
375 void node_step_in_container(const char *node, const char *sig) {
376         char node_step_command[200];
377
378         assert(snprintf(node_step_command, sizeof(node_step_command),
379                         "/home/ubuntu/test/node_step.sh lt-node_sim_%s %s 1>&2 2> node_step.log",
380                         node, sig) >= 0);
381         run_in_container(node_step_command, node, false);
382         PRINT_TEST_CASE_MSG("Signal %s sent to node_sim_%s\n", sig, node);
383 }
384
385 /* Change the IP Address of the Container running 'node'
386     Changes begin from X.X.X.254 and continue iteratively till an available address is found */
387 void change_ip(int node) {
388         char *gateway_addr;
389         char new_ip[20];
390         char *netmask;
391         char *last_dot_in_ip;
392         int last_ip_byte = 254;
393         FILE *if_fp;
394         char copy_command[200];
395         char container_name[100];
396         struct lxc_container *container;
397         int copy_file_stat;
398
399         /* Get IP Address of LXC Bridge Interface - this will be set up as the Gateway Address
400             of the Static IP assigned to the Container */
401         assert(gateway_addr = get_ip(lxc_bridge));
402         /* Get Netmask of LXC Brdige Interface */
403         assert(netmask = get_netmask(lxc_bridge));
404
405         /* Replace last byte of Container's IP with 254 to form the new Container IP */
406         assert(container_ips[node]);
407         strncpy(new_ip, container_ips[node], sizeof(new_ip));
408         assert(last_dot_in_ip = strrchr(new_ip, '.'));
409         assert(snprintf(last_dot_in_ip + 1, 4, "%d", last_ip_byte) >= 0);
410
411         /* Check that the new IP does not match the Container's existing IP
412             if it does, iterate till it doesn't */
413         /* TO DO: Make sure the IP does not conflict with any other running Container */
414         while(strcmp(new_ip, container_ips[node]) == 0) {
415                 last_ip_byte--;
416                 assert(snprintf(last_dot_in_ip + 1, 4, "%d", last_ip_byte) >= 0);
417         }
418
419         /* Create new 'interfaces' file for Container */
420         assert(if_fp = fopen("interfaces", "w"));
421         fprintf(if_fp, "auto lo\n");
422         fprintf(if_fp, "iface lo inet loopback\n");
423         fprintf(if_fp, "\n");
424         fprintf(if_fp, "auto eth0\n");
425         fprintf(if_fp, "iface eth0 inet static\n");
426         fprintf(if_fp, "\taddress %s\n", new_ip);
427         fprintf(if_fp, "\tnetmask %s\n", netmask);
428         fprintf(if_fp, "\tgateway %s\n", gateway_addr);
429         assert(fclose(if_fp) != EOF);
430
431         /* Copy 'interfaces' file into Container's /etc/network path */
432         assert(snprintf(copy_command, sizeof(copy_command),
433                         "%s/" LXC_UTIL_REL_PATH "/" LXC_COPY_SCRIPT " interfaces %s_%s /etc/network/interfaces",
434                         meshlink_root_path, state_ptr->test_case_name, state_ptr->node_names[node]) >= 0);
435         copy_file_stat = system(copy_command);
436         PRINT_TEST_CASE_MSG("Container '%s_%s' 'interfaces' file copy status: %d\n",
437                             state_ptr->test_case_name, state_ptr->node_names[node], copy_file_stat);
438         assert(copy_file_stat == 0);
439
440         /* Restart Container to apply new IP Address */
441         assert(snprintf(container_name, sizeof(container_name), "%s_%s", state_ptr->test_case_name,
442                         state_ptr->node_names[node]) >= 0);
443         assert(container = find_container(container_name));
444         container->shutdown(container, CONTAINER_SHUTDOWN_TIMEOUT);
445         /* Call stop() in case shutdown() fails
446             One of these two calls with always succeed */
447         container->stop(container);
448         assert(container->start(container, 0, NULL));
449
450         strncpy(container_ips[node], new_ip, sizeof(new_ip));   // Save the new IP Addres
451         PRINT_TEST_CASE_MSG("Node '%s' IP Address changed to %s\n", state_ptr->node_names[node],
452                             container_ips[node]);
453 }
454
455 /* Return container's IP address */
456 char *get_container_ip(const char *node_name) {
457         char *ip;
458         int n, node = -1, i;
459
460         for(i = 0; i < state_ptr->num_nodes; i++) {
461                 if(!strcasecmp(state_ptr->node_names[i], node_name)) {
462                         node = i;
463                         break;
464                 }
465         }
466
467         if(i == state_ptr->num_nodes) {
468                 return NULL;
469         }
470
471         n = strlen(container_ips[node]) + 1;
472         ip = malloc(n);
473         assert(ip);
474         strncpy(ip, container_ips[node], n);
475
476         return ip;
477 }
478
479 /* Install an app in a container */
480 void install_in_container(const char *node, const char *app) {
481         char install_cmd[100];
482
483         assert(snprintf(install_cmd, sizeof(install_cmd),
484                         "apt-get install %s -y >> /dev/null", app) >= 0);
485         char *ret = run_in_container(install_cmd, node, false);
486         // TODO: Check in container whether app has installed or not with a timeout
487         sleep(10);
488 }
489
490 /* Simulate a network failure by adding NAT rule in the container with it's IP address */
491 void block_node_ip(const char *node) {
492         char block_cmd[100];
493         char *node_ip;
494
495         node_ip = get_container_ip(node);
496         assert(node_ip);
497         assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A OUTPUT -p all -s %s -j DROP", node_ip) >= 0);
498         run_in_container(block_cmd, node, false);
499
500         assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A INPUT -p all -s %s -j DROP", node_ip) >= 0);
501         run_in_container(block_cmd, node, false);
502
503         assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A FORWARD -p all -s %s -j DROP", node_ip) >= 0);
504         run_in_container(block_cmd, node, false);
505
506         free(node_ip);
507 }
508
509 void accept_port_rule(const char *node, const char *chain, const char *protocol, int port) {
510         char block_cmd[100];
511
512         assert(port >= 0 && port < 65536);
513         assert(!strcmp(chain, "INPUT") || !strcmp(chain, "FORWARD") || !strcmp(chain, "OUTPUT"));
514         assert(!strcmp(protocol, "all") || !strcmp(protocol, "tcp") || !strcmp(protocol, "udp"));
515         assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A %s -p %s --dport %d -j ACCEPT", chain, protocol, port) >= 0);
516         run_in_container(block_cmd, node, false);
517 }
518
519 /* Restore the network that was blocked before by the NAT rule with it's own IP address */
520 void unblock_node_ip(const char *node) {
521         char unblock_cmd[100];
522         char *node_ip;
523
524         node_ip = get_container_ip(node);
525         assert(node_ip);
526         assert(snprintf(unblock_cmd, sizeof(unblock_cmd), "iptables -D OUTPUT -p all -s %s -j DROP", node_ip) >= 0);
527         run_in_container(unblock_cmd, node, false);
528
529         assert(snprintf(unblock_cmd, sizeof(unblock_cmd), "iptables -D INPUT -p all -s %s -j DROP", node_ip) >= 0);
530         run_in_container(unblock_cmd, node, false);
531
532         assert(snprintf(unblock_cmd, sizeof(unblock_cmd), "iptables -D FORWARD -p all -s %s -j DROP", node_ip) >= 0);
533         run_in_container(unblock_cmd, node, false);
534 }
535
536 /* Takes bridgeName as input parameter and creates a bridge */
537 void create_bridge(const char *bridgeName) {
538         char command[100] = "brctl addbr ";
539         strcat(command, bridgeName);
540         int create_bridge_status = system(command);
541         assert(create_bridge_status == 0);
542         PRINT_TEST_CASE_MSG("%s bridge created\n", bridgeName);
543 }
544
545 /* Add interface for the bridge created */
546 void add_interface(const char *bridgeName, const char *interfaceName) {
547         char command[100] = "brctl addif ";
548         char cmd[100] = "dhclient ";
549
550         strcat(command, bridgeName);
551         strcat(command, " ");
552         strcat(command, interfaceName);
553         int addif_status = system(command);
554         assert(addif_status == 0);
555         strcat(cmd, bridgeName);
556         int dhclient_status = system(cmd);
557         assert(dhclient_status == 0);
558         PRINT_TEST_CASE_MSG("Added interface for %s\n", bridgeName);
559 }
560
561 /* Create a veth pair and bring them up */
562 void add_veth_pair(const char *vethName1, const char *vethName2) {
563         char command[100] = "ip link add ";
564         char upCommand1[100] = "ip link set ";
565         char upCommand2[100] = "ip link set ";
566
567         strcat(command, vethName1);
568         strcat(command, " type veth peer name ");
569         strcat(command, vethName2);
570         int link_add_status = system(command);
571         assert(link_add_status == 0);
572         strcat(upCommand1, vethName1);
573         strcat(upCommand1, " up");
574         int link_set_veth1_status = system(upCommand1);
575         assert(link_set_veth1_status == 0);
576         strcat(upCommand2, vethName2);
577         strcat(upCommand2, " up");
578         int link_set_veth2_status = system(upCommand2);
579         assert(link_set_veth2_status == 0);
580         PRINT_TEST_CASE_MSG("Added veth pairs %s and %s\n", vethName1, vethName2);
581 }
582
583 /* Bring the interface up for the bridge created */
584 void bring_if_up(const char *bridgeName) {
585         char command[300] = "ifconfig ";
586         char dhcommand[300] = "dhclient ";
587         strcat(command, bridgeName);
588         strcat(command, " up");
589         int if_up_status = system(command);
590         assert(if_up_status == 0);
591         sleep(2);
592         PRINT_TEST_CASE_MSG("Interface brought up for %s created\n", bridgeName);
593 }
594
595 /**
596  * Replace all occurrences of a given a word in string.
597  */
598 void replaceAll(char *str, const char *oldWord, const char *newWord) {
599         char *pos, temp[BUFSIZ];
600         int index = 0;
601         int owlen;
602         owlen = strlen(oldWord);
603
604         while((pos = strstr(str, oldWord)) != NULL) {
605                 strcpy(temp, str);
606                 index = pos - str;
607                 str[index] = '\0';
608                 strcat(str, newWord);
609                 strcat(str, temp + index + owlen);
610         }
611 }
612
613 /* Switches the bridge for a given container */
614 void switch_bridge(const char *containerName, const char *currentBridge, const char *newBridge) {
615         char command[100] = "lxc-stop -n ";
616         char command_start[100] = "lxc-start -n ";
617         PRINT_TEST_CASE_MSG("Switching %s container to %s\n", containerName, newBridge);
618         strcat(command, containerName);
619         strcat(command_start, containerName);
620         int container_stop_status = system(command);
621         assert(container_stop_status == 0);
622         sleep(2);
623         FILE *fPtr;
624         FILE *fTemp;
625         char path[300] = "/var/lib/lxc/";
626         strcat(path, containerName);
627         strcat(path, "/config");
628
629         char buffer[BUFSIZ];
630         /*  Open all required files */
631         fPtr  = fopen(path, "r");
632         fTemp = fopen("replace.tmp", "w");
633
634         if(fPtr == NULL || fTemp == NULL) {
635                 PRINT_TEST_CASE_MSG("\nUnable to open file.\n");
636                 PRINT_TEST_CASE_MSG("Please check whether file exists and you have read/write privilege.\n");
637                 exit(EXIT_SUCCESS);
638         }
639
640         while((fgets(buffer, BUFSIZ, fPtr)) != NULL) {
641                 replaceAll(buffer, currentBridge, newBridge);
642                 fputs(buffer, fTemp);
643         }
644
645         fclose(fPtr);
646         fclose(fTemp);
647         remove(path);
648         rename("replace.tmp", path);
649         PRINT_TEST_CASE_MSG("Switching procedure done successfully\n");
650         int container_start_status = system(command_start);
651         assert(container_start_status == 0);
652         sleep(2);
653 }
654
655 /* Bring the interface down for the bridge created */
656 void bring_if_down(const char *bridgeName) {
657         char command[300] = "ip link set dev ";
658         strcat(command, bridgeName);
659         strcat(command, " down");
660         int if_down_status = system(command);
661         assert(if_down_status == 0);
662         PRINT_TEST_CASE_MSG("Interface brought down for %s created\n", bridgeName);
663 }
664
665 /* Delete interface for the bridge created */
666 void del_interface(const char *bridgeName, const char *interfaceName) {
667         char command[300] = "brctl delif ";
668         strcat(command, bridgeName);
669         strcat(command, interfaceName);
670         int if_delete_status = system(command);
671         assert(if_delete_status == 0);
672         PRINT_TEST_CASE_MSG("Deleted interface for %s\n", bridgeName);
673 }
674
675 /* Takes bridgeName as input parameter and deletes a bridge */
676 void delete_bridge(const char *bridgeName) {
677         bring_if_down(bridgeName);
678         char command[300] = "brctl delbr ";
679         strcat(command, bridgeName);
680         int bridge_delete = system(command);
681         assert(bridge_delete == 0);
682         PRINT_TEST_CASE_MSG("%s bridge deleted\n", bridgeName);
683         sleep(2);
684 }
685
686 /* Creates container on a specified bridge with added interface */
687 void create_container_on_bridge(const char *containerName, const char *bridgeName, const char *ifName) {
688         char command[100] = "lxc-create -t download -n ";
689         char cmd[100] = " -- -d ubuntu -r trusty -a ";
690         char start[100] = "lxc-start -n ";
691         FILE *fPtr;
692         char path[300] = "/var/lib/lxc/";
693         strcat(path, containerName);
694         strcat(path, "/config");
695         strcat(command, containerName);
696         strcat(command, cmd);
697         strcat(command, choose_arch);
698         int container_create_status = system(command);
699         assert(container_create_status == 0);
700         sleep(3);
701         assert(fPtr = fopen(path, "a+"));
702         fprintf(fPtr, "lxc.net.0.name = eth0\n");
703         fprintf(fPtr, "\n");
704         fprintf(fPtr, "lxc.net.1.type = veth\n");
705         fprintf(fPtr, "lxc.net.1.flags = up\n");
706         fprintf(fPtr, "lxc.net.1.link = %s\n", bridgeName);
707         fprintf(fPtr, "lxc.net.1.name = %s\n", ifName);
708         fprintf(fPtr, "lxc.net.1.hwaddr = 00:16:3e:ab:xx:xx\n");
709         fclose(fPtr);
710         strcat(start, containerName);
711         int container_start_status = system(start);
712         assert(container_start_status == 0);
713         sleep(3);
714         PRINT_TEST_CASE_MSG("Created %s on %s with interface name %s\n", containerName, bridgeName, ifName);
715 }
716
717 /* Configures dnsmasq and iptables for the specified container with inputs of listen address and dhcp range */
718 void config_dnsmasq(const char *containerName, const char *ifName, const char *listenAddress, const char *dhcpRange) {
719         char command[500] = "echo \"apt-get install dnsmasq iptables -y\" | lxc-attach -n ";
720         strcat(command, containerName);
721         strcat(command, " --");
722         int iptables_install_status = system(command);
723         assert(iptables_install_status == 0);
724         sleep(5);
725         char com1[300] = "echo \"echo \"interface=eth1\" >> /etc/dnsmasq.conf\" | lxc-attach -n ";
726         strcat(com1, containerName);
727         strcat(com1, " --");
728         int dnsmasq_status = system(com1);
729         assert(dnsmasq_status == 0);
730         sleep(5);
731         char com2[300] = "echo \"echo \"bind-interfaces\" >> /etc/dnsmasq.conf\" | lxc-attach -n ";
732         strcat(com2, containerName);
733         strcat(com2, " --");
734         dnsmasq_status = system(com2);
735         assert(dnsmasq_status == 0);
736         sleep(5);
737         char com3[300] = "echo \"echo \"listen-address=";
738         strcat(com3, listenAddress);
739         strcat(com3, "\" >> /etc/dnsmasq.conf\" | lxc-attach -n ");
740         strcat(com3, containerName);
741         strcat(com3, " --");
742         dnsmasq_status = system(com3);
743         assert(dnsmasq_status == 0);
744         sleep(5);
745         char com4[300] = "echo \"echo \"dhcp-range=";
746         strcat(com4, dhcpRange);
747         strcat(com4, "\" >> /etc/dnsmasq.conf\" | lxc-attach -n ");
748         strcat(com4, containerName);
749         strcat(com4, " --");
750         dnsmasq_status = system(com4);
751         assert(dnsmasq_status == 0);
752         sleep(5);
753         char cmd[300] = "echo \"ifconfig ";
754         strcat(cmd, ifName);
755         strcat(cmd, " ");
756         strcat(cmd, listenAddress);
757         strcat(cmd, " netmask 255.255.255.0 up\" | lxc-attach -n ");
758         strcat(cmd, containerName);
759         strcat(cmd, " --");
760         dnsmasq_status = system(cmd);
761         assert(dnsmasq_status == 0);
762         sleep(2);
763         char com[500] = "echo \"service dnsmasq restart >> /dev/null\" | lxc-attach -n ";
764         strcat(com, containerName);
765         strcat(com, " --");
766         dnsmasq_status = system(com);
767         assert(dnsmasq_status == 0);
768         sleep(2);
769         PRINT_TEST_CASE_MSG("Configured dnsmasq in %s with interface name %s, listen-address = %s, dhcp-range = %s\n", containerName, ifName, listenAddress, dhcpRange);
770 }
771
772 /* Configure the NAT rules inside the container */
773 void config_nat(const char *containerName, const char *listenAddress) {
774         char *last_dot_in_ip;
775         int last_ip_byte = 0;
776         char new_ip[300] = {0};
777         strncpy(new_ip, listenAddress, sizeof(new_ip));
778         assert(last_dot_in_ip = strrchr(new_ip, '.'));
779         assert(snprintf(last_dot_in_ip + 1, 4, "%d", last_ip_byte) >= 0);
780         char comd[300] = "echo \"iptables -t nat -A POSTROUTING -s ";
781         strcat(comd, new_ip);
782         strcat(comd, "/24 ! -d ");
783         strcat(comd, new_ip);
784         strcat(comd, "/24 -j MASQUERADE\" | lxc-attach -n ");
785         strcat(comd, containerName);
786         strcat(comd, " --");
787         int conf_nat_status = system(comd);
788         assert(conf_nat_status == 0);
789         sleep(2);
790         PRINT_TEST_CASE_MSG("Configured NAT on %s\n", containerName);
791 }
792
793 /* Creates a NAT layer on a specified bridge with certain dhcp range to allocate ips for nodes */
794 void create_nat_layer(const char *containerName, const char *bridgeName, const char *ifName, const char *listenAddress, char *dhcpRange) {
795         create_bridge(bridgeName);
796         bring_if_up(bridgeName);
797         create_container_on_bridge(containerName, bridgeName, ifName);
798         config_dnsmasq(containerName, ifName, listenAddress, dhcpRange);
799         config_nat(containerName, listenAddress);
800         PRINT_TEST_CASE_MSG("NAT layer created with %s\n", containerName);
801 }
802
803 /* Destroys the NAT layer created */
804 void destroy_nat_layer(const char *containerName, const char *bridgeName) {
805         bring_if_down(bridgeName);
806         delete_bridge(bridgeName);
807         char command[100] = "lxc-stop -n ";
808         strcat(command, containerName);
809         int container_stop_status = system(command);
810         assert(container_stop_status == 0);
811         char destroy[100] = "lxc-destroy -n ";
812         strcat(destroy, containerName);
813         strcat(destroy, " -s");
814         int container_destroy_status = system(destroy);
815         assert(container_destroy_status == 0);
816         PRINT_TEST_CASE_MSG("NAT layer destroyed with %s\n", containerName);
817 }
818
819 /* Add incoming firewall rules for ipv4 addresses with packet type and port number */
820 void incoming_firewall_ipv4(const char *packetType, int portNumber) {
821         char buf[5];
822         snprintf(buf, sizeof(buf), "%d", portNumber);
823         assert(system("iptables -F") == 0);
824         assert(system("iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") == 0);
825         assert(system("iptables -A INPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT") == 0);
826         char command[100] = "iptables -A INPUT -p ";
827         strcat(command, packetType);
828         strcat(command, " --dport ");
829         strcat(command, buf);
830         strcat(command, " -j ACCEPT");
831         assert(system(command) == 0);
832         sleep(2);
833         assert(system("iptables -A INPUT -j DROP") == 0);
834         PRINT_TEST_CASE_MSG("Firewall for incoming requests added on IPv4");
835         assert(system("iptables -L") == 0);
836 }
837
838 /* Add incoming firewall rules for ipv6 addresses with packet type and port number */
839 void incoming_firewall_ipv6(const char *packetType, int portNumber) {
840         char buf[5];
841         snprintf(buf, sizeof(buf), "%d", portNumber);
842         assert(system("ip6tables -F") == 0);
843         assert(system("ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") == 0);
844         assert(system("ip6tables -A INPUT -s ::1 -d ::1 -j ACCEPT") == 0);
845         char command[100] = "ip6tables -A INPUT -p ";
846         strcat(command, packetType);
847         strcat(command, " --dport ");
848         strcat(command, buf);
849         strcat(command, " -j ACCEPT");
850         assert(system(command) == 0);
851         sleep(2);
852         assert(system("ip6tables -A INPUT -j DROP") == 0);
853         PRINT_TEST_CASE_MSG("Firewall for incoming requests added on IPv6");
854         assert(system("ip6tables -L") == 0);
855 }
856
857 /* Add outgoing firewall rules for ipv4 addresses with packet type and port number */
858 void outgoing_firewall_ipv4(const char *packetType, int portNumber) {
859         char buf[5];
860         snprintf(buf, sizeof(buf), "%d", portNumber);
861         assert(system("iptables -F") == 0);
862         assert(system("iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") == 0);
863         assert(system("iptables -A OUTPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT") == 0);
864         char command[100] = "iptables -A OUTPUT -p ";
865         strcat(command, packetType);
866         strcat(command, " --dport ");
867         strcat(command, buf);
868         strcat(command, " -j ACCEPT");
869         assert(system(command) == 0);
870         sleep(2);
871         assert(system("iptables -A OUTPUT -j DROP") == 0);
872         PRINT_TEST_CASE_MSG("Firewall for outgoing requests added on IPv4");
873         assert(system("iptables -L") == 0);
874 }
875
876 /* Add outgoing firewall rules for ipv6 addresses with packet type and port number */
877 void outgoing_firewall_ipv6(const char *packetType, int portNumber) {
878         char buf[5];
879         snprintf(buf, sizeof(buf), "%d", portNumber);
880         assert(system("ip6tables -F") == 0);
881         assert(system("ip6tables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") == 0);
882         assert(system("ip6tables -A OUTPUT -s ::1 -d ::1 -j ACCEPT") == 0);
883         char command[100] = "ip6tables -A OUTPUT -p ";
884         strcat(command, packetType);
885         strcat(command, " --dport ");
886         strcat(command, buf);
887         strcat(command, " -j ACCEPT");
888         assert(system(command) == 0);
889         sleep(2);
890         assert(system("ip6tables -A OUTPUT -j DROP") == 0);
891         PRINT_TEST_CASE_MSG("Firewall for outgoing requests added on IPv6");
892         assert(system("ip6tables -L") == 0);
893 }