X-Git-Url: http://git.meshlink.io/?p=meshlink;a=blobdiff_plain;f=src%2Fnet.c;h=2ee8aee446bc1df899354fb1cb93c1373e7aa844;hp=b368e505b763106429badb9a7ef1a855110bc06f;hb=8c8b506da9ce578fe6e6e406a91bbd21af13f7a3;hpb=58f7363afa1960bbee1fa03f3d24db3b2a66cda8 diff --git a/src/net.c b/src/net.c index b368e505..2ee8aee4 100644 --- a/src/net.c +++ b/src/net.c @@ -1,6 +1,6 @@ /* net.c -- most of the network code - Copyright (C) 2014 Guus Sliepen + Copyright (C) 2014-2017 Guus Sliepen 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 @@ -29,11 +29,19 @@ #include "net.h" #include "netutl.h" #include "protocol.h" +#include "sptps.h" #include "xalloc.h" -static const int min(int a, int b) { +#include + +#if !defined(min) +static inline int min(int a, int b) { return a < b ? a : b; } +#endif + +static const int default_timeout = 5; +static const int default_interval = 60; /* Terminate a connection: @@ -43,16 +51,18 @@ static const int min(int a, int b) { - Check if we need to retry making an outgoing connection */ void terminate_connection(meshlink_handle_t *mesh, connection_t *c, bool report) { - logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Closing connection with %s (%s)", c->name, c->hostname); + logger(mesh, MESHLINK_INFO, "Closing connection with %s", c->name); c->status.active = false; - if(c->node && c->node->connection == c) + if(c->node && c->node->connection == c) { c->node->connection = NULL; + } if(c->edge) { - if(report) - send_del_edge(mesh, mesh->everyone, c->edge); + if(report) { + send_del_edge(mesh, mesh->everyone, c->edge, 0); + } edge_del(mesh, c->edge); c->edge = NULL; @@ -63,11 +73,12 @@ void terminate_connection(meshlink_handle_t *mesh, connection_t *c, bool report) /* If the node is not reachable anymore but we remember it had an edge to us, clean it up */ - if(report && !c->node->status.reachable) { + if(report && c->node && !c->node->status.reachable) { edge_t *e; e = lookup_edge(c->node, mesh->self); + if(e) { - send_del_edge(mesh, mesh->everyone, e); + send_del_edge(mesh, mesh->everyone, e, 0); edge_del(mesh, e); } } @@ -78,14 +89,9 @@ void terminate_connection(meshlink_handle_t *mesh, connection_t *c, bool report) /* Check if this was our outgoing connection */ - if(outgoing) + if(outgoing) { do_outgoing_connection(mesh, outgoing); - -#ifndef HAVE_MINGW - /* Clean up dead proxy processes */ - - while(waitpid(-1, NULL, WNOHANG) > 0); -#endif + } } /* @@ -97,101 +103,243 @@ void terminate_connection(meshlink_handle_t *mesh, connection_t *c, bool report) and close the connection. */ static void timeout_handler(event_loop_t *loop, void *data) { + assert(data); + meshlink_handle_t *mesh = loop->data; + logger(mesh, MESHLINK_DEBUG, "timeout_handler()"); for list_each(connection_t, c, mesh->connections) { - if(c->last_ping_time + mesh->pingtimeout <= mesh->loop.now.tv_sec) { + int pingtimeout = c->node ? mesh->dev_class_traits[c->node->devclass].pingtimeout : default_timeout; + int pinginterval = c->node ? mesh->dev_class_traits[c->node->devclass].pinginterval : default_interval; + + if(c->outgoing && c->outgoing->timeout < 5) { + pingtimeout = 1; + } + + // Also make sure that if outstanding key requests for the UDP counterpart of a connection has timed out, we restart it. + if(c->node) { + if(c->node->status.waitingforkey && c->node->last_req_key + pingtimeout <= mesh->loop.now.tv_sec) { + send_req_key(mesh, c->node); + } + } + + if(c->status.active && c->last_key_renewal + 3600 < mesh->loop.now.tv_sec) { + if(!sptps_force_kex(&c->sptps)) { + logger(mesh, MESHLINK_ERROR, "SPTPS key renewal for connection with %s failed", c->name); + terminate_connection(mesh, c, true); + continue; + } else { + c->last_key_renewal = mesh->loop.now.tv_sec; + } + } + + if(c->last_ping_time + pingtimeout <= mesh->loop.now.tv_sec) { if(c->status.active) { if(c->status.pinged) { - logger(DEBUG_CONNECTIONS, LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds", c->name, c->hostname, (long)mesh->loop.now.tv_sec - c->last_ping_time); - } else if(c->last_ping_time + mesh->pinginterval <= mesh->loop.now.tv_sec) { + logger(mesh, MESHLINK_INFO, "%s didn't respond to PING in %ld seconds", c->name, (long)mesh->loop.now.tv_sec - c->last_ping_time); + } else if(c->last_ping_time + pinginterval <= mesh->loop.now.tv_sec) { send_ping(mesh, c); continue; } else { continue; } } else { - if(c->status.connecting) - logger(DEBUG_CONNECTIONS, LOG_WARNING, "Timeout while connecting to %s (%s)", c->name, c->hostname); - else - logger(DEBUG_CONNECTIONS, LOG_WARNING, "Timeout from %s (%s) during authentication", c->name, c->hostname); + if(c->status.connecting) { + logger(mesh, MESHLINK_WARNING, "Timeout while connecting to %s", c->name); + } else { + logger(mesh, MESHLINK_WARNING, "Timeout from %s during authentication", c->name); + } } + terminate_connection(mesh, c, c->status.active); } } - timeout_set(&mesh->loop, data, &(struct timeval){mesh->pingtimeout, rand() % 100000}); + timeout_set(&mesh->loop, data, &(struct timespec) { + 1, prng(mesh, TIMER_FUDGE) + }); } -/// Utility function to establish connections based on condition check -/** The function iterates over all nodes, but skips those that do - * not pass the condition check. - * - * The condition check function is passed - * a pointer to a random number r between 0 and rand_modulo, a pointer to the - * current node index i, and the node pointer n. This function should return true - * if a connection attempt to the node should be made. - * - * @param mesh A pointer to the mesh structure - * @param rand_modulo Random index is selected between 0 and rand_modulo - * @cond_check A function pointer. This function should return true - * if a connection attempt to the node should be made - */ -static void cond_add_connection(meshlink_handle_t *mesh, int rand_modulo, bool (*cond_check)(int*, int*, node_t*)) { - int r = rand() % rand_modulo; - int i = 0; +// devclass asc, last_successfull_connection desc +static int node_compare_devclass_asc_lsc_desc(const void *a, const void *b) { + const node_t *na = a, *nb = b; - for splay_each(node_t, n, mesh->nodes) { - /* skip nodes that do not pass condition check */ - if(!(*cond_check)(&i, &r, n)) - continue; + if(na->devclass < nb->devclass) { + return -1; + } - /* check if there is already a connection attempt to this node */ - bool found = false; - for list_each(outgoing_t, outgoing, mesh->outgoings) { - if(!strcmp(outgoing->name, n->name)) { - found = true; - break; - } - } + if(na->devclass > nb->devclass) { + return 1; + } - if(!found) { - //TODO: if the node is blacklisted the connection will not happen, but - //the user will read this debug message "Autoconnecting to %s" that is misleading - logger(DEBUG_CONNECTIONS, LOG_INFO, "Autoconnecting to %s", n->name); - outgoing_t *outgoing = xzalloc(sizeof *outgoing); - outgoing->mesh = mesh; - outgoing->name = xstrdup(n->name); - list_insert_tail(mesh->outgoings, outgoing); - setup_outgoing_connection(mesh, outgoing); - } - break; + if(na->last_successfull_connection == nb->last_successfull_connection) { + return 0; + } + + if(na->last_successfull_connection == 0 || na->last_successfull_connection > nb->last_successfull_connection) { + return -1; } + + if(nb->last_successfull_connection == 0 || na->last_successfull_connection < nb->last_successfull_connection) { + return 1; + } + + if(na < nb) { + return -1; + } + + if(na > nb) { + return 1; + } + + return 0; } -static bool found_random_node(int *i, int *r, node_t *n) { - if((*i)++ != *r) - return false; +// last_successfull_connection desc +static int node_compare_lsc_desc(const void *a, const void *b) { + const node_t *na = a, *nb = b; + + if(na->last_successfull_connection == nb->last_successfull_connection) { + return 0; + } + + if(na->last_successfull_connection == 0 || na->last_successfull_connection > nb->last_successfull_connection) { + return -1; + } + + if(nb->last_successfull_connection == 0 || na->last_successfull_connection < nb->last_successfull_connection) { + return 1; + } + + if(na < nb) { + return -1; + } - if(n->connection) - return false; - - return true; + if(na > nb) { + return 1; + } + + return 0; } -static bool found_random_unreachable_node(int *i, int *r, node_t *n) { - if(n->status.reachable) - return false; - - if((*i)++ != *r) - return false; +// devclass desc +static int node_compare_devclass_desc(const void *a, const void *b) { + const node_t *na = a, *nb = b; - if(n->connection) - return false; + if(na->devclass < nb->devclass) { + return -1; + } - return true; + if(na->devclass > nb->devclass) { + return 1; + } + + if(na < nb) { + return -1; + } + + if(na > nb) { + return 1; + } + + return 0; } + +/* + +autoconnect() +{ + timeout = 5 + + // find the best one for initial connect + + if cur < min + newcon = + first from nodes + where dclass <= my.dclass and !connection and (timestamp - last_retry) > retry_timeout + order by dclass asc, last_connection desc + if newcon + timeout = 0 + goto connect + + + // find better nodes to connect to: in case we have less than min connections within [BACKBONE, i] and there are nodes which we are not connected to within the range + + if min <= cur < max + j = 0 + for i = BACKBONE to my.dclass + j += count(from connections where node.dclass = i) + if j < min + newcon = + first from nodes + where dclass = i and !connection and (timestamp - last_retry) > retry_timeout + order by last_connection desc + if newcon + goto connect + else + break + + + // heal partitions + + if min <= cur < max + newcon = + first from nodes + where dclass <= my.dclass and !reachable and (timestamp - last_retry) > retry_timeout + order by dclass asc, last_connection desc + if newcon + goto connect + + + // connect + +connect: + if newcon + connect newcon + + + // disconnect outgoing connections in case we have more than min connections within [BACKBONE, i] and there are nodes which we are connected to within the range [i, PORTABLE] + + if min < cur <= max + j = 0 + for i = BACKBONE to my.dclass + j += count(from connections where node.dclass = i) + if min < j + delcon = + first from nodes + where dclass >= i and outgoing_connection + order by dclass desc + if disconnect + goto disconnect + else + break + + + // disconnect connections in case we have more than enough connections + + if max < cur + delcon = + first from nodes + where outgoing_connection + order by dclass desc + goto disconnect + + // disconnect + +disconnect + if delcon + disconnect delcon + + + // next iteration + next (timeout, autoconnect) + +} + +*/ + + static void periodic_handler(event_loop_t *loop, void *data) { meshlink_handle_t *mesh = loop->data; @@ -201,120 +349,299 @@ static void periodic_handler(event_loop_t *loop, void *data) { */ if(mesh->contradicting_del_edge > 100 && mesh->contradicting_add_edge > 100) { - logger(DEBUG_ALWAYS, LOG_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", mesh->sleeptime); - usleep(mesh->sleeptime * 1000000LL); + logger(mesh, MESHLINK_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", mesh->sleeptime); + struct timespec ts = {mesh->sleeptime, 0}; + clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL); mesh->sleeptime *= 2; - if(mesh->sleeptime < 0) + + if(mesh->sleeptime < 0) { mesh->sleeptime = 3600; + } } else { mesh->sleeptime /= 2; - if(mesh->sleeptime < 10) + + if(mesh->sleeptime < 10) { mesh->sleeptime = 10; + } } mesh->contradicting_add_edge = 0; mesh->contradicting_del_edge = 0; - int timeout = 5; + int timeout = default_timeout; + + /* Check if we need to make or break connections. */ + + if(mesh->nodes->count > 1) { + + logger(mesh, MESHLINK_DEBUG, "--- autoconnect begin ---"); + + int retry_timeout = min(mesh->nodes->count * default_timeout, 60); - /* If AutoConnect is set, check if we need to make or break connections. */ + logger(mesh, MESHLINK_DEBUG, "* devclass = %d", mesh->devclass); + logger(mesh, MESHLINK_DEBUG, "* nodes = %d", mesh->nodes->count); + logger(mesh, MESHLINK_DEBUG, "* retry_timeout = %d", retry_timeout); + + + // connect disconnect nodes + + node_t *connect_to = NULL; + node_t *disconnect_from = NULL; + + + // get cur_connects + + unsigned int cur_connects = 0; - if(autoconnect && mesh->nodes->count > 1) { - /* Count number of active connections */ - int nc = 0; for list_each(connection_t, c, mesh->connections) { - if(c->status.active) - nc++; + if(c->status.active) { + cur_connects += 1; + } } - /* Count number of unreachable nodes */ - int num_unreachable = 0; - for splay_each(node_t, n, mesh->nodes) { - if(!n->status.reachable) - num_unreachable++; + logger(mesh, MESHLINK_DEBUG, "* cur_connects = %d", cur_connects); + logger(mesh, MESHLINK_DEBUG, "* outgoings = %d", mesh->outgoings->count); + + // get min_connects and max_connects + + unsigned int min_connects = mesh->dev_class_traits[mesh->devclass].min_connects; + unsigned int max_connects = mesh->dev_class_traits[mesh->devclass].max_connects; + + logger(mesh, MESHLINK_DEBUG, "* min_connects = %d", min_connects); + logger(mesh, MESHLINK_DEBUG, "* max_connects = %d", max_connects); + + // find the best one for initial connect + + if(cur_connects < min_connects) { + splay_tree_t *nodes = splay_alloc_tree(node_compare_devclass_asc_lsc_desc, NULL); + + for splay_each(node_t, n, mesh->nodes) { + logger(mesh, MESHLINK_DEBUG, "* %s->devclass = %d", n->name, n->devclass); + + if(n != mesh->self && n->devclass <= mesh->devclass && !n->connection && !n->status.blacklisted && (n->last_connect_try == 0 || (mesh->loop.now.tv_sec - n->last_connect_try) > retry_timeout)) { + splay_insert(nodes, n); + } + } + + if(nodes->head) { + //timeout = 0; + connect_to = (node_t *)nodes->head->data; + + logger(mesh, MESHLINK_DEBUG, "* found best one for initial connect: %s", connect_to->name); + } else { + logger(mesh, MESHLINK_DEBUG, "* could not find node for initial connect"); + } + + splay_delete_tree(nodes); } - if(nc < autoconnect) { - /* Not enough active connections, try to add one. - Choose a random node, if we don't have a connection to it, - and we are not already trying to make one, create an - outgoing connection to this node. - */ - cond_add_connection(mesh, mesh->nodes->count, &found_random_node); - } else if(num_unreachable > 0) { - /* Min number of connections established. Now try - to connect to some unreachable nodes to attempt - to heal possible partitions. - */ - cond_add_connection(mesh, num_unreachable, &found_random_unreachable_node); + + // find better nodes to connect to + + if(!connect_to && min_connects <= cur_connects && cur_connects < max_connects) { + unsigned int connects = 0; + + for(dev_class_t devclass = 0; devclass <= mesh->devclass; ++devclass) { + for list_each(connection_t, c, mesh->connections) { + if(c->status.active && c->node && c->node->devclass == devclass) { + connects += 1; + } + } + + if(connects < min_connects) { + splay_tree_t *nodes = splay_alloc_tree(node_compare_lsc_desc, NULL); + + for splay_each(node_t, n, mesh->nodes) { + if(n != mesh->self && n->devclass == devclass && !n->connection && !n->status.blacklisted && (n->last_connect_try == 0 || (mesh->loop.now.tv_sec - n->last_connect_try) > retry_timeout)) { + splay_insert(nodes, n); + } + } + + if(nodes->head) { + logger(mesh, MESHLINK_DEBUG, "* found better node"); + connect_to = (node_t *)nodes->head->data; + + splay_delete_tree(nodes); + break; + } + + splay_delete_tree(nodes); + } else { + break; + } + } + + if(!connect_to) { + logger(mesh, MESHLINK_DEBUG, "* could not find better nodes"); + } } - - if(nc > autoconnect) { - /* Too many active connections, try to remove one. - Choose a random outgoing connection to a node - that has at least one other connection. - */ - int r = rand() % nc; - int i = 0; - for list_each(connection_t, c, mesh->connections) { - if(!c->status.active) - continue; - if(i++ != r) - continue; + // heal partitions + + if(!connect_to && min_connects <= cur_connects && cur_connects < max_connects) { + splay_tree_t *nodes = splay_alloc_tree(node_compare_devclass_asc_lsc_desc, NULL); + + for splay_each(node_t, n, mesh->nodes) { + if(n != mesh->self && n->devclass <= mesh->devclass && !n->status.reachable && !n->status.blacklisted && (n->last_connect_try == 0 || (mesh->loop.now.tv_sec - n->last_connect_try) > retry_timeout)) { + splay_insert(nodes, n); + } + } + + if(nodes->head) { + logger(mesh, MESHLINK_DEBUG, "* try to heal partition"); + connect_to = (node_t *)nodes->head->data; + } else { + logger(mesh, MESHLINK_DEBUG, "* could not find nodes for partition healing"); + } - if(!c->outgoing || !c->node || c->node->edge_tree->count < 2) + splay_delete_tree(nodes); + } + + + // perform connect + + if(connect_to && !connect_to->connection) { + connect_to->last_connect_try = mesh->loop.now.tv_sec; + logger(mesh, MESHLINK_DEBUG, "Autoconnect trying to connect to %s", connect_to->name); + + /* check if there is already a connection attempt to this node */ + bool skip = false; + + for list_each(outgoing_t, outgoing, mesh->outgoings) { + if(outgoing->node == connect_to) { + logger(mesh, MESHLINK_DEBUG, "* skip autoconnect since it is an outgoing connection already"); + skip = true; break; + } + } + + if(!connect_to->status.reachable && !node_read_public_key(mesh, connect_to)) { + logger(mesh, MESHLINK_DEBUG, "* skip autoconnect since we don't know this node's public key"); + skip = true; + } - logger(DEBUG_CONNECTIONS, LOG_INFO, "Autodisconnecting from %s", c->name); - list_delete(mesh->outgoings, c->outgoing); - c->outgoing = NULL; - terminate_connection(mesh, c, c->status.active); - break; + if(!skip) { + logger(mesh, MESHLINK_DEBUG, "Autoconnecting to %s", connect_to->name); + outgoing_t *outgoing = xzalloc(sizeof(outgoing_t)); + outgoing->node = connect_to; + list_insert_tail(mesh->outgoings, outgoing); + setup_outgoing_connection(mesh, outgoing); } } - if(nc >= autoconnect) { - /* If we have enough active connections, - remove any pending outgoing connections. - Do not remove pending connections to unreachable - nodes. - */ - node_t *o_node = NULL; - for list_each(outgoing_t, o, mesh->outgoings) { - o_node = lookup_node(mesh, o->name); - /* o_node is NULL if it is not part of the graph yet */ - if(!o_node || !o_node->status.reachable) - continue; - bool found = false; + // disconnect suboptimal outgoing connections + + if(min_connects < cur_connects /*&& cur_connects <= max_connects*/) { + unsigned int connects = 0; + + for(dev_class_t devclass = 0; devclass <= mesh->devclass; ++devclass) { for list_each(connection_t, c, mesh->connections) { - if(c->outgoing == o) { - found = true; - break; + if(c->status.active && c->node && c->node->devclass == devclass) { + connects += 1; } } - if(!found) { - logger(DEBUG_CONNECTIONS, LOG_INFO, "Cancelled outgoing connection to %s", o->name); - /* The node variable is leaked in from using the list_each macro. - The o variable could be used, but using node directly - is more efficient. - */ - list_delete_node(mesh->outgoings, node); + + if(min_connects < connects) { + splay_tree_t *nodes = splay_alloc_tree(node_compare_devclass_desc, NULL); + + for list_each(connection_t, c, mesh->connections) { + if(c->outgoing && c->node && c->node->devclass >= devclass) { + splay_insert(nodes, c->node); + } + } + + if(nodes->head) { + logger(mesh, MESHLINK_DEBUG, "* disconnect suboptimal outgoing connection"); + disconnect_from = (node_t *)nodes->head->data; + } + + splay_delete_tree(nodes); + break; + } + } + + if(!disconnect_from) { + logger(mesh, MESHLINK_DEBUG, "* no suboptimal outgoing connections"); + } + } + + + // disconnect connections (too many connections) + + if(!disconnect_from && max_connects < cur_connects) { + splay_tree_t *nodes = splay_alloc_tree(node_compare_devclass_desc, NULL); + + for list_each(connection_t, c, mesh->connections) { + if(c->status.active && c->node) { + splay_insert(nodes, c->node); } } + + if(nodes->head) { + logger(mesh, MESHLINK_DEBUG, "* disconnect connection (too many connections)"); + + //timeout = 0; + disconnect_from = (node_t *)nodes->head->data; + } else { + logger(mesh, MESHLINK_DEBUG, "* no node we want to disconnect, even though we have too many connections"); + } + + splay_delete_tree(nodes); } - if (nc + mesh->outgoings->count < min(autoconnect, mesh->nodes->count - 1)) - timeout = 0; + + // perform disconnect + + if(disconnect_from && disconnect_from->connection) { + logger(mesh, MESHLINK_DEBUG, "Autodisconnecting from %s", disconnect_from->connection->name); + list_delete(mesh->outgoings, disconnect_from->connection->outgoing); + disconnect_from->connection->outgoing = NULL; + terminate_connection(mesh, disconnect_from->connection, disconnect_from->connection->status.active); + } + + // reduce timeout if we don't have enough connections + outgoings + if(cur_connects + mesh->outgoings->count < 3) { + timeout = 1; + } + + // done! + + logger(mesh, MESHLINK_DEBUG, "--- autoconnect end ---"); } - timeout_set(&mesh->loop, data, &(struct timeval){timeout, rand() % 100000}); + for splay_each(node_t, n, mesh->nodes) { + if(n->status.dirty) { + if(!node_write_config(mesh, n)) { + logger(mesh, MESHLINK_DEBUG, "Could not update %s", n->name); + } + + n->status.dirty = false; + } + + if(n->status.validkey && n->last_req_key + 3600 < mesh->loop.now.tv_sec) { + logger(mesh, MESHLINK_DEBUG, "SPTPS key renewal for node %s", n->name); + + if(!sptps_force_kex(&n->sptps)) { + logger(mesh, MESHLINK_ERROR, "SPTPS key renewal for node %s failed", n->name); + n->status.validkey = false; + sptps_stop(&n->sptps); + n->status.waitingforkey = false; + n->last_req_key = 0; + } else { + n->last_req_key = mesh->loop.now.tv_sec; + } + } + } + + timeout_set(&mesh->loop, data, &(struct timespec) { + timeout, prng(mesh, TIMER_FUDGE) + }); } void handle_meta_connection_data(meshlink_handle_t *mesh, connection_t *c) { - if (!receive_meta(mesh, c)) { + if(!receive_meta(mesh, c)) { terminate_connection(mesh, c, c->status.active); return; } @@ -324,38 +651,86 @@ void retry(meshlink_handle_t *mesh) { /* Reset the reconnection timers for all outgoing connections */ for list_each(outgoing_t, outgoing, mesh->outgoings) { outgoing->timeout = 0; - if(outgoing->ev.cb) - timeout_set(&mesh->loop, &outgoing->ev, &(struct timeval){0, 0}); + + if(outgoing->ev.cb) { + timeout_set(&mesh->loop, &outgoing->ev, &(struct timespec) { + 0, 0 + }); + } } - /* Check for outgoing connections that are in progress, and reset their ping timers */ + /* For active connections, check if their addresses are still valid. + * If yes, reset their ping timers, otherwise terminate them. */ for list_each(connection_t, c, mesh->connections) { - if(c->outgoing && !c->node) + if(!c->status.active) { + continue; + } + + if(!c->status.pinged) { c->last_ping_time = 0; + } + + sockaddr_t sa; + socklen_t salen = sizeof(sa); + + if(getsockname(c->socket, &sa.sa, &salen)) { + continue; + } + + switch(sa.sa.sa_family) { + case AF_INET: + sa.in.sin_port = 0; + break; + + case AF_INET6: + sa.in6.sin6_port = 0; + break; + + default: + continue; + } + + int sock = socket(sa.sa.sa_family, SOCK_STREAM, IPPROTO_TCP); + + if(sock != -1) { + continue; + } + + if(bind(sock, &sa.sa, salen) && errno == EADDRNOTAVAIL) { + logger(mesh, MESHLINK_DEBUG, "Local address for connection to %s no longer valid, terminating", c->name); + terminate_connection(mesh, c, c->status.active); + } + + closesocket(sock); } /* Kick the ping timeout handler */ - timeout_set(&mesh->loop, &mesh->pingtimer, &(struct timeval){0, 0}); + timeout_set(&mesh->loop, &mesh->pingtimer, &(struct timespec) { + 0, 0 + }); } /* this is where it all happens... */ -int main_loop(meshlink_handle_t *mesh) { - timeout_add(&mesh->loop, &mesh->pingtimer, timeout_handler, &mesh->pingtimer, &(struct timeval){mesh->pingtimeout, rand() % 100000}); - timeout_add(&mesh->loop, &mesh->periodictimer, periodic_handler, &mesh->periodictimer, &(struct timeval){0, 0}); +void main_loop(meshlink_handle_t *mesh) { + timeout_add(&mesh->loop, &mesh->pingtimer, timeout_handler, &mesh->pingtimer, &(struct timespec) { + 1, prng(mesh, TIMER_FUDGE) + }); + timeout_add(&mesh->loop, &mesh->periodictimer, periodic_handler, &mesh->periodictimer, &(struct timespec) { + 0, 0 + }); //Add signal handler mesh->datafromapp.signum = 0; - signal_add(&(mesh->loop),&(mesh->datafromapp), (signal_cb_t)meshlink_send_from_queue,mesh, mesh->datafromapp.signum); + signal_add(&mesh->loop, &mesh->datafromapp, meshlink_send_from_queue, mesh, mesh->datafromapp.signum); - if(!event_loop_run(&mesh->loop)) { - logger(DEBUG_ALWAYS, LOG_ERR, "Error while waiting for input: %s", strerror(errno)); - return 1; + if(!event_loop_run(&mesh->loop, &mesh->mutex)) { + logger(mesh, MESHLINK_ERROR, "Error while waiting for input: %s", strerror(errno)); + call_error_cb(mesh, MESHLINK_ENETWORK); } + signal_del(&mesh->loop, &mesh->datafromapp); timeout_del(&mesh->loop, &mesh->periodictimer); timeout_del(&mesh->loop, &mesh->pingtimer); - - return 0; }