You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
353 lines
9.0 KiB
353 lines
9.0 KiB
#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];
|
|
}
|
|
|