#include "helpers.h" /* * Function based on this example: * http://man7.org/linux/man-pages/man3/getifaddrs.3.html#EXAMPLE */ int get_own_id(void) { int id = -1; struct ifaddrs *ifaddr, *ifa; int family, s, n; char host[NI_MAXHOST]; if (getifaddrs(&ifaddr) == -1) { perror("Couldn't get network interfaces."); exit(EXIT_FAILURE); } // Walks through linked list, maintaining head pointer so we can free list later for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) { if (ifa->ifa_addr == NULL) { continue; } family = ifa->ifa_addr->sa_family; // Gets the address of an AF_INET* interface address if (family == AF_INET || family == AF_INET6) { s = getnameinfo(ifa->ifa_addr, (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (s != 0) { printf("getnameinfo() failed: %s\n", gai_strerror(s)); exit(EXIT_FAILURE); } id = extract_id_from_ip(host); if (id < 0) { continue; } break; } } freeifaddrs(ifaddr); return(id); } int extract_id_from_ip(const char *ip) { const char separator[2] = "."; int id = 0; char *rest, *token, *ip_cp; ip_cp = malloc(strlen(ip) * sizeof(char)); strcpy(ip_cp, ip); rest = ip_cp; token = strtok_r(rest, separator, &rest); if (!token || atoi(token) != 10) { return -1; } token = strtok_r(rest, separator, &rest); if (!token || atoi(token) != 0) { return -1; } token = strtok_r(rest, separator, &rest); if (!token) { return -1; } id = atoi(token) * 100; token = strtok_r(rest, separator, &rest); if (!token) { return -1; } id += atoi(token); return id; } void set_timer_and_handler(void (*handler)(int), long int timer_interval) { struct itimerval interval_timer; struct sigaction signal_action; // Installs handler as the signal handler for SIGALRM memset(&signal_action, 0, sizeof(signal_action)); signal_action.sa_handler = handler; if (sigaction(SIGALRM, &signal_action, NULL)) { perror("Couldn't install function handler for SIGALRM signals."); exit(EXIT_FAILURE); } // Sets an interval timer to deliver a SIGALRM signal every timer_interval seconds interval_timer.it_interval.tv_usec = 0; interval_timer.it_interval.tv_sec = timer_interval; interval_timer.it_value.tv_usec = 0; interval_timer.it_value.tv_sec = timer_interval; if (setitimer(ITIMER_REAL, &interval_timer, NULL) == -1) { perror("Couldn't set timer."); exit(EXIT_FAILURE); } } void enable_echo_broadcast(void) { if (system("echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts") < 0) { perror("Couldn't allow echo broadcasts."); exit(EXIT_FAILURE); } } /* * Function based on this snippet: * https://codereview.stackexchange.com/a/58107 */ void search_for_neighbors(node_handle_t **neighbors, uint16_t *num_neighbors, uint16_t port) { // Broadcasts ping if (system("ping -b 10.255.255.255 -c 3 > /dev/null") < 0) { perror("Couldn't broadcast echo."); exit(EXIT_FAILURE); } // Reads the ARP file checking for connected devices FILE *arpCache = fopen(ARP_CACHE, "r"); if (!arpCache) { perror("ARP Cache: Failed to open file \"" ARP_CACHE "\""); exit(EXIT_FAILURE); } // Ignores the first line, which contains the header char header[ARP_BUFFER_LEN]; if (!fgets(header, sizeof(header), arpCache)) { perror("Couldn't read ARP file header."); exit(EXIT_FAILURE); } // Extracts IP addresses found in the file char ipAddr[ARP_BUFFER_LEN]; char **neighbors_ips = (char **) malloc(sizeof(char *)); if (!neighbors_ips) { perror("Unable to allocate memory for neighbor IP."); exit(EXIT_FAILURE); } int count = 0; while (1 == fscanf(arpCache, ARP_LINE_FORMAT, ipAddr)) { ++count; if (count > 1) { char **r_neighbors_ips = realloc(neighbors_ips, count * sizeof(char *)); if (!r_neighbors_ips) { free(r_neighbors_ips); perror("Unable to reallocate memory for neighbor IP."); exit(EXIT_FAILURE); } neighbors_ips = r_neighbors_ips; } neighbors_ips[count - 1] = (char *) malloc(ARP_BUFFER_LEN * sizeof(char)); strcpy(neighbors_ips[count - 1], ipAddr); } // Allocates memory for the new neighbors structs if (!(*num_neighbors)) { // Neighbors array came empty (*neighbors) = (node_handle_t *) malloc(count * sizeof(node_handle_t)); if (!neighbors_ips) { perror("Unable to allocate memory for nodes."); exit(EXIT_FAILURE); } } else { node_handle_t *r_neighbors = realloc((*neighbors), count * sizeof(node_handle_t)); if (!r_neighbors) { free(r_neighbors); perror("Unable to reallocate memory for nodes."); exit(EXIT_FAILURE); } (*neighbors) = r_neighbors; } // Adds new event timestamps to neighbors structs for (uint8_t i = 0; i < count; ++i) { bool found_flag = false; for (uint8_t j = 0; j < (*num_neighbors); ++j) { if (!strcmp(inet_ntoa(node_get_addr((*neighbors)[j]).sin_addr), neighbors_ips[i])) { bool node_alive = check_node_alive(neighbors_ips[i]); node_add_timestamp((*neighbors)[i], time(NULL), node_alive); found_flag = true; break; } } if (!found_flag) { // New node found! printf("Adding %s\n", neighbors_ips[i]); struct sockaddr_in peer_name; init_sockaddr(&peer_name, neighbors_ips[i], port); (*neighbors)[(*num_neighbors)++] = node_init(peer_name); } } if (!((*num_neighbors) == count)) { (*num_neighbors) = count; } fclose(arpCache); } void create_message(node_handle_t *neighbors, char *new_message, int own_id, uint8_t num_neighbors, uint16_t max_message_length) { node_handle_t random_node = neighbors[rand() % (num_neighbors)]; int peer_id = extract_id_from_ip(inet_ntoa((node_get_addr(random_node)).sin_addr)); if (peer_id < 0) { perror("Couldn't extract own ID."); exit(EXIT_FAILURE); } snprintf(new_message, max_message_length, "%04d_%04d_%ld_%s", own_id, peer_id, time(NULL), "It's amazing... It's fantastic!"); } bool check_node_alive(const char *ipv4) { char command[64]; snprintf(command, 64, "ping %s -c 1 -W 1 | grep \"1 received\" > /dev/null", ipv4); int call_res = system(command); if (call_res < 0) { perror("Couldn't ping node."); exit(EXIT_FAILURE); } return call_res == 0; } /* * Function based on this example: * https://www.gnu.org/software/libc/manual/html_node/Inet-Example.html#Inet-Example */ int create_socket_and_listen(uint16_t port, uint8_t backlog_size) { int in_sock; struct sockaddr_in own_name; // Creates the socket in_sock = socket(PF_INET, SOCK_STREAM, 0); if (in_sock < 0) { perror("Couldn't create the socket."); exit(EXIT_FAILURE); } // Gives the socket a name own_name.sin_family = AF_INET; own_name.sin_port = htons(port); own_name.sin_addr.s_addr = htonl(INADDR_ANY); // Binds own address structure to socket if (bind(in_sock, (struct sockaddr *) &own_name, sizeof(own_name)) < 0) { perror("Couldn't bind the address structure to the socket."); exit(EXIT_FAILURE); } if (listen(in_sock, backlog_size) < 0) { perror("Couldn't listen for connections on the socket."); exit(EXIT_FAILURE); } return in_sock; } void send_message(struct sockaddr_in peer_name, const char *message) { int out_sock; // Creates the socket out_sock = socket(PF_INET, SOCK_STREAM, 0); if (out_sock < 0) { perror("Couldn't create the socket."); exit(EXIT_FAILURE); } // Connects to the peer if (connect(out_sock, (struct sockaddr *) &peer_name, sizeof(peer_name))) { printf("Couldn't connect to the peer.\n"); } else { // Sends data to the peer write_to_peer(out_sock, message); } close(out_sock); } void accept_connection(int sock, struct sockaddr_in *peer_name, fd_set *active_fd_set) { size_t peer_name_size = sizeof((*peer_name)); int comm_socket = accept(sock, (struct sockaddr *) peer_name, &peer_name_size); if (comm_socket < 0) { perror("Couldn't accept the connection."); exit(EXIT_FAILURE); } fprintf(stderr, "Connected to host %s, port %hd.\n", inet_ntoa((*peer_name).sin_addr), ntohs((*peer_name).sin_port)); FD_SET(comm_socket, active_fd_set); } void write_to_peer(int file_desc, const char *message) { int num_bytes = write(file_desc, message, strlen(message) + 1); if (num_bytes < 0) { perror("Couldn't write to peer."); exit(EXIT_FAILURE); } } int read_from_peer(int file_des, uint16_t max_line) { char buffer[max_line]; int num_bytes; num_bytes = read(file_des, buffer, sizeof(buffer)); if (num_bytes < 0) { perror("Couldn't read from peer."); exit(EXIT_FAILURE); } else if (num_bytes == 0) // End-of-file return -1; else { fprintf(stderr, "Got message: `%s'\n", buffer); return 0; } } /* * Function based on this example: * https://www.gnu.org/software/libc/manual/html_node/Inet-Example.html#Inet-Example */ void init_sockaddr(struct sockaddr_in *peer_name, const char *ipv4, uint16_t port) { struct hostent *hostinfo; peer_name->sin_family = AF_INET; peer_name->sin_port = htons(port); hostinfo = gethostbyname(ipv4); if (hostinfo == NULL) { fprintf(stderr, "Unknown host %s.\n", ipv4); exit(EXIT_FAILURE); } peer_name->sin_addr = *(struct in_addr *) hostinfo->h_addr_list[0]; }