mirror of
https://github.com/Hizenberg469/Routing-Table-Manager.git
synced 2026-04-19 18:02:23 +03:00
619 lines
13 KiB
C
619 lines
13 KiB
C
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <sys/shm.h>
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <arpa/inet.h>
|
|
#include <signal.h>
|
|
#include <sys/select.h>
|
|
|
|
#include "dll.h"
|
|
#include "mac-list.h"
|
|
#include "routing-table.h"
|
|
#include "sync.h"
|
|
|
|
#define MAX_CLIENTS 32
|
|
#define OP_LEN 128
|
|
#define MAX_MASK 32
|
|
|
|
extern int store_IP(const char* mac, const char* ip);
|
|
|
|
int synchronized; // indicates if server has finished sending current updates
|
|
int connection_socket; // socket to establish connections with new clients
|
|
int loop = 1; // indicates if server is still running to clients
|
|
|
|
|
|
/*
|
|
* Server's copies of network data structures
|
|
*/
|
|
|
|
dll_t* routing_table;
|
|
dll_t* mac_list;
|
|
|
|
/*
|
|
* An array of File descriptors which the server process is maintaining in order
|
|
* to talk with connected clients. Master skt FD is also a member of this array.
|
|
*/
|
|
|
|
int monitored_fd_set[MAX_CLIENTS];
|
|
pid_t client_pid_set[MAX_CLIENTS]; // array of client process id's.
|
|
|
|
/*
|
|
* Remove all the FDs and client pid's, if any, from the array.
|
|
*/
|
|
|
|
void initialize_monitor_fd_and_client_pid_set() {
|
|
int i = 0;
|
|
|
|
for (; i < MAX_CLIENTS; i++) {
|
|
monitored_fd_set[i] = -1;
|
|
client_pid_set[i] = -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add a new FD to the monitored_fd_set.
|
|
*/
|
|
|
|
void add_to_monitored_fd_set(int skt_fd) {
|
|
int i = 0;
|
|
for (; i < MAX_CLIENTS; i++) {
|
|
if (monitored_fd_set[i] != -1)
|
|
continue;
|
|
|
|
monitored_fd_set[i] = skt_fd;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add a new pid to the client_pid_set array
|
|
*/
|
|
|
|
void add_to_client_pid_set(pid_t pid) {
|
|
|
|
int i = 0;
|
|
for (; i < MAX_CLIENTS; i++) {
|
|
if (client_pid_set[i] != -1)
|
|
continue;
|
|
client_pid_set[i] = pid;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*Remove the FD from monitored_fd_set array*/
|
|
void remove_from_monitored_fd_set(int skt_fd) {
|
|
int i = 0;
|
|
for (; i < MAX_CLIENTS; i++) {
|
|
if (monitored_fd_set[i] != skt_fd)
|
|
continue;
|
|
monitored_fd_set[i] = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*Remove the pid from client_pid_set array*/
|
|
void remove_from_client_pid_set(int pid) {
|
|
int i = 0;
|
|
for (; i < MAX_CLIENTS; i++) {
|
|
if (monitored_fd_set[i] != pid)
|
|
continue;
|
|
client_pid_set[i] = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Clone all the fds in monitored_fd_set array into fd_set
|
|
* Data structure.
|
|
*/
|
|
void refresh_fd_set(fd_set* fd_set_ptr) {
|
|
|
|
FD_ZERO(fd_set_ptr);
|
|
int i = 0;
|
|
for (; i < MAX_CLIENTS; i++) {
|
|
if (monitored_fd_set[i] != -1) {
|
|
FD_SET(monitored_fd_set[i], fd_set_ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Inform clients to flush their routing tables and mac lists.
|
|
*/
|
|
|
|
void flush_clients() {
|
|
int i;
|
|
for (i = 0; i < MAX_CLIENTS; i++) {
|
|
int pid = client_pid_set[i];
|
|
if (pid != -1) {
|
|
kill(pid, SIGUSR1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Helper function for isValidMask.
|
|
*/
|
|
int digits_only(const char* s) {
|
|
|
|
while (*s) {
|
|
if (isdigit(*s++) == 0)return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Checks if IP address is Valid.
|
|
*/
|
|
int isValidIP(const char* addr) {
|
|
struct sockaddr_in sa;
|
|
return addr && inet_pton(AF_INET, addr, &(sa.sin_addr)) != 0;
|
|
}
|
|
|
|
/*
|
|
* Mask is valid if it is a string that can be
|
|
* converted to an integer within [0,32].
|
|
*/
|
|
|
|
int isValidMask(const char* mask) {
|
|
return digits_only(mask) && atoi(mask) <= 32;
|
|
}
|
|
|
|
/*
|
|
* Check if a given string represents a valid mac address
|
|
* in the format XX:XX:XX:XX:XX:XX, where each X represents
|
|
* a hexadecimal digit 0-9 or a-f.
|
|
*/
|
|
int isValidMAC(const char* addr) {
|
|
if (!addr)
|
|
return 0;
|
|
|
|
int i;
|
|
for (i = 0; i < MAC_ADDR_LEN - 1; i++) {
|
|
if (i % 3 != 2 && !isdigit(addr[i])) {
|
|
return 0;
|
|
}
|
|
|
|
if (i % 3 == 2 && addr[i] != ':')
|
|
return 0;
|
|
|
|
}
|
|
|
|
return addr[i] == '\0';
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the numerical max value among all fds which server
|
|
* is monitoring.
|
|
*/
|
|
|
|
int get_max_fd() {
|
|
int i = 0;
|
|
int max = -1;
|
|
|
|
for (; i < MAX_CLIENTS; i++) {
|
|
if (monitored_fd_set[i] > max)
|
|
max = monitored_fd_set[i];
|
|
}
|
|
|
|
return max;
|
|
}
|
|
|
|
/*
|
|
* Parses a string command, in the format
|
|
* <Opcode, Dest, Mask, GW, OIF> or <Opcode, MAC>
|
|
* with each field seperated by a space, to create
|
|
* a sync message for clients, instructing them on
|
|
* how to update their copies of the routing table.
|
|
* The silent parameter indicates whether the server
|
|
* is actively inputting a command for MAC list via
|
|
* stdin or a client is replicating a command sent by
|
|
* the server. Returns 0 on success and -1 on any
|
|
* failure.
|
|
*/
|
|
|
|
|
|
int create_sync_message(char* operation, sync_msg_t* sync_msg, int silent) {
|
|
|
|
char* token = strtok(operation, " ");
|
|
|
|
if (token) {
|
|
switch (token[0]) {
|
|
case 'C':
|
|
sync_msg->op_code = CREATE;
|
|
break;
|
|
|
|
case 'U':
|
|
sync_msg->op_code = UPDATE;
|
|
break;
|
|
|
|
case 'D':
|
|
sync_msg->op_code = DELETE;
|
|
break;
|
|
|
|
case 'S':
|
|
sync_msg->op_code = NONE;
|
|
display_routing_table(routing_table);
|
|
display_mac_list(mac_list);
|
|
return 0;
|
|
|
|
case 'F':
|
|
sync_msg->op_code = NONE;
|
|
|
|
flush_clients();
|
|
deinit_dll(routing_table);
|
|
deinit_dll(mac_list);
|
|
|
|
routing_table = init_dll();
|
|
mac_list = init_dll();
|
|
return 0;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "Invalid operation: unknown op code\n");
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
fprintf(stderr, "Invalid operation: missing op code\n");
|
|
return -1;
|
|
}
|
|
|
|
token = strtok(NULL, " ");
|
|
|
|
if (isValidIP(token)) {
|
|
sync_msg->l_code = L3;
|
|
memcpy(sync_msg->msg_body.routing_table_entry.dest, token, strlen(token));
|
|
}
|
|
else if (isValidMAC(token)) {
|
|
sync_msg->l_code = L2;
|
|
memcpy(sync_msg->msg_body.mac_list_entry.mac, token, strlen(token));
|
|
|
|
if (!silent && sync_msg->op_code == CREATE) {
|
|
|
|
printf("Enter an IP address:\n");
|
|
char ip[IP_ADDR_LEN];
|
|
int ret = read(0, ip, IP_ADDR_LEN);
|
|
|
|
ip[strcspn(ip, "\r\n")] = 0;
|
|
|
|
if (ret < 0 || store_IP(sync_msg->msg_body.mac_list_entry.mac, ip) == -1) {
|
|
fprintf(stderr, "Failed to store ip address\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
else {
|
|
fprintf(stderr, "Invalid operation: invalid or missing destination IP/MAC address\n");
|
|
return -1;
|
|
}
|
|
|
|
token = strtok(NULL, " ");
|
|
if (isValidMask(token)) {
|
|
sync_msg->msg_body.routing_table_entry.mask = atoi(token);
|
|
}
|
|
else {
|
|
fprintf(stderr, "Invalid operation: invalid or missing subnet mask/IP address\n");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Only CREATE and UPDATE require a gw and oif.
|
|
*/
|
|
|
|
if (sync_msg->op_code == CREATE || sync_msg->op_code == UPDATE) {
|
|
token = strtok(NULL, " ");
|
|
|
|
if (isValidIP(token)) {
|
|
memcpy(sync_msg->msg_body.routing_table_entry.gw, token, strlen(token));
|
|
}
|
|
else {
|
|
fprintf(stderr, "Invalid operation: invalid or missing gateway IP\n");
|
|
return -1;
|
|
}
|
|
|
|
token = strtok(NULL, " ");
|
|
if (token) {
|
|
memcpy(sync_msg->msg_body.routing_table_entry.oif, token, strlen(token));
|
|
}
|
|
else {
|
|
fprintf(stderr, "Invalid operation: missing OIF\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Break out of main infinite loop and inform clients of
|
|
* shutdown to exit clearly.
|
|
*/
|
|
|
|
void signal_handler(int signal_num) {
|
|
|
|
if (signal_num == SIGINT) {
|
|
int i, synchronized = WAIT, loop = 0;
|
|
sync_msg_t sync_msg;
|
|
sync_msg.op_code = NONE;
|
|
|
|
for (i = 2; i < MAX_CLIENTS; i++) {
|
|
int comm_socket_fd = monitored_fd_set[i];
|
|
|
|
if (comm_socket_fd != -1) {
|
|
write(comm_socket_fd, &sync_msg, sizeof(sync_msg_t));
|
|
write(comm_socket_fd, &synchronized, sizeof(int));
|
|
write(comm_socket_fd, &loop, sizeof(int));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Clean up resources.
|
|
*/
|
|
|
|
int shm_fd;
|
|
mac_list_entry_t* entry;
|
|
|
|
ITERATE_DLL(mac_list, entry, mac_list_entry_t)
|
|
|
|
shm_fd = shm_open(entry->mac,
|
|
O_CREAT | O_RDONLY,
|
|
0660);
|
|
|
|
shm_unlink(entry->mac);
|
|
close(shm_fd);
|
|
ITERATE_DLL_END()
|
|
|
|
deinit_dll(routing_table);
|
|
deinit_dll(mac_list);
|
|
close(connection_socket);
|
|
remove_from_monitored_fd_set(connection_socket);
|
|
unlink(SOCKET_NAME);
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Send newly client all necessary CREATE commands to replicate
|
|
* the server's copies of the current routing table or MAC list.
|
|
*/
|
|
|
|
void update_new_client(int data_socket, LCODE l_code, char* op, sync_msg_t* sync_msg) {
|
|
|
|
dll_node_t* head = l_code == L3 ? routing_table->head : mac_list->head;
|
|
dll_node_t* curr = head->next;
|
|
|
|
while (curr != head) {
|
|
routing_table_entry_t rt_entry = *((routing_table_entry_t*)curr->data);
|
|
|
|
mac_list_entry_t ml_entry = *((mac_list_entry_t*)curr->data);
|
|
|
|
sync_msg->op_code = CREATE;
|
|
|
|
if (l_code == L3) {
|
|
sprintf(op, "C %s %u %s %s",
|
|
rt_entry.dest,
|
|
rt_entry.mask,
|
|
rt_entry.gw,
|
|
rt_entry.oif);
|
|
}
|
|
else {
|
|
|
|
sprintf(op, "C %s", ml_entry.mac);
|
|
}
|
|
|
|
create_sync_message(op, sync_msg, 1);
|
|
|
|
write(data_socket, sync_msg, sizeof(sync_msg_t));
|
|
write(data_socket, &synchronized, sizeof(int));
|
|
write(data_socket, &loop, sizeof(int));
|
|
|
|
curr = curr->next;
|
|
}
|
|
|
|
/*
|
|
* Send dummy sync message to inform client that all
|
|
* current updates have been sent.
|
|
*/
|
|
|
|
sync_msg->op_code = NONE;
|
|
write(data_socket, sync_msg, sizeof(sync_msg_t));
|
|
synchronized = l_code == L3 ? RT : ML;
|
|
write(data_socket, &synchronized, sizeof(int));
|
|
write(data_socket, &loop, sizeof(int));
|
|
}
|
|
|
|
int main() {
|
|
|
|
struct sockaddr_un name;
|
|
int ret;
|
|
int data_socket;
|
|
fd_set readfds;
|
|
|
|
routing_table = init_dll();
|
|
mac_list = init_dll();
|
|
|
|
initialize_monitor_fd_and_client_pid_set();
|
|
add_to_monitored_fd_set(0);
|
|
|
|
unlink(SOCKET_NAME); //In case the program exited inadvertently on the last run, remove the socket.
|
|
|
|
/*
|
|
* Master socket for accepting connections
|
|
* from client
|
|
*/
|
|
|
|
connection_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
if (connection_socket == -1) {
|
|
perror("socket");
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* Initialize
|
|
*/
|
|
|
|
memset(&name, 0, sizeof(struct sockaddr_un));
|
|
|
|
/*
|
|
* Specify the socket Credentials
|
|
*/
|
|
name.sun_family = AF_UNIX;
|
|
strncpy(name.sun_path, SOCKET_NAME, sizeof(name.sun_path) - 1);
|
|
|
|
/*
|
|
* Bind socket to socket name
|
|
*/
|
|
|
|
ret = bind(connection_socket, (const struct sockaddr*) &name,
|
|
sizeof(struct sockaddr_un));
|
|
|
|
if (ret == -1) {
|
|
perror("bind");
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* Prepare for accepting connections.
|
|
*/
|
|
|
|
ret = listen(connection_socket, 20);
|
|
if (ret == -1) {
|
|
perror("listen");
|
|
exit(1);
|
|
}
|
|
|
|
add_to_monitored_fd_set(connection_socket);
|
|
|
|
signal(SIGINT, signal_handler); //register signal handler
|
|
|
|
/*
|
|
* The server continuously checks for new clients connections, monitors
|
|
* existing connections (i.e. incoming messages and inactive connections,
|
|
* modifies the routing table or MAC list if need be, and broadcasts to
|
|
* clients.
|
|
*/
|
|
|
|
while (1) {
|
|
char op[OP_LEN];
|
|
sync_msg_t* sync_msg = calloc(1, sizeof(sync_msg_t));
|
|
synchronized = 0;
|
|
|
|
refresh_fd_set(&readfds); /*Copy the entire monitored FDs to readfds*/
|
|
|
|
printf("Please select from the following options:\n");
|
|
printf("1.CREATE <Destination IP> <Mask (0-32)> <Gateway IP> <OIF>\n");
|
|
printf("2.UPDATE <Destination IP> <Mask (0-32)> <New Gateway IP> <New OIF>\n");
|
|
printf("3.DELETE <Destination IP> <Mask (0-32)>\n");
|
|
printf("4.CREATE <MAC>\n");
|
|
printf("5.DELETE <MAC>\n");
|
|
printf("6.SHOW\n");
|
|
printf("7.FLUSH\n");
|
|
|
|
select(get_max_fd() + 1, &readfds, NULL, NULL, NULL); /* Wait for incoming connections*/
|
|
|
|
/* New Connection : send entire routing table and mac list states to newly connected clients*/
|
|
if (FD_ISSET(connection_socket, &readfds)) {
|
|
data_socket = accept(connection_socket, NULL, NULL);
|
|
if (data_socket == -1) {
|
|
perror("accept");
|
|
exit(1);
|
|
}
|
|
|
|
pid_t pid;
|
|
if (read(data_socket, &pid, sizeof(pid_t)) == -1) {
|
|
perror("read");
|
|
exit(1);
|
|
}
|
|
|
|
add_to_monitored_fd_set(data_socket);
|
|
add_to_client_pid_set(pid);
|
|
|
|
update_new_client(data_socket, L3, op, sync_msg);
|
|
update_new_client(data_socket, L2, op, sync_msg);
|
|
|
|
}
|
|
else if (FD_ISSET(0, &readfds)) { // server stdin
|
|
|
|
ret = read(0, op, OP_LEN - 1);
|
|
|
|
op[strcspn(op, "\r\n")] = 0; //flush new line
|
|
|
|
if (ret == -1) {
|
|
perror("read");
|
|
return 1;
|
|
}
|
|
|
|
op[ret] = 0;
|
|
|
|
|
|
if (!create_sync_message(op, sync_msg, 0)) {
|
|
//Update server's tables
|
|
|
|
if (sync_msg->l_code == L3) {
|
|
process_sync_mesg(routing_table, sync_msg);
|
|
synchronized = RT;
|
|
}
|
|
else {
|
|
process_sync_mesg(mac_list, sync_msg);
|
|
synchronized = ML;
|
|
}
|
|
|
|
// Notify existing clients of change.
|
|
int i, comm_socket_fd;
|
|
for (i = 2; i < MAX_CLIENTS; i++) {
|
|
//Start at 2 since 0 and 1 are for server's stdin and stdout
|
|
comm_socket_fd = monitored_fd_set[i];
|
|
if (comm_socket_fd != -1) {
|
|
write(comm_socket_fd, sync_msg, sizeof(sync_msg_t));
|
|
write(comm_socket_fd, &synchronized, sizeof(int));
|
|
write(comm_socket_fd, &loop, sizeof(int));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/*
|
|
* Check active status of clients.
|
|
*/
|
|
|
|
int i;
|
|
for (i = 2; i < MAX_CLIENTS; i++) {
|
|
if (FD_ISSET(monitored_fd_set[i], &readfds)) {
|
|
int done;
|
|
int comm_socket_fd = monitored_fd_set[i];
|
|
|
|
ret = read(comm_socket_fd, &done, sizeof(int));
|
|
if (done == 1) {
|
|
//this client is disconnecting
|
|
close(comm_socket_fd);
|
|
remove_from_monitored_fd_set(comm_socket_fd);
|
|
}
|
|
else if (ret == -1) {
|
|
perror("read");
|
|
exit(1);
|
|
}
|
|
else {
|
|
printf("%i\n", done);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
exit(0);
|
|
} |