X-Git-Url: http://git.meshlink.io/?a=blobdiff_plain;f=test%2Fblackbox%2Fcommon%2Fcontainers.c;h=f99abde584f3b8d8ca6bef285c144df36ed4fbcc;hb=cdb3dada645394bb1e0d0bf7816291fbd9a2a4a0;hp=263cbf976ddd878da19d27be9c956ce0ee821095;hpb=dc68da94af8fca91748579c84ef5ed798db7efab;p=meshlink diff --git a/test/blackbox/common/containers.c b/test/blackbox/common/containers.c index 263cbf97..f99abde5 100644 --- a/test/blackbox/common/containers.c +++ b/test/blackbox/common/containers.c @@ -54,7 +54,7 @@ void rename_container(const char *old_name, const char *new_name) { struct lxc_container *old_container; /* Stop the old container if its still running */ - assert(old_container = find_container(old_name)); + assert((old_container = find_container(old_name))); old_container->shutdown(old_container, CONTAINER_SHUTDOWN_TIMEOUT); /* Call stop() in case shutdown() fails - one of these two will always succeed */ old_container->stop(old_container); @@ -69,38 +69,51 @@ void rename_container(const char *old_name, const char *new_name) { assert(rename_status == 0); } +char *run_in_container(const char *cmd, const char *container_name, bool daemonize) { + char container_find_name[1000]; + struct lxc_container *container; + + assert(cmd); + assert(container_name); + assert(snprintf(container_find_name, sizeof(container_find_name), "%s_%s", + state_ptr->test_case_name, container_name) >= 0); + assert((container = find_container(container_find_name))); + + return run_in_container_ex(cmd, container, daemonize); +} + +char *execute_in_container(const char *cmd, const char *container_name, bool daemonize) { + struct lxc_container *container; + + assert(cmd); + assert(container_name); + assert((container = find_container(container_name))); + + return run_in_container_ex(cmd, container, daemonize); +} + /* Run 'cmd' inside the Container created for 'node' and return the first line of the output or NULL if there is no output - useful when, for example, a meshlink invite is generated by a node running inside a Container 'cmd' is run as a daemon if 'daemonize' is true - this mode is useful for running node simulations in Containers The caller is responsible for freeing the returned string */ -char *run_in_container(const char *cmd, const char *node, bool daemonize) { - char attach_command[400]; - char *attach_argv[4]; - char container_find_name[100]; - struct lxc_container *container; - FILE *attach_fp; +char *run_in_container_ex(const char *cmd, struct lxc_container *container, bool daemonize) { char *output = NULL; - size_t output_len; - int i; - - assert(snprintf(container_find_name, sizeof(container_find_name), "%s_%s", - state_ptr->test_case_name, node) >= 0); - assert(container = find_container(container_find_name)); + size_t output_len = 0; /* Run the command within the Container, either as a daemon or foreground process */ /* TO DO: Perform this operation using the LXC API - currently does not work using the API Need to determine why it doesn't work, and make it work */ if(daemonize) { - for(i = 0; i < 3; i++) { - assert(attach_argv[i] = malloc(DAEMON_ARGV_LEN)); - } + char run_script_path[100]; + char *attach_argv[4]; - assert(snprintf(attach_argv[0], DAEMON_ARGV_LEN, "%s/" LXC_UTIL_REL_PATH "/" LXC_RUN_SCRIPT, + assert(snprintf(run_script_path, sizeof(run_script_path), "%s/" LXC_UTIL_REL_PATH "/" LXC_RUN_SCRIPT, meshlink_root_path) >= 0); - strncpy(attach_argv[1], cmd, DAEMON_ARGV_LEN); - strncpy(attach_argv[2], container->name, DAEMON_ARGV_LEN); + attach_argv[0] = run_script_path; + attach_argv[1] = (char *)cmd; + attach_argv[2] = container->name; attach_argv[3] = NULL; /* To daemonize, create a child process and detach it from its parent (this program) */ @@ -108,19 +121,24 @@ char *run_in_container(const char *cmd, const char *node, bool daemonize) { assert(daemon(1, 0) != -1); // Detach from the parent process assert(execv(attach_argv[0], attach_argv) != -1); // Run exec() in the child process } - - for(i = 0; i < 3; i++) { - free(attach_argv[i]); - } } else { - assert(snprintf(attach_command, sizeof(attach_command), + char *attach_command; + size_t attach_command_len; + int i; + attach_command_len = strlen(meshlink_root_path) + strlen(LXC_UTIL_REL_PATH) + strlen(LXC_RUN_SCRIPT) + strlen(cmd) + strlen(container->name) + 10; + attach_command = malloc(attach_command_len); + assert(attach_command); + + assert(snprintf(attach_command, attach_command_len, "%s/" LXC_UTIL_REL_PATH "/" LXC_RUN_SCRIPT " \"%s\" %s", meshlink_root_path, cmd, container->name) >= 0); - assert(attach_fp = popen(attach_command, "r")); + FILE *attach_fp; + assert((attach_fp = popen(attach_command, "r"))); + free(attach_command); /* If the command has an output, strip out any trailing carriage returns or newlines and return it, otherwise return NULL */ - assert(output = malloc(100)); - output_len = sizeof(output); + output = NULL; + output_len = 0; if(getline(&output, &output_len, attach_fp) != -1) { i = strlen(output) - 1; @@ -143,26 +161,40 @@ char *run_in_container(const char *cmd, const char *node, bool daemonize) { /* Wait for a starting Container to obtain an IP Address, then save that IP for future use */ void container_wait_ip(int node) { - char container_name[100], lxcls_command[200]; + char container_name[100]; + char *ip; + + assert(snprintf(container_name, sizeof(container_name), "%s_%s", state_ptr->test_case_name, + state_ptr->node_names[node]) >= 0); + ip = container_wait_ip_ex(container_name); + + strncpy(container_ips[node], ip, sizeof(container_ips[node])); // Save the IP for future use + PRINT_TEST_CASE_MSG("Node '%s' has IP Address %s\n", state_ptr->node_names[node], + container_ips[node]); + free(ip); +} + +char *container_wait_ip_ex(const char *container_name) { struct lxc_container *test_container; + char lxcls_command[200]; char *ip; size_t ip_len; - int i; bool ip_found; + int i; + int timeout; FILE *lxcls_fp; - assert(snprintf(container_name, sizeof(container_name), "%s_%s", state_ptr->test_case_name, - state_ptr->node_names[node]) >= 0); - assert(test_container = find_container(container_name)); + assert((test_container = find_container(container_name))); assert(snprintf(lxcls_command, sizeof(lxcls_command), "lxc-ls -f | grep %s | tr -s ' ' | cut -d ' ' -f 5", test_container->name) >= 0); PRINT_TEST_CASE_MSG("Waiting for Container '%s' to acquire IP\n", test_container->name); - assert(ip = malloc(20)); + assert((ip = malloc(20))); ip_len = sizeof(ip); ip_found = false; + timeout = 60; - while(!ip_found) { - assert(lxcls_fp = popen(lxcls_command, "r")); // Run command + while(!ip_found && timeout) { + assert((lxcls_fp = popen(lxcls_command, "r"))); // Run command assert(getline((char **)&ip, &ip_len, lxcls_fp) != -1); // Read its output /* Strip newlines and carriage returns from output */ i = strlen(ip) - 1; @@ -175,13 +207,13 @@ void container_wait_ip(int node) { ip_found = (strcmp(ip, "-") != 0); // If the output is not "-", IP has been acquired assert(pclose(lxcls_fp) != -1); sleep(1); + timeout--; } - strncpy(container_ips[node], ip, sizeof(container_ips[node])); // Save the IP for future use - PRINT_TEST_CASE_MSG("Node '%s' has IP Address %s\n", state_ptr->node_names[node], - container_ips[node]); + // Fail if IP cannot be read + assert(timeout); - free(ip); + return ip; } /* Create all required test containers */ @@ -189,7 +221,7 @@ void create_containers(const char *node_names[], int num_nodes) { int i; char container_name[100]; int create_status, snapshot_status, snap_restore_status; - struct lxc_container *first_container; + struct lxc_container *first_container = NULL; for(i = 0; i < num_nodes; i++) { assert(snprintf(container_name, sizeof(container_name), "run_%s", node_names[i]) >= 0); @@ -197,7 +229,7 @@ void create_containers(const char *node_names[], int num_nodes) { /* If this is the first Container, create it otherwise restore the snapshot saved for the first Container to create an additional Container */ if(i == 0) { - assert(first_container = lxc_container_new(container_name, NULL)); + assert((first_container = lxc_container_new(container_name, NULL))); assert(!first_container->is_defined(first_container)); create_status = first_container->createl(first_container, "download", NULL, NULL, LXC_CREATE_QUIET, "-d", "ubuntu", "-r", "trusty", "-a", choose_arch, NULL); @@ -209,6 +241,7 @@ void create_containers(const char *node_names[], int num_nodes) { first_container->error_num, first_container->error_string); assert(snapshot_status != -1); } else { + assert(first_container); snap_restore_status = first_container->snapshot_restore(first_container, "snap0", container_name); fprintf(stderr, "Snapshot restore to Container '%s' status: %d - %s\n", container_name, @@ -223,7 +256,7 @@ void create_containers(const char *node_names[], int num_nodes) { after setting the state of the test case to an instance of black_box_state_t */ void setup_containers(void **state) { black_box_state_t *test_state = (black_box_state_t *)(*state); - int i, confbase_del_status; + int i; char build_command[200]; struct lxc_container *test_container, *new_container; char container_find_name[100]; @@ -238,7 +271,7 @@ void setup_containers(void **state) { test_state->node_names[i]) >= 0); if(!(test_container = find_container(container_find_name))) { - assert(test_container = lxc_container_new(container_find_name, NULL)); + assert((test_container = lxc_container_new(container_find_name, NULL))); assert(!test_container->is_defined(test_container)); create_status = test_container->createl(test_container, "download", NULL, NULL, LXC_CREATE_QUIET, "-d", "ubuntu", "-r", "trusty", "-a", choose_arch, NULL); @@ -259,7 +292,7 @@ void setup_containers(void **state) { if(!(new_container = find_container(container_new_name))) { rename_container(test_container->name, container_new_name); - assert(new_container = find_container(container_new_name)); + assert((new_container = find_container(container_new_name))); } /* Start the Container */ @@ -302,7 +335,7 @@ void destroy_containers(void) { test_containers[i]->stop(test_containers[i]); /* Destroy the Container */ test_containers[i]->destroy(test_containers[i]); - /* call destroy_with_snapshots() in case destroy() fails + /* Call destroy_with_snapshots() in case destroy() fails one of these two calls will always succeed */ test_containers[i]->destroy_with_snapshots(test_containers[i]); } @@ -320,7 +353,7 @@ void restart_all_containers(void) { /* Shutdown, then start the Container, then wait for it to acquire an IP Address */ assert(snprintf(container_name, sizeof(container_name), "%s_%s", state_ptr->test_case_name, state_ptr->node_names[i]) >= 0); - assert(test_container = find_container(container_name)); + assert((test_container = find_container(container_name))); test_container->shutdown(test_container, CONTAINER_SHUTDOWN_TIMEOUT); test_container->stop(test_container); test_container->start(test_container, 0, NULL); @@ -337,7 +370,24 @@ char *invite_in_container(const char *inviter, const char *invitee) { assert(snprintf(invite_command, sizeof(invite_command), "LD_LIBRARY_PATH=/home/ubuntu/test/.libs /home/ubuntu/test/gen_invite %s %s " "2> gen_invite.log", inviter, invitee) >= 0); - assert(invite_url = run_in_container(invite_command, inviter, false)); + assert((invite_url = run_in_container(invite_command, inviter, false))); + PRINT_TEST_CASE_MSG("Invite Generated from '%s' to '%s': %s\n", inviter, + invitee, invite_url); + + return invite_url; +} + +/* Run the gen_invite command inside the 'inviter' container to generate an invite + for 'invitee' belonging to pparticular submesh, and return the generated invite + which is output on the terminal */ +char *submesh_invite_in_container(const char *inviter, const char *invitee, const char *submesh) { + char invite_command[200]; + char *invite_url; + + assert(snprintf(invite_command, sizeof(invite_command), + "LD_LIBRARY_PATH=/home/ubuntu/test/.libs /home/ubuntu/test/gen_invite %s %s %s " + "2> gen_invite.log", inviter, invitee, submesh) >= 0); + assert((invite_url = run_in_container(invite_command, inviter, false))); PRINT_TEST_CASE_MSG("Invite Generated from '%s' to '%s': %s\n", inviter, invitee, invite_url); @@ -346,36 +396,47 @@ char *invite_in_container(const char *inviter, const char *invitee) { /* Run the node_sim_ program inside the 'node''s container */ void node_sim_in_container(const char *node, const char *device_class, const char *invite_url) { - char node_sim_command[200]; + char *node_sim_command; + size_t node_sim_command_len; - assert(snprintf(node_sim_command, sizeof(node_sim_command), + node_sim_command_len = 500 + (invite_url ? strlen(invite_url) : 0); + node_sim_command = calloc(1, node_sim_command_len); + assert(node_sim_command); + assert(snprintf(node_sim_command, node_sim_command_len, "LD_LIBRARY_PATH=/home/ubuntu/test/.libs /home/ubuntu/test/node_sim_%s %s %s %s " "1>&2 2>> node_sim_%s.log", node, node, device_class, (invite_url) ? invite_url : "", node) >= 0); run_in_container(node_sim_command, node, true); PRINT_TEST_CASE_MSG("node_sim_%s started in Container\n", node); + + free(node_sim_command); } -/* Run the node_sim_ program inside the 'node''s container with event handling capable*/ +/* Run the node_sim_ program inside the 'node''s container with event handling capable */ void node_sim_in_container_event(const char *node, const char *device_class, const char *invite_url, const char *clientId, const char *import) { - char node_sim_command[200]; + char *node_sim_command; + size_t node_sim_command_len; - assert(snprintf(node_sim_command, sizeof(node_sim_command), + assert(node && device_class && clientId && import); + node_sim_command_len = 500 + (invite_url ? strlen(invite_url) : 0); + node_sim_command = calloc(1, node_sim_command_len); + assert(node_sim_command); + assert(snprintf(node_sim_command, node_sim_command_len, "LD_LIBRARY_PATH=/home/ubuntu/test/.libs /home/ubuntu/test/node_sim_%s %s %s %s %s %s " "1>&2 2>> node_sim_%s.log", node, node, device_class, clientId, import, (invite_url) ? invite_url : "", node) >= 0); run_in_container(node_sim_command, node, true); - PRINT_TEST_CASE_MSG("node_sim_%s(Client Id :%s) started in Container with event handling\n", - node, clientId); - PRINT_TEST_CASE_MSG("node_sim_%s mesh event import string : %s\n", - node, import); + PRINT_TEST_CASE_MSG("node_sim_%s(Client Id :%s) started in Container with event handling\n%s\n", + node, clientId, node_sim_command); + + free(node_sim_command); } /* Run the node_step.sh script inside the 'node''s container to send the 'sig' signal to the node_sim program in the container */ void node_step_in_container(const char *node, const char *sig) { - char node_step_command[200]; + char node_step_command[1000]; assert(snprintf(node_step_command, sizeof(node_step_command), "/home/ubuntu/test/node_step.sh lt-node_sim_%s %s 1>&2 2> node_step.log", @@ -388,7 +449,7 @@ void node_step_in_container(const char *node, const char *sig) { Changes begin from X.X.X.254 and continue iteratively till an available address is found */ void change_ip(int node) { char *gateway_addr; - char new_ip[20]; + char new_ip[20] = ""; char *netmask; char *last_dot_in_ip; int last_ip_byte = 254; @@ -400,14 +461,14 @@ void change_ip(int node) { /* Get IP Address of LXC Bridge Interface - this will be set up as the Gateway Address of the Static IP assigned to the Container */ - assert(gateway_addr = get_ip(lxc_bridge)); + assert((gateway_addr = get_ip(lxc_bridge))); /* Get Netmask of LXC Brdige Interface */ - assert(netmask = get_netmask(lxc_bridge)); + assert((netmask = get_netmask(lxc_bridge))); /* Replace last byte of Container's IP with 254 to form the new Container IP */ assert(container_ips[node]); - strncpy(new_ip, container_ips[node], sizeof(new_ip)); - assert(last_dot_in_ip = strrchr(new_ip, '.')); + strncpy(new_ip, container_ips[node], sizeof(new_ip) - 1); + assert((last_dot_in_ip = strrchr(new_ip, '.'))); assert(snprintf(last_dot_in_ip + 1, 4, "%d", last_ip_byte) >= 0); /* Check that the new IP does not match the Container's existing IP @@ -419,7 +480,7 @@ void change_ip(int node) { } /* Create new 'interfaces' file for Container */ - assert(if_fp = fopen("interfaces", "w")); + assert((if_fp = fopen("interfaces", "w"))); fprintf(if_fp, "auto lo\n"); fprintf(if_fp, "iface lo inet loopback\n"); fprintf(if_fp, "\n"); @@ -442,14 +503,628 @@ void change_ip(int node) { /* Restart Container to apply new IP Address */ assert(snprintf(container_name, sizeof(container_name), "%s_%s", state_ptr->test_case_name, state_ptr->node_names[node]) >= 0); - assert(container = find_container(container_name)); + assert((container = find_container(container_name))); container->shutdown(container, CONTAINER_SHUTDOWN_TIMEOUT); /* Call stop() in case shutdown() fails One of these two calls with always succeed */ container->stop(container); assert(container->start(container, 0, NULL)); - strncpy(container_ips[node], new_ip, sizeof(new_ip)); // Save the new IP Addres + strncpy(container_ips[node], new_ip, sizeof(container_ips[node])); // Save the new IP Address PRINT_TEST_CASE_MSG("Node '%s' IP Address changed to %s\n", state_ptr->node_names[node], container_ips[node]); } + +char **get_container_interface_ips(const char *container_name, const char *interface_name) { + char **ips; + struct lxc_container *container = find_container(container_name); + assert(container); + + char **interfaces = container->get_interfaces(container); + assert(interfaces); + + int i; + ips = NULL; + + for(i = 0; interfaces[i]; i++) { + if(!strcasecmp(interfaces[i], interface_name)) { + ips = container->get_ips(container, interface_name, "inet", 0); + assert(ips); + break; + } + } + + free(interfaces); + + return ips; +} + +/* Install an app in a container */ +void install_in_container(const char *node, const char *app) { + char install_cmd[100]; + + assert(snprintf(install_cmd, sizeof(install_cmd), + "apt-get install %s -y >> /dev/null", app) >= 0); + run_in_container(install_cmd, node, false); + // TODO: Check in container whether app has installed or not with a timeout + sleep(10); +} + +/* Return container's IP address */ +char *get_container_ip(const char *node_name) { + int node = -1, i; + + for(i = 0; i < state_ptr->num_nodes; i++) { + if(!strcasecmp(state_ptr->node_names[i], node_name)) { + node = i; + break; + } + } + + if(i == state_ptr->num_nodes) { + return NULL; + } + + char *ip = strdup(container_ips[node]); + assert(ip); + + return ip; +} + +/* Simulate a network failure by adding NAT rule in the container with it's IP address */ +void block_node_ip(const char *node) { + char block_cmd[100]; + char *node_ip; + + node_ip = get_container_ip(node); + assert(node_ip); + assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A OUTPUT -p all -s %s -j DROP", node_ip) >= 0); + run_in_container(block_cmd, node, false); + + assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A INPUT -p all -s %s -j DROP", node_ip) >= 0); + run_in_container(block_cmd, node, false); + + assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A FORWARD -p all -s %s -j DROP", node_ip) >= 0); + run_in_container(block_cmd, node, false); + + free(node_ip); +} + +void accept_port_rule(const char *node, const char *chain, const char *protocol, int port) { + char block_cmd[100]; + + assert(port >= 0 && port < 65536); + assert(!strcmp(chain, "INPUT") || !strcmp(chain, "FORWARD") || !strcmp(chain, "OUTPUT")); + assert(!strcmp(protocol, "all") || !strcmp(protocol, "tcp") || !strcmp(protocol, "udp") || !strcmp(protocol, "icmp")); + assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A %s -p %s --dport %d -j ACCEPT", chain, protocol, port) >= 0); + run_in_container(block_cmd, node, false); +} + +/* Restore the network that was blocked before by the NAT rule with it's own IP address */ +void unblock_node_ip(const char *node) { + char unblock_cmd[100]; + char *node_ip; + + node_ip = get_container_ip(node); + assert(node_ip); + assert(snprintf(unblock_cmd, sizeof(unblock_cmd), "iptables -D OUTPUT -p all -s %s -j DROP", node_ip) >= 0); + run_in_container(unblock_cmd, node, false); + + assert(snprintf(unblock_cmd, sizeof(unblock_cmd), "iptables -D INPUT -p all -s %s -j DROP", node_ip) >= 0); + run_in_container(unblock_cmd, node, false); + + assert(snprintf(unblock_cmd, sizeof(unblock_cmd), "iptables -D FORWARD -p all -s %s -j DROP", node_ip) >= 0); + run_in_container(unblock_cmd, node, false); +} + +char *block_icmp(const char *container_name) { + char block_cmd[500]; + assert(container_name); + assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -A FORWARD -p icmp -j DROP") >= 0); + return execute_in_container(block_cmd, container_name, false); +} + +char *unblock_icmp(const char *container_name) { + char block_cmd[500]; + assert(container_name); + assert(snprintf(block_cmd, sizeof(block_cmd), "iptables -D FORWARD -p icmp -j DROP") >= 0); + return execute_in_container(block_cmd, container_name, false); +} + +char *change_container_mtu(const char *container_name, const char *interface_name, int mtu) { + char cmd[500]; + assert(container_name); + assert(snprintf(cmd, sizeof(cmd), "ifconfig %s mtu %d", interface_name, mtu) >= 0); + return execute_in_container(cmd, container_name, false); +} + +char *flush_conntrack(const char *container_name) { + assert(container_name); + + return execute_in_container("conntrack -F", container_name, false); +} + +void flush_nat_rules(const char *container_name, const char *chain) { + char *ret; + char flush_cmd[500]; + + assert(container_name); + assert(snprintf(flush_cmd, sizeof(flush_cmd), "iptables -F %s", chain ? chain : "") >= 0); + ret = execute_in_container("iptables -F", container_name, false); + assert(ret == NULL); +} + +void add_full_cone_nat_rules(const char *container_name, const char *pub_interface, const char *priv_interface_listen_address) { + char nat_cmd[500]; + + char **pub_interface_ips = get_container_interface_ips(container_name, pub_interface); + assert(pub_interface_ips[0]); + char *pub_interface_ip = pub_interface_ips[0]; + + assert(snprintf(nat_cmd, sizeof(nat_cmd), + "%s/" LXC_UTIL_REL_PATH "/" LXC_NAT_FULL_CONE " %s %s %s %s >/dev/null", + meshlink_root_path, container_name, pub_interface, pub_interface_ip, priv_interface_listen_address) >= 0); + assert(system(nat_cmd) == 0); + free(pub_interface_ips); +} + +/* Create a NAT and a bridge, bridge connected to NAT and containers to be NATed can be switched + to the NAT bridge from lxcbr0 */ +void nat_create(const char *nat_name, const char *nat_bridge, int nat_type) { + (void)nat_type; + + char build_command[200]; + assert(snprintf(build_command, sizeof(build_command), + "%s/" LXC_UTIL_REL_PATH "/" LXC_NAT_BUILD " %s %s %s >/dev/stderr", + meshlink_root_path, nat_name, nat_bridge, meshlink_root_path) >= 0); + assert(system(build_command) == 0); +} + +void nat_destroy(const char *nat_name) { + char build_command[200]; + assert(snprintf(build_command, sizeof(build_command), + "%s/" LXC_UTIL_REL_PATH "/" LXC_NAT_DESTROY " %s +x >/dev/null", + meshlink_root_path, nat_name) >= 0); + assert(system(build_command) == 0); +} + +/* Switches a container from current bridge to a new bridge */ +void container_switch_bridge(const char *container_name, char *lxc_conf_path, const char *current_bridge, const char *new_bridge) { + char config_path[500]; + char buffer[500]; + struct lxc_container *container; + char *lxc_path_temp; + char *ip; + + PRINT_TEST_CASE_MSG("Switiching container %s from bridge '%s' to bridge '%s'", container_name, current_bridge, new_bridge); + lxc_path_temp = lxc_path; + lxc_path = lxc_conf_path; + container = find_container(container_name); + assert(container); + lxc_path = lxc_path_temp; + assert(snprintf(config_path, sizeof(config_path), "%s/%s/config", lxc_conf_path, container_name) >= 0); + FILE *fp = fopen(config_path, "r"); + assert(fp); + FILE *fp_temp = fopen(".temp_file", "w"); + assert(fp_temp); + + int net_no; + + while((fgets(buffer, sizeof(buffer), fp)) != NULL) { + if(sscanf(buffer, "lxc.net.%d.link", &net_no) == 1) { + char *ptr; + int len; + + if((ptr = strstr(buffer, current_bridge)) != NULL) { + len = strlen(current_bridge); + + if(((*(ptr - 1) == ' ') || (*(ptr - 1) == '\t') || (*(ptr - 1) == '=')) && ((ptr[len] == '\n') || (ptr[len] == '\t') || (ptr[len] == '\0') || (ptr[len] == ' '))) { + sprintf(buffer, "lxc.net.%d.link = %s\n", net_no, new_bridge); + } + } + } + + fputs(buffer, fp_temp); + } + + fclose(fp_temp); + fclose(fp); + remove(config_path); + rename(".temp_file", config_path); + + /* Restart the Container after building it and wait for it to acquire an IP */ + char cmd[200]; + int sys_ret; + assert(snprintf(cmd, sizeof(cmd), "lxc-stop %s", container_name) >= 0); + sys_ret = system(cmd); + assert(snprintf(cmd, sizeof(cmd), "lxc-start %s", container_name) >= 0); + sys_ret = system(cmd); + assert(sys_ret == 0); + ip = container_wait_ip_ex(container_name); + PRINT_TEST_CASE_MSG("Obtained IP address: %s for container %s after switching bridge", ip, container_name); + free(ip); +} + +/* Takes bridgeName as input parameter and creates a bridge */ +void create_bridge(const char *bridgeName) { + char command[100] = "brctl addbr "; + strcat(command, bridgeName); + int create_bridge_status = system(command); + assert(create_bridge_status == 0); + PRINT_TEST_CASE_MSG("%s bridge created\n", bridgeName); +} + +/* Add interface for the bridge created */ +void add_interface(const char *bridgeName, const char *interfaceName) { + char command[100] = "brctl addif "; + char cmd[100] = "dhclient "; + + strcat(command, bridgeName); + strcat(command, " "); + strcat(command, interfaceName); + int addif_status = system(command); + assert(addif_status == 0); + strcat(cmd, bridgeName); + int dhclient_status = system(cmd); + assert(dhclient_status == 0); + PRINT_TEST_CASE_MSG("Added interface for %s\n", bridgeName); +} + +/* Create a veth pair and bring them up */ +void add_veth_pair(const char *vethName1, const char *vethName2) { + char command[100] = "ip link add "; + char upCommand1[100] = "ip link set "; + char upCommand2[100] = "ip link set "; + + strcat(command, vethName1); + strcat(command, " type veth peer name "); + strcat(command, vethName2); + int link_add_status = system(command); + assert(link_add_status == 0); + strcat(upCommand1, vethName1); + strcat(upCommand1, " up"); + int link_set_veth1_status = system(upCommand1); + assert(link_set_veth1_status == 0); + strcat(upCommand2, vethName2); + strcat(upCommand2, " up"); + int link_set_veth2_status = system(upCommand2); + assert(link_set_veth2_status == 0); + PRINT_TEST_CASE_MSG("Added veth pairs %s and %s\n", vethName1, vethName2); +} + +/* Bring the interface up for the bridge created */ +void bring_if_up(const char *bridgeName) { + char command[300] = "ifconfig "; + strcat(command, bridgeName); + strcat(command, " up"); + int if_up_status = system(command); + assert(if_up_status == 0); + sleep(2); + PRINT_TEST_CASE_MSG("Interface brought up for %s created\n", bridgeName); +} + +/** + * Replace all occurrences of a given a word in string. + */ +void replaceAll(char *str, const char *oldWord, const char *newWord) { + char *pos, temp[BUFSIZ]; + int index = 0; + int owlen; + owlen = strlen(oldWord); + + while((pos = strstr(str, oldWord)) != NULL) { + strcpy(temp, str); + index = pos - str; + str[index] = '\0'; + strcat(str, newWord); + strcat(str, temp + index + owlen); + } +} + +/* Switches the bridge for a given container */ +void switch_bridge(const char *containerName, const char *currentBridge, const char *newBridge) { + char command[100] = "lxc-stop -n "; + char command_start[100] = "lxc-start -n "; + PRINT_TEST_CASE_MSG("Switching %s container to %s\n", containerName, newBridge); + strcat(command, containerName); + strcat(command_start, containerName); + int container_stop_status = system(command); + assert(container_stop_status == 0); + sleep(2); + FILE *fPtr; + FILE *fTemp; + char path[300] = "/var/lib/lxc/"; + strcat(path, containerName); + strcat(path, "/config"); + + char buffer[BUFSIZ]; + /* Open all required files */ + fPtr = fopen(path, "r"); + fTemp = fopen("replace.tmp", "w"); + + if(fPtr == NULL || fTemp == NULL) { + PRINT_TEST_CASE_MSG("\nUnable to open file.\n"); + PRINT_TEST_CASE_MSG("Please check whether file exists and you have read/write privilege.\n"); + exit(EXIT_SUCCESS); + } + + while((fgets(buffer, BUFSIZ, fPtr)) != NULL) { + replaceAll(buffer, currentBridge, newBridge); + fputs(buffer, fTemp); + } + + fclose(fPtr); + fclose(fTemp); + remove(path); + rename("replace.tmp", path); + PRINT_TEST_CASE_MSG("Switching procedure done successfully\n"); + int container_start_status = system(command_start); + assert(container_start_status == 0); + sleep(2); +} + +/* Bring the interface down for the bridge created */ +void bring_if_down(const char *bridgeName) { + char command[300] = "ip link set dev "; + strcat(command, bridgeName); + strcat(command, " down"); + int if_down_status = system(command); + assert(if_down_status == 0); + PRINT_TEST_CASE_MSG("Interface brought down for %s created\n", bridgeName); +} + +/* Delete interface for the bridge created */ +void del_interface(const char *bridgeName, const char *interfaceName) { + char command[300] = "brctl delif "; + strcat(command, bridgeName); + strcat(command, interfaceName); + int if_delete_status = system(command); + assert(if_delete_status == 0); + PRINT_TEST_CASE_MSG("Deleted interface for %s\n", bridgeName); +} + +/* Takes bridgeName as input parameter and deletes a bridge */ +void delete_bridge(const char *bridgeName) { + bring_if_down(bridgeName); + char command[300] = "brctl delbr "; + strcat(command, bridgeName); + int bridge_delete = system(command); + assert(bridge_delete == 0); + PRINT_TEST_CASE_MSG("%s bridge deleted\n", bridgeName); + sleep(2); +} + +/* Creates container on a specified bridge with added interface */ +void create_container_on_bridge(const char *containerName, const char *bridgeName, const char *ifName) { + char command[100] = "lxc-create -t download -n "; + char cmd[100] = " -- -d ubuntu -r trusty -a "; + char start[100] = "lxc-start -n "; + FILE *fPtr; + char path[300] = "/var/lib/lxc/"; + strcat(path, containerName); + strcat(path, "/config"); + strcat(command, containerName); + strcat(command, cmd); + strcat(command, choose_arch); + int container_create_status = system(command); + assert(container_create_status == 0); + sleep(3); + assert((fPtr = fopen(path, "a+"))); + fprintf(fPtr, "lxc.net.0.name = eth0\n"); + fprintf(fPtr, "\n"); + fprintf(fPtr, "lxc.net.1.type = veth\n"); + fprintf(fPtr, "lxc.net.1.flags = up\n"); + fprintf(fPtr, "lxc.net.1.link = %s\n", bridgeName); + fprintf(fPtr, "lxc.net.1.name = %s\n", ifName); + fprintf(fPtr, "lxc.net.1.hwaddr = 00:16:3e:ab:xx:xx\n"); + fclose(fPtr); + strcat(start, containerName); + int container_start_status = system(start); + assert(container_start_status == 0); + sleep(3); + PRINT_TEST_CASE_MSG("Created %s on %s with interface name %s\n", containerName, bridgeName, ifName); +} + +/* Configures dnsmasq and iptables for the specified container with inputs of listen address and dhcp range */ +void config_dnsmasq(const char *containerName, const char *ifName, const char *listenAddress, const char *dhcpRange) { + char command[500] = "echo \"apt-get install dnsmasq iptables -y\" | lxc-attach -n "; + strcat(command, containerName); + strcat(command, " --"); + int iptables_install_status = system(command); + assert(iptables_install_status == 0); + sleep(5); + char com1[300] = "echo \"echo \"interface=eth1\" >> /etc/dnsmasq.conf\" | lxc-attach -n "; + strcat(com1, containerName); + strcat(com1, " --"); + int dnsmasq_status = system(com1); + assert(dnsmasq_status == 0); + sleep(5); + char com2[300] = "echo \"echo \"bind-interfaces\" >> /etc/dnsmasq.conf\" | lxc-attach -n "; + strcat(com2, containerName); + strcat(com2, " --"); + dnsmasq_status = system(com2); + assert(dnsmasq_status == 0); + sleep(5); + char com3[300] = "echo \"echo \"listen-address="; + strcat(com3, listenAddress); + strcat(com3, "\" >> /etc/dnsmasq.conf\" | lxc-attach -n "); + strcat(com3, containerName); + strcat(com3, " --"); + dnsmasq_status = system(com3); + assert(dnsmasq_status == 0); + sleep(5); + char com4[300] = "echo \"echo \"dhcp-range="; + strcat(com4, dhcpRange); + strcat(com4, "\" >> /etc/dnsmasq.conf\" | lxc-attach -n "); + strcat(com4, containerName); + strcat(com4, " --"); + dnsmasq_status = system(com4); + assert(dnsmasq_status == 0); + sleep(5); + char cmd[300] = "echo \"ifconfig "; + strcat(cmd, ifName); + strcat(cmd, " "); + strcat(cmd, listenAddress); + strcat(cmd, " netmask 255.255.255.0 up\" | lxc-attach -n "); + strcat(cmd, containerName); + strcat(cmd, " --"); + dnsmasq_status = system(cmd); + assert(dnsmasq_status == 0); + sleep(2); + char com[500] = "echo \"service dnsmasq restart >> /dev/null\" | lxc-attach -n "; + strcat(com, containerName); + strcat(com, " --"); + dnsmasq_status = system(com); + assert(dnsmasq_status == 0); + sleep(2); + PRINT_TEST_CASE_MSG("Configured dnsmasq in %s with interface name %s, listen-address = %s, dhcp-range = %s\n", containerName, ifName, listenAddress, dhcpRange); +} + +/* Configure the NAT rules inside the container */ +void config_nat(const char *containerName, const char *listenAddress) { + char *last_dot_in_ip; + int last_ip_byte = 0; + char new_ip[300] = {0}; + strncpy(new_ip, listenAddress, sizeof(new_ip) - 1); + assert((last_dot_in_ip = strrchr(new_ip, '.'))); + assert(snprintf(last_dot_in_ip + 1, 4, "%d", last_ip_byte) >= 0); + char comd[300] = "echo \"iptables -t nat -A POSTROUTING -s "; + strcat(comd, new_ip); + strcat(comd, "/24 ! -d "); + strcat(comd, new_ip); + strcat(comd, "/24 -j MASQUERADE\" | lxc-attach -n "); + strcat(comd, containerName); + strcat(comd, " --"); + int conf_nat_status = system(comd); + assert(conf_nat_status == 0); + sleep(2); + PRINT_TEST_CASE_MSG("Configured NAT on %s\n", containerName); +} + +/* Creates a NAT layer on a specified bridge with certain dhcp range to allocate ips for nodes */ +void create_nat_layer(const char *containerName, const char *bridgeName, const char *ifName, const char *listenAddress, char *dhcpRange) { + create_bridge(bridgeName); + bring_if_up(bridgeName); + create_container_on_bridge(containerName, bridgeName, ifName); + config_dnsmasq(containerName, ifName, listenAddress, dhcpRange); + config_nat(containerName, listenAddress); + PRINT_TEST_CASE_MSG("NAT layer created with %s\n", containerName); +} + +/* Destroys the NAT layer created */ +void destroy_nat_layer(const char *containerName, const char *bridgeName) { + bring_if_down(bridgeName); + delete_bridge(bridgeName); + char command[100] = "lxc-stop -n "; + strcat(command, containerName); + int container_stop_status = system(command); + assert(container_stop_status == 0); + char destroy[100] = "lxc-destroy -n "; + strcat(destroy, containerName); + strcat(destroy, " -s"); + int container_destroy_status = system(destroy); + assert(container_destroy_status == 0); + PRINT_TEST_CASE_MSG("NAT layer destroyed with %s\n", containerName); +} + +/* Add incoming firewall rules for ipv4 addresses with packet type and port number */ +void incoming_firewall_ipv4(const char *packetType, int portNumber) { + char buf[5]; + snprintf(buf, sizeof(buf), "%d", portNumber); + assert(system("iptables -F") == 0); + assert(system("iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") == 0); + assert(system("iptables -A INPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT") == 0); + char command[100] = "iptables -A INPUT -p "; + strcat(command, packetType); + strcat(command, " --dport "); + strcat(command, buf); + strcat(command, " -j ACCEPT"); + assert(system(command) == 0); + sleep(2); + assert(system("iptables -A INPUT -j DROP") == 0); + PRINT_TEST_CASE_MSG("Firewall for incoming requests added on IPv4"); + assert(system("iptables -L") == 0); +} + +/* Add incoming firewall rules for ipv6 addresses with packet type and port number */ +void incoming_firewall_ipv6(const char *packetType, int portNumber) { + char buf[5]; + snprintf(buf, sizeof(buf), "%d", portNumber); + assert(system("ip6tables -F") == 0); + assert(system("ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") == 0); + assert(system("ip6tables -A INPUT -s ::1 -d ::1 -j ACCEPT") == 0); + char command[100] = "ip6tables -A INPUT -p "; + strcat(command, packetType); + strcat(command, " --dport "); + strcat(command, buf); + strcat(command, " -j ACCEPT"); + assert(system(command) == 0); + sleep(2); + assert(system("ip6tables -A INPUT -j DROP") == 0); + PRINT_TEST_CASE_MSG("Firewall for incoming requests added on IPv6"); + assert(system("ip6tables -L") == 0); +} + +/* Add outgoing firewall rules for ipv4 addresses with packet type and port number */ +void outgoing_firewall_ipv4(const char *packetType, int portNumber) { + char buf[5]; + snprintf(buf, sizeof(buf), "%d", portNumber); + assert(system("iptables -F") == 0); + assert(system("iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") == 0); + assert(system("iptables -A OUTPUT -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT") == 0); + char command[100] = "iptables -A OUTPUT -p "; + strcat(command, packetType); + strcat(command, " --dport "); + strcat(command, buf); + strcat(command, " -j ACCEPT"); + assert(system(command) == 0); + sleep(2); + assert(system("iptables -A OUTPUT -j DROP") == 0); + PRINT_TEST_CASE_MSG("Firewall for outgoing requests added on IPv4"); + assert(system("iptables -L") == 0); +} + +/* Add outgoing firewall rules for ipv6 addresses with packet type and port number */ +void outgoing_firewall_ipv6(const char *packetType, int portNumber) { + char buf[5]; + snprintf(buf, sizeof(buf), "%d", portNumber); + assert(system("ip6tables -F") == 0); + assert(system("ip6tables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT") == 0); + assert(system("ip6tables -A OUTPUT -s ::1 -d ::1 -j ACCEPT") == 0); + char command[100] = "ip6tables -A OUTPUT -p "; + strcat(command, packetType); + strcat(command, " --dport "); + strcat(command, buf); + strcat(command, " -j ACCEPT"); + assert(system(command) == 0); + sleep(2); + assert(system("ip6tables -A OUTPUT -j DROP") == 0); + PRINT_TEST_CASE_MSG("Firewall for outgoing requests added on IPv6"); + assert(system("ip6tables -L") == 0); +} + +void bridge_add(const char *bridge_name) { + char cmd[500]; + + assert(bridge_name); + assert(snprintf(cmd, sizeof(cmd), "brctl addbr %s", bridge_name) >= 0); + assert(system(cmd) == 0); + assert(snprintf(cmd, sizeof(cmd), "ifconfig %s up", bridge_name) >= 0); + assert(system(cmd) == 0); +} + +void bridge_delete(const char *bridge_name) { + char cmd[500]; + + assert(bridge_name); + assert(snprintf(cmd, sizeof(cmd), "brctl delbr %s", bridge_name) >= 0); + assert(system(cmd) == 0); +} + +void bridge_add_interface(const char *bridge_name, const char *interface_name) { + char cmd[500]; + + assert(bridge_name || interface_name); + assert(snprintf(cmd, sizeof(cmd), "brctl addif %s %s", bridge_name, interface_name) >= 0); + assert(system(cmd) == 0); +}