]> git.meshlink.io Git - meshlink/blob - test/blackbox/common/containers.c
Fix compiler warnings in the test suites.
[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 char *run_in_container(const char *cmd, const char *container_name, bool daemonize) {
73         char container_find_name[1000];
74         struct lxc_container *container;
75
76         assert(cmd);
77         assert(container_name);
78         assert(snprintf(container_find_name, sizeof(container_find_name), "%s_%s",
79                         state_ptr->test_case_name, container_name) >= 0);
80         assert((container = find_container(container_find_name)));
81
82         return run_in_container_ex(cmd, container, daemonize);
83 }
84
85 char *execute_in_container(const char *cmd, const char *container_name, bool daemonize) {
86         struct lxc_container *container;
87
88         assert(cmd);
89         assert(container_name);
90         assert((container = find_container(container_name)));
91
92         return run_in_container_ex(cmd, container, daemonize);
93 }
94
95 /* Run 'cmd' inside the Container created for 'node' and return the first line of the output
96     or NULL if there is no output - useful when, for example, a meshlink invite is generated
97     by a node running inside a Container
98     'cmd' is run as a daemon if 'daemonize' is true - this mode is useful for running node
99     simulations in Containers
100     The caller is responsible for freeing the returned string */
101 char *run_in_container_ex(const char *cmd, struct lxc_container *container, bool daemonize) {
102         char *output = NULL;
103         size_t output_len = 0;
104
105         /* Run the command within the Container, either as a daemon or foreground process */
106         /* TO DO: Perform this operation using the LXC API - currently does not work using the API
107             Need to determine why it doesn't work, and make it work */
108         if(daemonize) {
109                 char run_script_path[100];
110                 char *attach_argv[4];
111
112                 assert(snprintf(run_script_path, sizeof(run_script_path), "%s/" LXC_UTIL_REL_PATH "/" LXC_RUN_SCRIPT,
113                                 meshlink_root_path) >= 0);
114                 attach_argv[0] = run_script_path;
115                 attach_argv[1] = (char *)cmd;
116                 attach_argv[2] = container->name;
117                 attach_argv[3] = NULL;
118
119                 /* To daemonize, create a child process and detach it from its parent (this program) */
120                 if(fork() == 0) {
121                         assert(daemon(1, 0) != -1);    // Detach from the parent process
122                         assert(execv(attach_argv[0], attach_argv) != -1);   // Run exec() in the child process
123                 }
124         } else {
125                 char *attach_command;
126                 size_t attach_command_len;
127                 int i;
128                 attach_command_len = strlen(meshlink_root_path) + strlen(LXC_UTIL_REL_PATH) + strlen(LXC_RUN_SCRIPT) + strlen(cmd) + strlen(container->name) + 10;
129                 attach_command = malloc(attach_command_len);
130                 assert(attach_command);
131
132                 assert(snprintf(attach_command, attach_command_len,
133                                 "%s/" LXC_UTIL_REL_PATH "/" LXC_RUN_SCRIPT " \"%s\" %s", meshlink_root_path, cmd,
134                                 container->name) >= 0);
135                 FILE *attach_fp;
136                 assert((attach_fp = popen(attach_command, "r")));
137                 free(attach_command);
138                 /* If the command has an output, strip out any trailing carriage returns or newlines and
139                     return it, otherwise return NULL */
140                 output = NULL;
141                 output_len = 0;
142
143                 if(getline(&output, &output_len, attach_fp) != -1) {
144                         i = strlen(output) - 1;
145
146                         while(output[i] == '\n' || output[i] == '\r') {
147                                 i--;
148                         }
149
150                         output[i + 1] = '\0';
151                 } else {
152                         free(output);
153                         output = NULL;
154                 }
155
156                 assert(pclose(attach_fp) != -1);
157         }
158
159         return output;
160 }
161
162 /* Wait for a starting Container to obtain an IP Address, then save that IP for future use */
163 void container_wait_ip(int node) {
164         char container_name[100];
165         char *ip;
166
167         assert(snprintf(container_name, sizeof(container_name), "%s_%s", state_ptr->test_case_name,
168                         state_ptr->node_names[node]) >= 0);
169         ip = container_wait_ip_ex(container_name);
170
171         strncpy(container_ips[node], ip, sizeof(container_ips[node])); // Save the IP for future use
172         PRINT_TEST_CASE_MSG("Node '%s' has IP Address %s\n", state_ptr->node_names[node],
173                             container_ips[node]);
174         free(ip);
175 }
176
177 char *container_wait_ip_ex(const char *container_name) {
178         struct lxc_container *test_container;
179         char  lxcls_command[200];
180         char *ip;
181         size_t ip_len;
182         bool ip_found;
183         int i;
184         int timeout;
185         FILE *lxcls_fp;
186
187         assert((test_container = find_container(container_name)));
188         assert(snprintf(lxcls_command, sizeof(lxcls_command),
189                         "lxc-ls -f | grep %s | tr -s ' ' | cut -d ' ' -f 5", test_container->name) >= 0);
190         PRINT_TEST_CASE_MSG("Waiting for Container '%s' to acquire IP\n", test_container->name);
191         assert((ip = malloc(20)));
192         ip_len = sizeof(ip);
193         ip_found = false;
194         timeout = 60;
195
196         while(!ip_found && timeout) {
197                 assert((lxcls_fp = popen(lxcls_command, "r")));   // Run command
198                 assert(getline((char **)&ip, &ip_len, lxcls_fp) != -1); // Read its output
199                 /* Strip newlines and carriage returns from output */
200                 i = strlen(ip) - 1;
201
202                 while(ip[i] == '\n' || ip[i] == '\r') {
203                         i--;
204                 }
205
206                 ip[i + 1] = '\0';
207                 ip_found = (strcmp(ip, "-") != 0);  // If the output is not "-", IP has been acquired
208                 assert(pclose(lxcls_fp) != -1);
209                 sleep(1);
210                 timeout--;
211         }
212
213         // Fail if IP cannot be read
214         assert(timeout);
215
216         return ip;
217 }
218
219 /* Create all required test containers */
220 void create_containers(const char *node_names[], int num_nodes) {
221         int i;
222         char container_name[100];
223         int create_status, snapshot_status, snap_restore_status;
224         struct lxc_container *first_container = NULL;
225
226         for(i = 0; i < num_nodes; i++) {
227                 assert(snprintf(container_name, sizeof(container_name), "run_%s", node_names[i]) >= 0);
228
229                 /* If this is the first Container, create it otherwise restore the snapshot saved
230                     for the first Container to create an additional Container */
231                 if(i == 0) {
232                         assert((first_container = lxc_container_new(container_name, NULL)));
233                         assert(!first_container->is_defined(first_container));
234                         create_status = first_container->createl(first_container, "download", NULL, NULL,
235                                         LXC_CREATE_QUIET, "-d", "ubuntu", "-r", "trusty", "-a", choose_arch, NULL);
236                         fprintf(stderr, "Container '%s' create status: %d - %s\n", container_name,
237                                 first_container->error_num, first_container->error_string);
238                         assert(create_status);
239                         snapshot_status = first_container->snapshot(first_container, NULL);
240                         fprintf(stderr, "Container '%s' snapshot status: %d - %s\n", container_name,
241                                 first_container->error_num, first_container->error_string);
242                         assert(snapshot_status != -1);
243                 } else {
244                         assert(first_container);
245                         snap_restore_status = first_container->snapshot_restore(first_container, "snap0",
246                                               container_name);
247                         fprintf(stderr, "Snapshot restore to Container '%s' status: %d - %s\n", container_name,
248                                 first_container->error_num, first_container->error_string);
249                         assert(snap_restore_status);
250                 }
251         }
252 }
253
254 /* Setup Containers required for a test
255     This function should always be invoked in a CMocka context
256     after setting the state of the test case to an instance of black_box_state_t */
257 void setup_containers(void **state) {
258         black_box_state_t *test_state = (black_box_state_t *)(*state);
259         int i;
260         char build_command[200];
261         struct lxc_container *test_container, *new_container;
262         char container_find_name[100];
263         char container_new_name[100];
264         int create_status, build_status;
265
266         PRINT_TEST_CASE_HEADER();
267
268         for(i = 0; i < test_state->num_nodes; i++) {
269                 /* Find the run_<node-name> Container or create it if it doesn't exist */
270                 assert(snprintf(container_find_name, sizeof(container_find_name), "run_%s",
271                                 test_state->node_names[i]) >= 0);
272
273                 if(!(test_container = find_container(container_find_name))) {
274                         assert((test_container = lxc_container_new(container_find_name, NULL)));
275                         assert(!test_container->is_defined(test_container));
276                         create_status = test_container->createl(test_container, "download", NULL, NULL,
277                                                                 LXC_CREATE_QUIET, "-d", "ubuntu", "-r", "trusty", "-a", choose_arch, NULL);
278                         PRINT_TEST_CASE_MSG("Container '%s' create status: %d - %s\n", container_find_name,
279                                             test_container->error_num, test_container->error_string);
280                         assert(create_status);
281                 }
282
283                 /* Stop the Container if it's running */
284                 test_container->shutdown(test_container, CONTAINER_SHUTDOWN_TIMEOUT);
285                 /* Call stop() in case shutdown() fails
286                     One of these two calls will always succeed */
287                 test_container->stop(test_container);
288                 /* Rename the Container to make it specific to this test case,
289                     if a Container with the target name already exists, skip this step */
290                 assert(snprintf(container_new_name, sizeof(container_new_name), "%s_%s",
291                                 test_state->test_case_name, test_state->node_names[i]) >= 0);
292
293                 if(!(new_container = find_container(container_new_name))) {
294                         rename_container(test_container->name, container_new_name);
295                         assert((new_container = find_container(container_new_name)));
296                 }
297
298                 /* Start the Container */
299                 assert(new_container->start(new_container, 0, NULL));
300                 /* Build the Container by copying required files into it */
301                 assert(snprintf(build_command, sizeof(build_command),
302                                 "%s/" LXC_UTIL_REL_PATH "/" LXC_BUILD_SCRIPT " %s %s %s +x >/dev/null",
303                                 meshlink_root_path, test_state->test_case_name, test_state->node_names[i],
304                                 meshlink_root_path) >= 0);
305                 build_status = system(build_command);
306                 PRINT_TEST_CASE_MSG("Container '%s' build Status: %d\n", new_container->name,
307                                     build_status);
308                 assert(build_status == 0);
309                 /* Restart the Container after building it and wait for it to acquire an IP */
310                 new_container->shutdown(new_container, CONTAINER_SHUTDOWN_TIMEOUT);
311                 new_container->stop(new_container);
312                 new_container->start(new_container, 0, NULL);
313                 container_wait_ip(i);
314         }
315 }
316
317 /* Destroy all Containers with names containing 'run_' - Containers saved for debugging will
318     have names beginning with test_case_ ; 'run_' is reserved for temporary Containers
319     intended to be re-used for the next test */
320 void destroy_containers(void) {
321         struct lxc_container **test_containers;
322         char **container_names;
323         int num_containers, i;
324
325         assert((num_containers = list_all_containers(lxc_path, &container_names,
326                                  &test_containers)) != -1);
327
328         for(i = 0; i < num_containers; i++) {
329                 if(strstr(container_names[i], "run_")) {
330                         fprintf(stderr, "Destroying Container '%s'\n", container_names[i]);
331                         /* Stop the Container - it cannot be destroyed till it is stopped */
332                         test_containers[i]->shutdown(test_containers[i], CONTAINER_SHUTDOWN_TIMEOUT);
333                         /* Call stop() in case shutdown() fails
334                             One of these two calls will always succeed */
335                         test_containers[i]->stop(test_containers[i]);
336                         /* Destroy the Container */
337                         test_containers[i]->destroy(test_containers[i]);
338                         /* Call destroy_with_snapshots() in case destroy() fails
339                             one of these two calls will always succeed */
340                         test_containers[i]->destroy_with_snapshots(test_containers[i]);
341                 }
342         }
343 }
344
345 /* Restart all the Containers being used in the current test case i.e. Containers with
346     names beginning with <test-case-name>_<node-name> */
347 void restart_all_containers(void) {
348         char container_name[100];
349         struct lxc_container *test_container;
350         int i;
351
352         for(i = 0; i < state_ptr->num_nodes; i++) {
353                 /* Shutdown, then start the Container, then wait for it to acquire an IP Address */
354                 assert(snprintf(container_name, sizeof(container_name), "%s_%s", state_ptr->test_case_name,
355                                 state_ptr->node_names[i]) >= 0);
356                 assert((test_container = find_container(container_name)));
357                 test_container->shutdown(test_container, CONTAINER_SHUTDOWN_TIMEOUT);
358                 test_container->stop(test_container);
359                 test_container->start(test_container, 0, NULL);
360                 container_wait_ip(i);
361         }
362 }
363
364 /* Run the gen_invite command inside the 'inviter' container to generate an invite
365     for 'invitee', and return the generated invite which is output on the terminal */
366 char *invite_in_container(const char *inviter, const char *invitee) {
367         char invite_command[200];
368         char *invite_url;
369
370         assert(snprintf(invite_command, sizeof(invite_command),
371                         "LD_LIBRARY_PATH=/home/ubuntu/test/.libs /home/ubuntu/test/gen_invite %s %s "
372                         "2> gen_invite.log", inviter, invitee) >= 0);
373         assert((invite_url = run_in_container(invite_command, inviter, false)));
374         PRINT_TEST_CASE_MSG("Invite Generated from '%s' to '%s': %s\n", inviter,
375                             invitee, invite_url);
376
377         return invite_url;
378 }
379
380 /* Run the gen_invite command inside the 'inviter' container to generate an invite
381     for 'invitee' belonging to pparticular submesh, and return the generated invite
382     which is output on the terminal */
383 char *submesh_invite_in_container(const char *inviter, const char *invitee, const char *submesh) {
384         char invite_command[200];
385         char *invite_url;
386
387         assert(snprintf(invite_command, sizeof(invite_command),
388                         "LD_LIBRARY_PATH=/home/ubuntu/test/.libs /home/ubuntu/test/gen_invite %s %s %s "
389                         "2> gen_invite.log", inviter, invitee, submesh) >= 0);
390         assert((invite_url = run_in_container(invite_command, inviter, false)));
391         PRINT_TEST_CASE_MSG("Invite Generated from '%s' to '%s': %s\n", inviter,
392                             invitee, invite_url);
393
394         return invite_url;
395 }
396
397 /* Run the node_sim_<nodename> program inside the 'node''s container */
398 void node_sim_in_container(const char *node, const char *device_class, const char *invite_url) {
399         char *node_sim_command;
400         size_t node_sim_command_len;
401
402         node_sim_command_len = 500 + (invite_url ? strlen(invite_url) : 0);
403         node_sim_command = calloc(1, node_sim_command_len);
404         assert(node_sim_command);
405         assert(snprintf(node_sim_command, node_sim_command_len,
406                         "LD_LIBRARY_PATH=/home/ubuntu/test/.libs /home/ubuntu/test/node_sim_%s %s %s %s "
407                         "1>&2 2>> node_sim_%s.log", node, node, device_class,
408                         (invite_url) ? invite_url : "", node) >= 0);
409         run_in_container(node_sim_command, node, true);
410         PRINT_TEST_CASE_MSG("node_sim_%s started in Container\n", node);
411
412         free(node_sim_command);
413 }
414
415 /* Run the node_sim_<nodename> program inside the 'node''s container with event handling capable */
416 void node_sim_in_container_event(const char *node, const char *device_class,
417                                  const char *invite_url, const char *clientId, const char *import) {
418         char *node_sim_command;
419         size_t node_sim_command_len;
420
421         assert(node && device_class && clientId && import);
422         node_sim_command_len = 500 + (invite_url ? strlen(invite_url) : 0);
423         node_sim_command = calloc(1, node_sim_command_len);
424         assert(node_sim_command);
425         assert(snprintf(node_sim_command, node_sim_command_len,
426                         "LD_LIBRARY_PATH=/home/ubuntu/test/.libs /home/ubuntu/test/node_sim_%s %s %s %s %s %s "
427                         "1>&2 2>> node_sim_%s.log", node, node, device_class,
428                         clientId, import, (invite_url) ? invite_url : "", node) >= 0);
429         run_in_container(node_sim_command, node, true);
430         PRINT_TEST_CASE_MSG("node_sim_%s(Client Id :%s) started in Container with event handling\n%s\n",
431                             node, clientId, node_sim_command);
432
433         free(node_sim_command);
434 }
435
436 /* Run the node_step.sh script inside the 'node''s container to send the 'sig' signal to the
437     node_sim program in the container */
438 void node_step_in_container(const char *node, const char *sig) {
439         char node_step_command[1000];
440
441         assert(snprintf(node_step_command, sizeof(node_step_command),
442                         "/home/ubuntu/test/node_step.sh lt-node_sim_%s %s 1>&2 2> node_step.log",
443                         node, sig) >= 0);
444         run_in_container(node_step_command, node, false);
445         PRINT_TEST_CASE_MSG("Signal %s sent to node_sim_%s\n", sig, node);
446 }
447
448 /* Change the IP Address of the Container running 'node'
449     Changes begin from X.X.X.254 and continue iteratively till an available address is found */
450 void change_ip(int node) {
451         char *gateway_addr;
452         char new_ip[20] = "";
453         char *netmask;
454         char *last_dot_in_ip;
455         int last_ip_byte = 254;
456         FILE *if_fp;
457         char copy_command[200];
458         char container_name[100];
459         struct lxc_container *container;
460         int copy_file_stat;
461
462         /* Get IP Address of LXC Bridge Interface - this will be set up as the Gateway Address
463             of the Static IP assigned to the Container */
464         assert((gateway_addr = get_ip(lxc_bridge)));
465         /* Get Netmask of LXC Brdige Interface */
466         assert((netmask = get_netmask(lxc_bridge)));
467
468         /* Replace last byte of Container's IP with 254 to form the new Container IP */
469         assert(container_ips[node]);
470         strncpy(new_ip, container_ips[node], sizeof(new_ip) - 1);
471         assert((last_dot_in_ip = strrchr(new_ip, '.')));
472         assert(snprintf(last_dot_in_ip + 1, 4, "%d", last_ip_byte) >= 0);
473
474         /* Check that the new IP does not match the Container's existing IP
475             if it does, iterate till it doesn't */
476         /* TO DO: Make sure the IP does not conflict with any other running Container */
477         while(strcmp(new_ip, container_ips[node]) == 0) {
478                 last_ip_byte--;
479                 assert(snprintf(last_dot_in_ip + 1, 4, "%d", last_ip_byte) >= 0);
480         }
481
482         /* Create new 'interfaces' file for Container */
483         assert((if_fp = fopen("interfaces", "w")));
484         fprintf(if_fp, "auto lo\n");
485         fprintf(if_fp, "iface lo inet loopback\n");
486         fprintf(if_fp, "\n");
487         fprintf(if_fp, "auto eth0\n");
488         fprintf(if_fp, "iface eth0 inet static\n");
489         fprintf(if_fp, "\taddress %s\n", new_ip);
490         fprintf(if_fp, "\tnetmask %s\n", netmask);
491         fprintf(if_fp, "\tgateway %s\n", gateway_addr);
492         assert(fclose(if_fp) != EOF);
493
494         /* Copy 'interfaces' file into Container's /etc/network path */
495         assert(snprintf(copy_command, sizeof(copy_command),
496                         "%s/" LXC_UTIL_REL_PATH "/" LXC_COPY_SCRIPT " interfaces %s_%s /etc/network/interfaces",
497                         meshlink_root_path, state_ptr->test_case_name, state_ptr->node_names[node]) >= 0);
498         copy_file_stat = system(copy_command);
499         PRINT_TEST_CASE_MSG("Container '%s_%s' 'interfaces' file copy status: %d\n",
500                             state_ptr->test_case_name, state_ptr->node_names[node], copy_file_stat);
501         assert(copy_file_stat == 0);
502
503         /* Restart Container to apply new IP Address */
504         assert(snprintf(container_name, sizeof(container_name), "%s_%s", state_ptr->test_case_name,
505                         state_ptr->node_names[node]) >= 0);
506         assert((container = find_container(container_name)));
507         container->shutdown(container, CONTAINER_SHUTDOWN_TIMEOUT);
508         /* Call stop() in case shutdown() fails
509             One of these two calls with always succeed */
510         container->stop(container);
511         assert(container->start(container, 0, NULL));
512
513         strncpy(container_ips[node], new_ip, sizeof(container_ips[node]));   // Save the new IP Address
514         PRINT_TEST_CASE_MSG("Node '%s' IP Address changed to %s\n", state_ptr->node_names[node],
515                             container_ips[node]);
516 }
517
518 char **get_container_interface_ips(const char *container_name, const char *interface_name) {
519         char **ips;
520         struct lxc_container *container = find_container(container_name);
521         assert(container);
522
523         char **interfaces = container->get_interfaces(container);
524         assert(interfaces);
525
526         int i;
527         ips = NULL;
528
529         for(i = 0; interfaces[i]; i++) {
530                 if(!strcasecmp(interfaces[i], interface_name)) {
531                         ips = container->get_ips(container, interface_name, "inet", 0);
532                         assert(ips);
533                         break;
534                 }
535         }
536
537         free(interfaces);
538
539         return ips;
540 }
541
542 /* Install an app in a container */
543 void install_in_container(const char *node, const char *app) {
544         char install_cmd[100];
545
546         assert(snprintf(install_cmd, sizeof(install_cmd),
547                         "apt-get install %s -y >> /dev/null", app) >= 0);
548         run_in_container(install_cmd, node, false);
549         // TODO: Check in container whether app has installed or not with a timeout
550         sleep(10);
551 }
552
553 /* Return container's IP address */
554 char *get_container_ip(const char *node_name) {
555         int node = -1, i;
556
557         for(i = 0; i < state_ptr->num_nodes; i++) {
558                 if(!strcasecmp(state_ptr->node_names[i], node_name)) {
559                         node = i;
560                         break;
561                 }
562         }
563
564         if(i == state_ptr->num_nodes) {
565                 return NULL;
566         }
567
568         char *ip = strdup(container_ips[node]);
569         assert(ip);
570
571         return ip;
572 }
573
574 /* Simulate a network failure by adding NAT rule in the container with it's IP address */
575 void block_node_ip(const char *node) {
576         char block_cmd[100];
577         char *node_ip;
578
579         node_ip = get_container_ip(node);
580         assert(node_ip);
581         assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A OUTPUT -p all -s %s -j DROP", node_ip) >= 0);
582         run_in_container(block_cmd, node, false);
583
584         assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A INPUT -p all -s %s -j DROP", node_ip) >= 0);
585         run_in_container(block_cmd, node, false);
586
587         assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A FORWARD -p all -s %s -j DROP", node_ip) >= 0);
588         run_in_container(block_cmd, node, false);
589
590         free(node_ip);
591 }
592
593 void accept_port_rule(const char *node, const char *chain, const char *protocol, int port) {
594         char block_cmd[100];
595
596         assert(port >= 0 && port < 65536);
597         assert(!strcmp(chain, "INPUT") || !strcmp(chain, "FORWARD") || !strcmp(chain, "OUTPUT"));
598         assert(!strcmp(protocol, "all") || !strcmp(protocol, "tcp") || !strcmp(protocol, "udp") || !strcmp(protocol, "icmp"));
599         assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A %s -p %s --dport %d -j ACCEPT", chain, protocol, port) >= 0);
600         run_in_container(block_cmd, node, false);
601 }
602
603 /* Restore the network that was blocked before by the NAT rule with it's own IP address */
604 void unblock_node_ip(const char *node) {
605         char unblock_cmd[100];
606         char *node_ip;
607
608         node_ip = get_container_ip(node);
609         assert(node_ip);
610         assert(snprintf(unblock_cmd, sizeof(unblock_cmd), "iptables -D OUTPUT -p all -s %s -j DROP", node_ip) >= 0);
611         run_in_container(unblock_cmd, node, false);
612
613         assert(snprintf(unblock_cmd, sizeof(unblock_cmd), "iptables -D INPUT -p all -s %s -j DROP", node_ip) >= 0);
614         run_in_container(unblock_cmd, node, false);
615
616         assert(snprintf(unblock_cmd, sizeof(unblock_cmd), "iptables -D FORWARD -p all -s %s -j DROP", node_ip) >= 0);
617         run_in_container(unblock_cmd, node, false);
618 }
619
620 char *block_icmp(const char *container_name) {
621         char block_cmd[500];
622         assert(container_name);
623         assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A FORWARD -p icmp -j DROP") >= 0);
624         return execute_in_container(block_cmd, container_name, false);
625 }
626
627 char *unblock_icmp(const char *container_name) {
628         char block_cmd[500];
629         assert(container_name);
630         assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -D FORWARD -p icmp -j DROP") >= 0);
631         return execute_in_container(block_cmd, container_name, false);
632 }
633
634 char *change_container_mtu(const char *container_name, const char *interface_name, int mtu) {
635         char cmd[500];
636         assert(container_name);
637         assert(snprintf(cmd, sizeof(cmd), "ifconfig %s mtu %d", interface_name, mtu) >= 0);
638         return execute_in_container(cmd, container_name, false);
639 }
640
641 char *flush_conntrack(const char *container_name) {
642         assert(container_name);
643
644         return execute_in_container("conntrack -F", container_name, false);
645 }
646
647 void flush_nat_rules(const char *container_name, const char *chain) {
648         char *ret;
649         char flush_cmd[500];
650
651         assert(container_name);
652         assert(snprintf(flush_cmd, sizeof(flush_cmd), "iptables -F %s", chain ? chain : "") >= 0);
653         ret = execute_in_container("iptables -F", container_name, false);
654         assert(ret == NULL);
655 }
656
657 void add_full_cone_nat_rules(const char *container_name, const char *pub_interface, const char *priv_interface_listen_address) {
658         char nat_cmd[500];
659
660         char **pub_interface_ips = get_container_interface_ips(container_name, pub_interface);
661         assert(pub_interface_ips[0]);
662         char *pub_interface_ip = pub_interface_ips[0];
663
664         assert(snprintf(nat_cmd, sizeof(nat_cmd),
665                         "%s/" LXC_UTIL_REL_PATH "/" LXC_NAT_FULL_CONE " %s %s %s %s >/dev/null",
666                         meshlink_root_path, container_name, pub_interface, pub_interface_ip, priv_interface_listen_address) >= 0);
667         assert(system(nat_cmd) == 0);
668         free(pub_interface_ips);
669 }
670
671 /* Create a NAT and a bridge, bridge connected to NAT and containers to be NATed can be switched
672     to the NAT bridge from lxcbr0 */
673 void nat_create(const char *nat_name, const char *nat_bridge, int nat_type) {
674         (void)nat_type;
675
676         char build_command[200];
677         assert(snprintf(build_command, sizeof(build_command),
678                         "%s/" LXC_UTIL_REL_PATH "/" LXC_NAT_BUILD " %s %s %s >/dev/stderr",
679                         meshlink_root_path, nat_name, nat_bridge, meshlink_root_path) >= 0);
680         assert(system(build_command) == 0);
681 }
682
683 void nat_destroy(const char *nat_name) {
684         char build_command[200];
685         assert(snprintf(build_command, sizeof(build_command),
686                         "%s/" LXC_UTIL_REL_PATH "/" LXC_NAT_DESTROY " %s +x >/dev/null",
687                         meshlink_root_path, nat_name) >= 0);
688         assert(system(build_command) == 0);
689 }
690
691 /* Switches a container from current bridge to a new bridge */
692 void container_switch_bridge(const char *container_name, char *lxc_conf_path, const char *current_bridge, const char *new_bridge) {
693         char config_path[500];
694         char buffer[500];
695         struct lxc_container *container;
696         char *lxc_path_temp;
697         char *ip;
698
699         PRINT_TEST_CASE_MSG("Switiching container %s from bridge '%s' to bridge '%s'", container_name, current_bridge, new_bridge);
700         lxc_path_temp = lxc_path;
701         lxc_path = lxc_conf_path;
702         container = find_container(container_name);
703         assert(container);
704         lxc_path = lxc_path_temp;
705         assert(snprintf(config_path, sizeof(config_path), "%s/%s/config", lxc_conf_path, container_name) >= 0);
706         FILE *fp = fopen(config_path, "r");
707         assert(fp);
708         FILE *fp_temp = fopen(".temp_file", "w");
709         assert(fp_temp);
710
711         int net_no;
712
713         while((fgets(buffer, sizeof(buffer), fp)) != NULL) {
714                 if(sscanf(buffer, "lxc.net.%d.link", &net_no) == 1) {
715                         char *ptr;
716                         int len;
717
718                         if((ptr = strstr(buffer, current_bridge)) != NULL) {
719                                 len = strlen(current_bridge);
720
721                                 if(((*(ptr - 1) == ' ') || (*(ptr - 1) == '\t') || (*(ptr - 1) == '=')) && ((ptr[len] == '\n') || (ptr[len] == '\t') || (ptr[len] == '\0') || (ptr[len] == ' '))) {
722                                         sprintf(buffer, "lxc.net.%d.link = %s\n", net_no, new_bridge);
723                                 }
724                         }
725                 }
726
727                 fputs(buffer, fp_temp);
728         }
729
730         fclose(fp_temp);
731         fclose(fp);
732         remove(config_path);
733         rename(".temp_file", config_path);
734
735         /* Restart the Container after building it and wait for it to acquire an IP */
736         char cmd[200];
737         int sys_ret;
738         assert(snprintf(cmd, sizeof(cmd), "lxc-stop %s", container_name) >= 0);
739         sys_ret = system(cmd);
740         assert(snprintf(cmd, sizeof(cmd), "lxc-start %s", container_name) >= 0);
741         sys_ret = system(cmd);
742         assert(sys_ret == 0);
743         ip = container_wait_ip_ex(container_name);
744         PRINT_TEST_CASE_MSG("Obtained IP address: %s for container %s after switching bridge", ip, container_name);
745         free(ip);
746 }
747
748 /* Takes bridgeName as input parameter and creates a bridge */
749 void create_bridge(const char *bridgeName) {
750         char command[100] = "brctl addbr ";
751         strcat(command, bridgeName);
752         int create_bridge_status = system(command);
753         assert(create_bridge_status == 0);
754         PRINT_TEST_CASE_MSG("%s bridge created\n", bridgeName);
755 }
756
757 /* Add interface for the bridge created */
758 void add_interface(const char *bridgeName, const char *interfaceName) {
759         char command[100] = "brctl addif ";
760         char cmd[100] = "dhclient ";
761
762         strcat(command, bridgeName);
763         strcat(command, " ");
764         strcat(command, interfaceName);
765         int addif_status = system(command);
766         assert(addif_status == 0);
767         strcat(cmd, bridgeName);
768         int dhclient_status = system(cmd);
769         assert(dhclient_status == 0);
770         PRINT_TEST_CASE_MSG("Added interface for %s\n", bridgeName);
771 }
772
773 /* Create a veth pair and bring them up */
774 void add_veth_pair(const char *vethName1, const char *vethName2) {
775         char command[100] = "ip link add ";
776         char upCommand1[100] = "ip link set ";
777         char upCommand2[100] = "ip link set ";
778
779         strcat(command, vethName1);
780         strcat(command, " type veth peer name ");
781         strcat(command, vethName2);
782         int link_add_status = system(command);
783         assert(link_add_status == 0);
784         strcat(upCommand1, vethName1);
785         strcat(upCommand1, " up");
786         int link_set_veth1_status = system(upCommand1);
787         assert(link_set_veth1_status == 0);
788         strcat(upCommand2, vethName2);
789         strcat(upCommand2, " up");
790         int link_set_veth2_status = system(upCommand2);
791         assert(link_set_veth2_status == 0);
792         PRINT_TEST_CASE_MSG("Added veth pairs %s and %s\n", vethName1, vethName2);
793 }
794
795 /* Bring the interface up for the bridge created */
796 void bring_if_up(const char *bridgeName) {
797         char command[300] = "ifconfig ";
798         strcat(command, bridgeName);
799         strcat(command, " up");
800         int if_up_status = system(command);
801         assert(if_up_status == 0);
802         sleep(2);
803         PRINT_TEST_CASE_MSG("Interface brought up for %s created\n", bridgeName);
804 }
805
806 /**
807  * Replace all occurrences of a given a word in string.
808  */
809 void replaceAll(char *str, const char *oldWord, const char *newWord) {
810         char *pos, temp[BUFSIZ];
811         int index = 0;
812         int owlen;
813         owlen = strlen(oldWord);
814
815         while((pos = strstr(str, oldWord)) != NULL) {
816                 strcpy(temp, str);
817                 index = pos - str;
818                 str[index] = '\0';
819                 strcat(str, newWord);
820                 strcat(str, temp + index + owlen);
821         }
822 }
823
824 /* Switches the bridge for a given container */
825 void switch_bridge(const char *containerName, const char *currentBridge, const char *newBridge) {
826         char command[100] = "lxc-stop -n ";
827         char command_start[100] = "lxc-start -n ";
828         PRINT_TEST_CASE_MSG("Switching %s container to %s\n", containerName, newBridge);
829         strcat(command, containerName);
830         strcat(command_start, containerName);
831         int container_stop_status = system(command);
832         assert(container_stop_status == 0);
833         sleep(2);
834         FILE *fPtr;
835         FILE *fTemp;
836         char path[300] = "/var/lib/lxc/";
837         strcat(path, containerName);
838         strcat(path, "/config");
839
840         char buffer[BUFSIZ];
841         /*  Open all required files */
842         fPtr  = fopen(path, "r");
843         fTemp = fopen("replace.tmp", "w");
844
845         if(fPtr == NULL || fTemp == NULL) {
846                 PRINT_TEST_CASE_MSG("\nUnable to open file.\n");
847                 PRINT_TEST_CASE_MSG("Please check whether file exists and you have read/write privilege.\n");
848                 exit(EXIT_SUCCESS);
849         }
850
851         while((fgets(buffer, BUFSIZ, fPtr)) != NULL) {
852                 replaceAll(buffer, currentBridge, newBridge);
853                 fputs(buffer, fTemp);
854         }
855
856         fclose(fPtr);
857         fclose(fTemp);
858         remove(path);
859         rename("replace.tmp", path);
860         PRINT_TEST_CASE_MSG("Switching procedure done successfully\n");
861         int container_start_status = system(command_start);
862         assert(container_start_status == 0);
863         sleep(2);
864 }
865
866 /* Bring the interface down for the bridge created */
867 void bring_if_down(const char *bridgeName) {
868         char command[300] = "ip link set dev ";
869         strcat(command, bridgeName);
870         strcat(command, " down");
871         int if_down_status = system(command);
872         assert(if_down_status == 0);
873         PRINT_TEST_CASE_MSG("Interface brought down for %s created\n", bridgeName);
874 }
875
876 /* Delete interface for the bridge created */
877 void del_interface(const char *bridgeName, const char *interfaceName) {
878         char command[300] = "brctl delif ";
879         strcat(command, bridgeName);
880         strcat(command, interfaceName);
881         int if_delete_status = system(command);
882         assert(if_delete_status == 0);
883         PRINT_TEST_CASE_MSG("Deleted interface for %s\n", bridgeName);
884 }
885
886 /* Takes bridgeName as input parameter and deletes a bridge */
887 void delete_bridge(const char *bridgeName) {
888         bring_if_down(bridgeName);
889         char command[300] = "brctl delbr ";
890         strcat(command, bridgeName);
891         int bridge_delete = system(command);
892         assert(bridge_delete == 0);
893         PRINT_TEST_CASE_MSG("%s bridge deleted\n", bridgeName);
894         sleep(2);
895 }
896
897 /* Creates container on a specified bridge with added interface */
898 void create_container_on_bridge(const char *containerName, const char *bridgeName, const char *ifName) {
899         char command[100] = "lxc-create -t download -n ";
900         char cmd[100] = " -- -d ubuntu -r trusty -a ";
901         char start[100] = "lxc-start -n ";
902         FILE *fPtr;
903         char path[300] = "/var/lib/lxc/";
904         strcat(path, containerName);
905         strcat(path, "/config");
906         strcat(command, containerName);
907         strcat(command, cmd);
908         strcat(command, choose_arch);
909         int container_create_status = system(command);
910         assert(container_create_status == 0);
911         sleep(3);
912         assert((fPtr = fopen(path, "a+")));
913         fprintf(fPtr, "lxc.net.0.name = eth0\n");
914         fprintf(fPtr, "\n");
915         fprintf(fPtr, "lxc.net.1.type = veth\n");
916         fprintf(fPtr, "lxc.net.1.flags = up\n");
917         fprintf(fPtr, "lxc.net.1.link = %s\n", bridgeName);
918         fprintf(fPtr, "lxc.net.1.name = %s\n", ifName);
919         fprintf(fPtr, "lxc.net.1.hwaddr = 00:16:3e:ab:xx:xx\n");
920         fclose(fPtr);
921         strcat(start, containerName);
922         int container_start_status = system(start);
923         assert(container_start_status == 0);
924         sleep(3);
925         PRINT_TEST_CASE_MSG("Created %s on %s with interface name %s\n", containerName, bridgeName, ifName);
926 }
927
928 /* Configures dnsmasq and iptables for the specified container with inputs of listen address and dhcp range */
929 void config_dnsmasq(const char *containerName, const char *ifName, const char *listenAddress, const char *dhcpRange) {
930         char command[500] = "echo \"apt-get install dnsmasq iptables -y\" | lxc-attach -n ";
931         strcat(command, containerName);
932         strcat(command, " --");
933         int iptables_install_status = system(command);
934         assert(iptables_install_status == 0);
935         sleep(5);
936         char com1[300] = "echo \"echo \"interface=eth1\" >> /etc/dnsmasq.conf\" | lxc-attach -n ";
937         strcat(com1, containerName);
938         strcat(com1, " --");
939         int dnsmasq_status = system(com1);
940         assert(dnsmasq_status == 0);
941         sleep(5);
942         char com2[300] = "echo \"echo \"bind-interfaces\" >> /etc/dnsmasq.conf\" | lxc-attach -n ";
943         strcat(com2, containerName);
944         strcat(com2, " --");
945         dnsmasq_status = system(com2);
946         assert(dnsmasq_status == 0);
947         sleep(5);
948         char com3[300] = "echo \"echo \"listen-address=";
949         strcat(com3, listenAddress);
950         strcat(com3, "\" >> /etc/dnsmasq.conf\" | lxc-attach -n ");
951         strcat(com3, containerName);
952         strcat(com3, " --");
953         dnsmasq_status = system(com3);
954         assert(dnsmasq_status == 0);
955         sleep(5);
956         char com4[300] = "echo \"echo \"dhcp-range=";
957         strcat(com4, dhcpRange);
958         strcat(com4, "\" >> /etc/dnsmasq.conf\" | lxc-attach -n ");
959         strcat(com4, containerName);
960         strcat(com4, " --");
961         dnsmasq_status = system(com4);
962         assert(dnsmasq_status == 0);
963         sleep(5);
964         char cmd[300] = "echo \"ifconfig ";
965         strcat(cmd, ifName);
966         strcat(cmd, " ");
967         strcat(cmd, listenAddress);
968         strcat(cmd, " netmask 255.255.255.0 up\" | lxc-attach -n ");
969         strcat(cmd, containerName);
970         strcat(cmd, " --");
971         dnsmasq_status = system(cmd);
972         assert(dnsmasq_status == 0);
973         sleep(2);
974         char com[500] = "echo \"service dnsmasq restart >> /dev/null\" | lxc-attach -n ";
975         strcat(com, containerName);
976         strcat(com, " --");
977         dnsmasq_status = system(com);
978         assert(dnsmasq_status == 0);
979         sleep(2);
980         PRINT_TEST_CASE_MSG("Configured dnsmasq in %s with interface name %s, listen-address = %s, dhcp-range = %s\n", containerName, ifName, listenAddress, dhcpRange);
981 }
982
983 /* Configure the NAT rules inside the container */
984 void config_nat(const char *containerName, const char *listenAddress) {
985         char *last_dot_in_ip;
986         int last_ip_byte = 0;
987         char new_ip[300] = {0};
988         strncpy(new_ip, listenAddress, sizeof(new_ip) - 1);
989         assert((last_dot_in_ip = strrchr(new_ip, '.')));
990         assert(snprintf(last_dot_in_ip + 1, 4, "%d", last_ip_byte) >= 0);
991         char comd[300] = "echo \"iptables -t nat -A POSTROUTING -s ";
992         strcat(comd, new_ip);
993         strcat(comd, "/24 ! -d ");
994         strcat(comd, new_ip);
995         strcat(comd, "/24 -j MASQUERADE\" | lxc-attach -n ");
996         strcat(comd, containerName);
997         strcat(comd, " --");
998         int conf_nat_status = system(comd);
999         assert(conf_nat_status == 0);
1000         sleep(2);
1001         PRINT_TEST_CASE_MSG("Configured NAT on %s\n", containerName);
1002 }
1003
1004 /* Creates a NAT layer on a specified bridge with certain dhcp range to allocate ips for nodes */
1005 void create_nat_layer(const char *containerName, const char *bridgeName, const char *ifName, const char *listenAddress, char *dhcpRange) {
1006         create_bridge(bridgeName);
1007         bring_if_up(bridgeName);
1008         create_container_on_bridge(containerName, bridgeName, ifName);
1009         config_dnsmasq(containerName, ifName, listenAddress, dhcpRange);
1010         config_nat(containerName, listenAddress);
1011         PRINT_TEST_CASE_MSG("NAT layer created with %s\n", containerName);
1012 }
1013
1014 /* Destroys the NAT layer created */
1015 void destroy_nat_layer(const char *containerName, const char *bridgeName) {
1016         bring_if_down(bridgeName);
1017         delete_bridge(bridgeName);
1018         char command[100] = "lxc-stop -n ";
1019         strcat(command, containerName);
1020         int container_stop_status = system(command);
1021         assert(container_stop_status == 0);
1022         char destroy[100] = "lxc-destroy -n ";
1023         strcat(destroy, containerName);
1024         strcat(destroy, " -s");
1025         int container_destroy_status = system(destroy);
1026         assert(container_destroy_status == 0);
1027         PRINT_TEST_CASE_MSG("NAT layer destroyed with %s\n", containerName);
1028 }
1029
1030 /* Add incoming firewall rules for ipv4 addresses with packet type and port number */
1031 void incoming_firewall_ipv4(const char *packetType, int portNumber) {
1032         char buf[5];
1033         snprintf(buf, sizeof(buf), "%d", portNumber);
1034         assert(system("iptables -F") == 0);
1035         assert(system("iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") == 0);
1036         assert(system("iptables -A INPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT") == 0);
1037         char command[100] = "iptables -A INPUT -p ";
1038         strcat(command, packetType);
1039         strcat(command, " --dport ");
1040         strcat(command, buf);
1041         strcat(command, " -j ACCEPT");
1042         assert(system(command) == 0);
1043         sleep(2);
1044         assert(system("iptables -A INPUT -j DROP") == 0);
1045         PRINT_TEST_CASE_MSG("Firewall for incoming requests added on IPv4");
1046         assert(system("iptables -L") == 0);
1047 }
1048
1049 /* Add incoming firewall rules for ipv6 addresses with packet type and port number */
1050 void incoming_firewall_ipv6(const char *packetType, int portNumber) {
1051         char buf[5];
1052         snprintf(buf, sizeof(buf), "%d", portNumber);
1053         assert(system("ip6tables -F") == 0);
1054         assert(system("ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") == 0);
1055         assert(system("ip6tables -A INPUT -s ::1 -d ::1 -j ACCEPT") == 0);
1056         char command[100] = "ip6tables -A INPUT -p ";
1057         strcat(command, packetType);
1058         strcat(command, " --dport ");
1059         strcat(command, buf);
1060         strcat(command, " -j ACCEPT");
1061         assert(system(command) == 0);
1062         sleep(2);
1063         assert(system("ip6tables -A INPUT -j DROP") == 0);
1064         PRINT_TEST_CASE_MSG("Firewall for incoming requests added on IPv6");
1065         assert(system("ip6tables -L") == 0);
1066 }
1067
1068 /* Add outgoing firewall rules for ipv4 addresses with packet type and port number */
1069 void outgoing_firewall_ipv4(const char *packetType, int portNumber) {
1070         char buf[5];
1071         snprintf(buf, sizeof(buf), "%d", portNumber);
1072         assert(system("iptables -F") == 0);
1073         assert(system("iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") == 0);
1074         assert(system("iptables -A OUTPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT") == 0);
1075         char command[100] = "iptables -A OUTPUT -p ";
1076         strcat(command, packetType);
1077         strcat(command, " --dport ");
1078         strcat(command, buf);
1079         strcat(command, " -j ACCEPT");
1080         assert(system(command) == 0);
1081         sleep(2);
1082         assert(system("iptables -A OUTPUT -j DROP") == 0);
1083         PRINT_TEST_CASE_MSG("Firewall for outgoing requests added on IPv4");
1084         assert(system("iptables -L") == 0);
1085 }
1086
1087 /* Add outgoing firewall rules for ipv6 addresses with packet type and port number */
1088 void outgoing_firewall_ipv6(const char *packetType, int portNumber) {
1089         char buf[5];
1090         snprintf(buf, sizeof(buf), "%d", portNumber);
1091         assert(system("ip6tables -F") == 0);
1092         assert(system("ip6tables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") == 0);
1093         assert(system("ip6tables -A OUTPUT -s ::1 -d ::1 -j ACCEPT") == 0);
1094         char command[100] = "ip6tables -A OUTPUT -p ";
1095         strcat(command, packetType);
1096         strcat(command, " --dport ");
1097         strcat(command, buf);
1098         strcat(command, " -j ACCEPT");
1099         assert(system(command) == 0);
1100         sleep(2);
1101         assert(system("ip6tables -A OUTPUT -j DROP") == 0);
1102         PRINT_TEST_CASE_MSG("Firewall for outgoing requests added on IPv6");
1103         assert(system("ip6tables -L") == 0);
1104 }
1105
1106 void bridge_add(const char *bridge_name) {
1107         char cmd[500];
1108
1109         assert(bridge_name);
1110         assert(snprintf(cmd, sizeof(cmd), "brctl addbr %s", bridge_name) >= 0);
1111         assert(system(cmd) == 0);
1112         assert(snprintf(cmd, sizeof(cmd), "ifconfig %s up", bridge_name) >= 0);
1113         assert(system(cmd) == 0);
1114 }
1115
1116 void bridge_delete(const char *bridge_name) {
1117         char cmd[500];
1118
1119         assert(bridge_name);
1120         assert(snprintf(cmd, sizeof(cmd), "brctl delbr %s", bridge_name) >= 0);
1121         assert(system(cmd) == 0);
1122 }
1123
1124 void bridge_add_interface(const char *bridge_name, const char *interface_name) {
1125         char cmd[500];
1126
1127         assert(bridge_name || interface_name);
1128         assert(snprintf(cmd, sizeof(cmd), "brctl addif %s %s", bridge_name, interface_name) >= 0);
1129         assert(system(cmd) == 0);
1130 }