Commit 10797b1c authored by xpetrak2's avatar xpetrak2
Browse files

Routing and sending daemon messages

parent 33f1a37d
......@@ -16,7 +16,8 @@ src_coincerd_SOURCES = \
src/neighbours.c src/neighbours.h \
src/p2p.c src/p2p.h \
src/paths.c src/paths.h \
src/peers.c src/peers.h
src/peers.c src/peers.h \
src/routing.c src/routing.h
src_coincerd_CFLAGS = $(AM_CFLAGS) $(JANSSON_CFLAGS) $(LIBEVENT_CFLAGS) $(LIBSODIUM_CFLAGS)
src_coincerd_LDADD = $(AM_LDADD) $(JANSSON_LIBS) $(LIBEVENT_LIBS) $(LIBSODIUM_LIBS)
......
......@@ -4,6 +4,9 @@
- Host: A device that is possibly connected to the Coincer network
- Identifier: The public key of a cryptographic keypair
- Identity: A peer representation that we own (identifier + its corresponding secret key)
- Message trace: A tuple of a peer-to-peer message's nonce and a pointer to a neighbour from which we've received the message. The message traces are being used for routing loop detection
- n2n: neighbour-to-neighbour
- Neighbour: A host that we are interconnected with
- Nonce: Ever-increasing integer tied to every message sent for message repetiton or replay attack detection
- p2p: peer-to-peer
- Peer: The Coincer network participant
......@@ -26,15 +26,21 @@
#include "neighbours.h"
#include "p2p.h"
#include "peers.h"
#include "routing.h"
/** The time interval in seconds between loop_update_long calls. */
#define UPDATE_TIME_LONG 60
/** The time interval in seconds between loop_update_medium calls. */
#define UPDATE_TIME_MEDIUM 30
/** The time interval in seconds between loop_update_short calls. */
#define UPDATE_TIME_SHORT 10
static void loop_update_long(evutil_socket_t fd __attribute__((unused)),
short events __attribute__((unused)),
void *ctx);
static void loop_update_medium(evutil_socket_t fd __attribute__((unused)),
short events __attribute__((unused)),
void *ctx);
static void loop_update_short(evutil_socket_t fd __attribute__((unused)),
short events __attribute__((unused)),
void *ctx);
......@@ -57,7 +63,8 @@ static void connections_maintain(global_state_t *global_state)
needed_conns = MIN_NEIGHBOURS -
linkedlist_size(&global_state->neighbours);
if (needed_conns > 0) {
log_debug("conns_cb - need %d more neighbours", needed_conns);
log_debug("connections_maintain - need %d more neighbours",
needed_conns);
/* ask twice more hosts than we need; it's preferable
* to have more neighbours than minimum */
add_more_connections(global_state, 2 * needed_conns);
......@@ -120,6 +127,23 @@ int daemon_events_setup(global_state_t *global_state)
return 1;
}
/* register the medium time-interval loop updates */
interval.tv_sec = UPDATE_TIME_MEDIUM;
interval.tv_usec = 0;
event = event_new(global_state->event_loop,
-1,
EV_PERSIST,
loop_update_medium,
global_state);
if (!event ||
event_add(event, &interval) < 0 ||
!linkedlist_append(events, event)) {
log_error("Creating or adding medium loop update callback");
return 1;
}
/* register the long time-interval loop updates */
interval.tv_sec = UPDATE_TIME_LONG;
interval.tv_usec = 0;
......@@ -156,6 +180,25 @@ static void loop_update_long(evutil_socket_t fd __attribute__((unused)),
remove_stale_records(global_state);
}
/**
* The medium time-triggered loop update.
*
* @param fd File descriptor.
* @param events Event flags.
* @param ctx Global state.
*/
static void loop_update_medium(evutil_socket_t fd __attribute__((unused)),
short events __attribute__((unused)),
void *ctx)
{
global_state_t *global_state = (global_state_t *) ctx;
if (linkedlist_size(&global_state->neighbours) >= MIN_NEIGHBOURS) {
send_p2p_route_adv(&global_state->neighbours,
global_state->true_identity);
}
}
/**
* The short time-triggered loop update.
*
......@@ -184,10 +227,18 @@ static void remove_stale_records(global_state_t *global_state)
neighbour_t *current_neighbour;
linkedlist_node_t *current_node;
peer_t *current_peer;
time_t current_time;
linkedlist_node_t *next_node;
current_time = time(NULL);
/* if a route is stale, delete it from the routing table */
linkedlist_apply_if(&global_state->routing_table,
&current_time,
route_is_stale,
route_clear,
linkedlist_delete);
/* remove stale nonces of known peers */
current_list = &global_state->peers;
current_node = linkedlist_get_first(current_list);
......@@ -210,6 +261,13 @@ static void remove_stale_records(global_state_t *global_state)
current_node = linkedlist_get_next(current_list, current_node);
}
/* remove stale message traces */
linkedlist_apply_if(&global_state->message_traces,
&current_time,
message_trace_is_stale,
NULL,
linkedlist_delete);
/* remove unneeded identities */
current_list = &global_state->identities;
current_node = linkedlist_get_first(current_list);
......@@ -218,6 +276,8 @@ static void remove_stale_records(global_state_t *global_state)
next_node = linkedlist_get_next(current_list, current_node);
if (current_identity->flags & IDENTITY_TMP) {
send_p2p_bye(&global_state->neighbours,
current_identity);
linkedlist_delete(current_node);
}
......
......@@ -24,6 +24,7 @@
#include "neighbours.h"
#include "paths.h"
#include "peers.h"
#include "routing.h"
/**
* Clear global state variables.
......@@ -32,9 +33,11 @@
*/
void global_state_clear(global_state_t *global_state)
{
linkedlist_destroy(&global_state->routing_table, route_clear);
linkedlist_destroy(&global_state->pending_neighbours, clear_neighbour);
linkedlist_destroy(&global_state->peers, peer_clear);
linkedlist_destroy(&global_state->neighbours, clear_neighbour);
linkedlist_destroy(&global_state->message_traces, NULL);
linkedlist_destroy(&global_state->hosts, NULL);
/* event nodes' data are not malloc'd; just apply the removal */
linkedlist_apply(&global_state->events, event_free, linkedlist_remove);
......@@ -67,9 +70,13 @@ int global_state_init(global_state_t *global_state)
linkedlist_init(&global_state->events);
linkedlist_init(&global_state->hosts);
linkedlist_init(&global_state->message_traces);
linkedlist_init(&global_state->neighbours);
linkedlist_init(&global_state->peers);
linkedlist_init(&global_state->pending_neighbours);
linkedlist_init(&global_state->routing_table);
global_state->port = 0;
return 0;
}
......@@ -39,12 +39,19 @@ typedef struct s_global_state {
linkedlist_t hosts;
/** List of our identities. */
linkedlist_t identities;
/** Linked list of recently received message traces (their hash and
* a pointer to a neighbour who's sent us the message). */
linkedlist_t message_traces;
/** Linked list of our neighbours. */
linkedlist_t neighbours;
/** Known peers. */
linkedlist_t peers;
/** Hosts that didn't accept/reject us yet. */
linkedlist_t pending_neighbours;
/** We listen on this port. */
unsigned short port;
/** Routes to hosts. */
linkedlist_t routing_table;
/** Our true identity. */
identity_t *true_identity;
} global_state_t;
......
......@@ -27,17 +27,6 @@
#include "linkedlist.h"
#include "log.h"
/**
* Delete all hosts and their data.
*
* @param hosts Linkedlist of hosts.
*/
void clear_hosts(linkedlist_t *hosts)
{
/* host_t has no dynamically allocated variables */
linkedlist_destroy(hosts, NULL);
}
/**
* Fetch hosts from file into linkedlist.
*
......@@ -49,7 +38,7 @@ void clear_hosts(linkedlist_t *hosts)
*/
int fetch_hosts(const char *hosts_path, linkedlist_t *hosts)
{
struct in6_addr addr;
host_t current_host;
FILE *hosts_file;
hosts_file = fopen(hosts_path, "rb");
......@@ -59,8 +48,11 @@ int fetch_hosts(const char *hosts_path, linkedlist_t *hosts)
return 1;
}
while (fread(&addr, sizeof(struct in6_addr), 1, hosts_file) == 1) {
save_host(hosts, &addr);
while (fread(&current_host, sizeof(host_t), 1, hosts_file) == 1) {
save_host(hosts,
&current_host.addr,
current_host.port,
current_host.flags);
}
fclose(hosts_file);
......@@ -194,14 +186,19 @@ int hosts_to_str(const linkedlist_t *hosts, char **output)
/**
* Save new host into sorted linkedlist of hosts.
*
* @param hosts The linkedlist of hosts.
* @param addr Address of the new host.
* @param hosts The linkedlist of hosts.
* @param addr Address of the new host.
* @param port Host's listening port.
* @param flags Host's flags.
*
* @return host_t Newly saved host.
* @return NULL Host is already saved, default or
* allocation failure.
* @return host_t Newly saved host.
* @return NULL Host is already saved, default or
* allocation failure.
*/
host_t *save_host(linkedlist_t *hosts, const struct in6_addr *addr)
host_t *save_host(linkedlist_t *hosts,
const struct in6_addr *addr,
unsigned short port,
int flags)
{
int cmp_value;
struct in6_addr curr_addr;
......@@ -230,7 +227,9 @@ host_t *save_host(linkedlist_t *hosts, const struct in6_addr *addr)
/* initialize all attributes of the new host */
memcpy(&new_host->addr, addr, 16);
set_host_flags(new_host, HOST_AVAILABLE);
new_host->port = port;
new_host->flags = 0x0;
set_host_flags(new_host, flags);
/* get textual representation of 'addr' */
inet_ntop(AF_INET6, addr, text_ip, INET6_ADDRSTRLEN);
......@@ -330,8 +329,8 @@ int store_hosts(const char *hosts_path, const linkedlist_t *hosts)
while (current) {
current_host = (host_t *) current->data;
/* if fwrite fails, terminate storing */
if (fwrite(&current_host->addr,
sizeof(struct in6_addr),
if (fwrite(current_host,
sizeof(host_t),
1,
hosts_file) != 1) {
log_error("Storing hosts");
......
......@@ -56,11 +56,11 @@ typedef struct s_host {
struct in6_addr addr;
/** A set of flags for this host. */
int flags;
/* Host's listening port. */
unsigned short port;
/* TODO: add uptime */
} host_t;
void clear_hosts(linkedlist_t *hosts);
int fetch_hosts(const char *hosts_path, linkedlist_t *hosts);
int fetch_specific_hosts(const linkedlist_t *hosts,
......@@ -74,7 +74,10 @@ int hosts_to_str(const linkedlist_t *hosts, char **output);
void reset_hosts_availability(linkedlist_t *hosts);
host_t *save_host(linkedlist_t *hosts, const struct in6_addr *addr);
host_t *save_host(linkedlist_t *hosts,
const struct in6_addr *addr,
unsigned short port,
int flags);
void set_host_flags(host_t *host, int flags);
......
......@@ -36,6 +36,7 @@
#include "log.h"
#include "neighbours.h"
#include "p2p.h"
#include "routing.h"
/**
* Simple helper for conversion of binary IP to readable IP address.
......@@ -48,27 +49,6 @@ static void ip_to_string(const struct in6_addr *binary_ip, char *ip)
inet_ntop(AF_INET6, binary_ip, ip, INET6_ADDRSTRLEN);
}
/**
* Ask a neighbour for a list of addresses.
*
* @param global_state Data for the event loop to work with.
* @param neighbour The neighbour to be asked.
*/
static void ask_for_addresses(neighbour_t *neighbour)
{
struct bufferevent *bev;
if (!neighbour || !(bev = neighbour->buffer_event)) {
return;
}
/* send message "hosts" to the neighbour, as a request
* for the list of hosts; 6 is the number of bytes to be transmitted */
evbuffer_add(bufferevent_get_output(bev), "hosts", 6);
/* accept addresses only from those neighbours that we've asked */
set_neighbour_flags(neighbour, NEIGHBOUR_ADDRS_REQ);
}
/**
* Process received list of host addresses.
*
......@@ -86,7 +66,10 @@ static void process_hosts(global_state_t *global_state, char *hosts)
while (line) {
if (inet_pton(AF_INET6, line, &addr) == 1) {
save_host(&global_state->hosts, &addr);
save_host(&global_state->hosts,
&addr,
DEFAULT_PORT,
HOST_AVAILABLE);
}
line = strtok_r(NULL, delim, &save_ptr);
}
......@@ -186,9 +169,11 @@ static void p2p_process(struct bufferevent *bev, void *ctx)
*
* @param neighbours Linked list of neighbours.
* @param neighbour Timeout invoked on this neighbour.
* @param routing_table Routing table.
*/
static void timeout_process(linkedlist_t *neighbours,
neighbour_t *neighbour)
neighbour_t *neighbour,
linkedlist_t *routing_table)
{
char text_ip[INET6_ADDRSTRLEN];
......@@ -204,14 +189,12 @@ static void timeout_process(linkedlist_t *neighbours,
log_debug("timeout_process - sending ping to %s. "
"Failed pings: %lu", text_ip,
neighbour->failed_pings);
/* send ping to the inactive neighbour;
* 5 is the length of bytes to be transmitted */
evbuffer_add(bufferevent_get_output(neighbour->buffer_event),
"ping", 5);
send_p2p_ping(neighbour);
neighbour->failed_pings++;
} else {
log_info("%s timed out", text_ip);
/* remove this neighbour (next hop) from all routes */
routing_table_remove_next_hop(routing_table, neighbour);
linkedlist_delete_safely(neighbour->node, clear_neighbour);
}
}
......@@ -246,13 +229,17 @@ static void process_pending_neighbour(global_state_t *global_state,
/* move the neighbour from pending into neighbours */
linkedlist_move(neighbour->node, &global_state->neighbours);
/* send p2p.hello */
send_p2p_hello(neighbour, global_state->port);
needed_conns = MIN_NEIGHBOURS -
linkedlist_size(&global_state->neighbours);
/* if we need more neighbours */
if (needed_conns > 0) {
/* and we don't have enough hosts available */
if (available_hosts_size < needed_conns) {
ask_for_addresses(neighbour);
/* ask for some */
send_p2p_peers_sol(neighbour);
}
}
/* connecting to the neighbour was unsuccessful */
......@@ -282,13 +269,23 @@ static void process_neighbour(global_state_t *global_state,
if (events & BEV_EVENT_ERROR) {
log_info("Connection error, removing %s", text_ip);
/* remove this neighbour (next hop) from all routes */
routing_table_remove_next_hop(&global_state->routing_table,
neighbour);
linkedlist_delete_safely(neighbour->node, clear_neighbour);
} else if (events & BEV_EVENT_EOF) {
log_info("%s disconnected", text_ip);
/* remove this neighbour (next hop) from all routes */
routing_table_remove_next_hop(&global_state->routing_table,
neighbour);
linkedlist_delete_safely(neighbour->node, clear_neighbour);
/* timeout flag on 'bev' */
} else if (events & BEV_EVENT_TIMEOUT) {
timeout_process(&global_state->neighbours, neighbour);
timeout_process(&global_state->neighbours,
neighbour,
&global_state->routing_table);
}
}
......@@ -341,6 +338,7 @@ static void accept_connection(struct evconnlistener *listener,
neighbour_t *neigh;
struct in6_addr *new_addr;
host_t *host;
unsigned short port;
char text_ip[INET6_ADDRSTRLEN];
struct timeval timeout;
......@@ -349,6 +347,7 @@ static void accept_connection(struct evconnlistener *listener,
/* put binary representation of IP to 'new_addr' */
new_addr = &addr_in6->sin6_addr;
port = addr_in6->sin6_port;
ip_to_string(new_addr, text_ip);
......@@ -390,7 +389,7 @@ static void accept_connection(struct evconnlistener *listener,
log_info("New connection from [%s]:%d", text_ip,
ntohs(addr_in6->sin6_port));
host = save_host(&global_state->hosts, new_addr);
host = save_host(&global_state->hosts, new_addr, port, 0x0);
if (!host) {
host = find_host(&global_state->hosts, new_addr);
}
......@@ -441,7 +440,7 @@ int listen_init(struct evconnlistener **listener,
struct event_base **base = &global_state->event_loop;
struct sockaddr_in6 sock_addr;
int port = DEFAULT_PORT;
int port = global_state->port = DEFAULT_PORT;
*base = event_base_new();
if (!*base) {
......@@ -473,18 +472,20 @@ int listen_init(struct evconnlistener **listener,
}
/**
* Attempt to connect to a particular addr.
* Attempt to connect to a particular host.
*
* @param global_state Data for the event loop to work with.
* @param addr Binary IP of a host that we want to connect to.
* @param port Host's listening port.
*
* @return 0 The connection attempt was successful.
* @return 1 The host is already our neighbour.
* @return 2 The host is pending to become our neighbour.
* @return 3 Adding new pending neighbour unsuccessful.
*/
int connect_to_addr(global_state_t *global_state,
const struct in6_addr *addr)
int connect_to_host(global_state_t *global_state,
const struct in6_addr *addr,
unsigned short port)
{
struct bufferevent *bev;
host_t *host;
......@@ -516,8 +517,8 @@ int connect_to_addr(global_state_t *global_state,
memset(&sock_addr6, 0x0, sizeof(sock_addr6));
sock_addr6.sin6_family = AF_INET6;
sock_addr6.sin6_port = htons(DEFAULT_PORT);
memcpy(&sock_addr6.sin6_addr, addr, 16);
sock_addr6.sin6_port = htons(port);
memcpy(&sock_addr6.sin6_addr, addr, sizeof(struct in6_addr));
sock_addr = (struct sockaddr *) &sock_addr6;
sock_len = sizeof(sock_addr6);
......@@ -603,14 +604,14 @@ void add_more_connections(global_state_t *global_state, size_t conns_amount)
"connecting to a default host...");
/* choose random default host */
idx = get_random_uint32_t(DEFAULT_HOSTS_SIZE);
memcpy(&addr, DEFAULT_HOSTS[idx], 16);
memcpy(&addr, DEFAULT_HOSTS[idx], sizeof(struct in6_addr));
/* if the host becomes our neighbour, and we need more
* connections, get a list of hosts from them and attempt to
* connect to them; it's our goal to use as few default
* hosts as possible
*/
result = connect_to_addr(global_state, &addr);
result = connect_to_host(global_state, &addr, DEFAULT_PORT);
/* the host is already our neighbour; ask them for more addrs */
if (result == 1) {
......@@ -618,7 +619,7 @@ void add_more_connections(global_state_t *global_state, size_t conns_amount)
&addr,
compare_neighbour_addrs);
assert(neigh != NULL);
ask_for_addresses(neigh);
send_p2p_peers_sol(neigh);
log_debug("add_more_connections - "
"asking for hosts");
}
......@@ -635,8 +636,9 @@ void add_more_connections(global_state_t *global_state, size_t conns_amount)
idx = cur_conn_attempts;
selected_host = available_hosts[idx];
/* perform a connection attempt */
connect_to_addr(global_state,
&selected_host->addr);
connect_to_host(global_state,
&selected_host->addr,
selected_host->port);
cur_conn_attempts++;
}
}
......
......@@ -33,8 +33,9 @@
void add_more_connections(global_state_t *global_state, size_t conns_amount);
int connect_to_addr(global_state_t *global_state,
const struct in6_addr *addr);
int connect_to_host(global_state_t *global_state,
const struct in6_addr *addr,
unsigned short port);
int listen_init(struct evconnlistener **listener,
global_state_t *global_state);
......
This diff is collapsed.
/*
* Coincer
* Copyright (C) 2017-2018 Coincer Developers
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ROUTING_H
#define ROUTING_H
#include <sodium.h>
#include <stdint.h>
#include <time.h>
#include "daemon_messages.h"
#include "global_state.h"
#include "linkedlist.h"
#include "neighbours.h"
#include "peers.h"
/** After this many seconds we consider a message trace to be stale. */
#define MESSAGE_TRACE_STALE_TIME 60
/** After this many seconds we consider a route to be stale. */
#define ROUTE_STALE_TIME 60
/** A message trace representation. */
typedef struct s_message_trace {
/** Message's nonce value. */
uint64_t nonce_value;
/** Neighbour who's sent us the message */
const neighbour_t *sender;
/** Creation timestamp. */
time_t creation;
} message_trace_t;
/** Data type for a routing table. */
typedef struct s_route {
/** Destination peer. */
peer_t *destination;
/** List of possible next hops (pointers to our neighbours)
* to the destination. Naturally sorted by delay viability. */
linkedlist_t next_hops;
/** The time of the last destination's announcement of presence or
* the time of this route's creation. */
time_t last_update;
/** The route's node in the routing table. */
linkedlist_node_t *node;
} route_t;
int message_forward(const message_t *message,
const neighbour_t *sender,
global_state_t *global_state);
int message_trace_is_stale(const message_trace_t *msg_trace,
const time_t *current_time);
route_t *route_add(linkedlist_t *routing_table,
peer_t *dest,
neighbour_t *next_hop);
void route_clear(route_t *route);
void route_delete(linkedlist_t *routing_table, const unsigned char *dest_id);
route_t *route_find(const linkedlist_t *routing_table,
const unsigned char *dest_id);
int route_is_stale(const route_t *route, const time_t *current_time);
int route_next_hop_add(route_t *route, neighbour_t *next_hop);
void route_next_hop_remove(route_t *route, neighbour_t *next_hop);
int route_reset(route_t *route, neighbour_t *next_hop);
int routing_loop_detect(const linkedlist_t *msg_traces,
const neighbour_t *neighbour,
uint64_t nonce_value);
void routing_loop_remove(linkedlist_t *routing_table,
linkedlist_t *neighbours,
linkedlist_t *identities,
const unsigned char *dest_id);
void routing_table_remove_next_hop(linkedlist_t *routing_table,
neighbour_t *next_hop);
int send_p2p_bye(linkedlist_t *neighbours, identity_t *identity);
int send_p2p_hello(neighbour_t *dest, unsigned short port);
int send_p2p_peers_adv(neighbour_t *dest, const linkedlist_t *hosts);
int send_p2p_peers_sol(neighbour_t *dest);