X-Git-Url: http://git.meshlink.io/?p=meshlink;a=blobdiff_plain;f=src%2Fnet.c;h=7c600534e3a682488200bbfa9f4cf320e7fe11f3;hp=8e5c2c1830ee3b489d73314606042734b978f4c5;hb=be83b0af60449c7b35d17d97f2e6dc12f611e831;hpb=ea294dac41365a08a77b64c62c0ba630292d7a07 diff --git a/src/net.c b/src/net.c index 8e5c2c18..7c600534 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 @@ -34,11 +34,14 @@ #include #if !defined(min) -static const int min(int a, int b) { +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: - Mark it as inactive @@ -51,12 +54,14 @@ void terminate_connection(meshlink_handle_t *mesh, connection_t *c, bool report) 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; @@ -67,11 +72,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); } } @@ -82,13 +88,15 @@ 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 } @@ -101,36 +109,50 @@ 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) { + 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 + mesh->pingtimeout <= mesh->loop.now.tv_sec) + if(c->node->status.waitingforkey && c->node->last_req_key + pingtimeout <= mesh->loop.now.tv_sec) { send_req_key(mesh, c->node); + } } - if(c->last_ping_time + mesh->pingtimeout <= mesh->loop.now.tv_sec) { + + if(c->last_ping_time + pingtimeout <= mesh->loop.now.tv_sec) { if(c->status.active) { - if(c->status.pinged) + if(c->status.pinged) { 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 + mesh->pinginterval <= mesh->loop.now.tv_sec) { + } else if(c->last_ping_time + pinginterval <= mesh->loop.now.tv_sec) { send_ping(mesh, c); continue; - } else + } else { continue; + } } else { - if(c->status.connecting) + if(c->status.connecting) { logger(mesh, MESHLINK_WARNING, "Timeout while connecting to %s", c->name); - else + } 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 + 1, prng(mesh, TIMER_FUDGE) }); } @@ -138,26 +160,33 @@ static void timeout_handler(event_loop_t *loop, void *data) { static int node_compare_devclass_asc_lsc_desc(const void *a, const void *b) { const node_t *na = a, *nb = b; - if(na->devclass < nb->devclass) + if(na->devclass < nb->devclass) { return -1; + } - if(na->devclass > nb->devclass) + if(na->devclass > nb->devclass) { return 1; + } - if(na->last_successfull_connection == nb->last_successfull_connection) + 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) + 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) + if(nb->last_successfull_connection == 0 || na->last_successfull_connection < nb->last_successfull_connection) { return 1; + } - if(na < nb) + if(na < nb) { return -1; + } - if(na > nb) + if(na > nb) { return 1; + } return 0; } @@ -166,20 +195,25 @@ static int node_compare_devclass_asc_lsc_desc(const void *a, const void *b) { 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) + 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) + 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) + if(nb->last_successfull_connection == 0 || na->last_successfull_connection < nb->last_successfull_connection) { return 1; + } - if(na < nb) + if(na < nb) { return -1; + } - if(na > nb) + if(na > nb) { return 1; + } return 0; } @@ -188,17 +222,21 @@ static int node_compare_lsc_desc(const void *a, const void *b) { static int node_compare_devclass_desc(const void *a, const void *b) { const node_t *na = a, *nb = b; - if(na->devclass < nb->devclass) + if(na->devclass < nb->devclass) { return -1; + } - if(na->devclass > nb->devclass) + if(na->devclass > nb->devclass) { return 1; + } - if(na < nb) + if(na < nb) { return -1; + } - if(na > nb) + if(na > nb) { return 1; + } return 0; } @@ -310,18 +348,22 @@ static void periodic_handler(event_loop_t *loop, void *data) { logger(mesh, MESHLINK_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", mesh->sleeptime); usleep(mesh->sleeptime * 1000000LL); 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. */ @@ -329,7 +371,7 @@ static void periodic_handler(event_loop_t *loop, void *data) { logger(mesh, MESHLINK_DEBUG, "--- autoconnect begin ---"); - int retry_timeout = min(mesh->nodes->count * 5, 60); + int retry_timeout = min(mesh->nodes->count * default_timeout, 60); logger(mesh, MESHLINK_DEBUG, "* devclass = %d", mesh->devclass); logger(mesh, MESHLINK_DEBUG, "* nodes = %d", mesh->nodes->count); @@ -344,11 +386,12 @@ static void periodic_handler(event_loop_t *loop, void *data) { // get cur_connects - int cur_connects = 0; + unsigned int cur_connects = 0; for list_each(connection_t, c, mesh->connections) { - if(c->status.active) + if(c->status.active) { cur_connects += 1; + } } logger(mesh, MESHLINK_DEBUG, "* cur_connects = %d", cur_connects); @@ -356,35 +399,35 @@ static void periodic_handler(event_loop_t *loop, void *data) { // get min_connects and max_connects - assert(mesh->devclass >= 0 && mesh->devclass <= _DEV_CLASS_MAX); - - int min_connects = dev_class_traits[mesh->devclass].min_connects; - int max_connects = dev_class_traits[mesh->devclass].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, "* n->devclass = %d", n->devclass); - if(n != mesh->self && n->devclass <= mesh->devclass && !n->connection && (n->last_connect_try == 0 || (time(NULL) - n->last_connect_try) > retry_timeout)) + 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) { - logger(mesh, MESHLINK_DEBUG, "* found best one for initial connect"); - //timeout = 0; connect_to = (node_t *)nodes->head->data; - } else + + 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_free_tree(nodes); + splay_delete_tree(nodes); } @@ -393,35 +436,39 @@ static void periodic_handler(event_loop_t *loop, void *data) { if(!connect_to && min_connects <= cur_connects && cur_connects < max_connects) { unsigned int connects = 0; - for(int devclass = 0; devclass <= mesh->devclass; ++devclass) { + 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) + 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->last_connect_try == 0 || (time(NULL) - n->last_connect_try) > retry_timeout)) + 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_free_tree(nodes); + splay_delete_tree(nodes); break; } - splay_free_tree(nodes); - } else + splay_delete_tree(nodes); + } else { break; + } } - if(!connect_to) + if(!connect_to) { logger(mesh, MESHLINK_DEBUG, "* could not find better nodes"); + } } @@ -431,43 +478,51 @@ static void periodic_handler(event_loop_t *loop, void *data) { 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->last_connect_try == 0 || (time(NULL) - n->last_connect_try) > retry_timeout)) + 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 + } else { logger(mesh, MESHLINK_DEBUG, "* could not find nodes for partition healing"); + } - splay_free_tree(nodes); + splay_delete_tree(nodes); } // perform connect if(connect_to && !connect_to->connection) { - connect_to->last_connect_try = time(NULL); + 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 found = false; + bool skip = false; + for list_each(outgoing_t, outgoing, mesh->outgoings) { - if(!strcmp(outgoing->name, connect_to->name)) { - found = true; + if(outgoing->node == connect_to) { + logger(mesh, MESHLINK_DEBUG, "* skip autoconnect since it is an outgoing connection already"); + skip = true; break; } } - if(!found) { + 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; + } + + if(!skip) { logger(mesh, MESHLINK_DEBUG, "Autoconnecting to %s", connect_to->name); outgoing_t *outgoing = xzalloc(sizeof(outgoing_t)); - outgoing->mesh = mesh; - outgoing->name = xstrdup(connect_to->name); + outgoing->node = connect_to; list_insert_tail(mesh->outgoings, outgoing); setup_outgoing_connection(mesh, outgoing); - } else - logger(mesh, MESHLINK_DEBUG, "* skip autoconnect since it is an outgoing connection already"); + } } @@ -476,18 +531,20 @@ static void periodic_handler(event_loop_t *loop, void *data) { if(min_connects < cur_connects /*&& cur_connects <= max_connects*/) { unsigned int connects = 0; - for(int devclass = 0; devclass <= mesh->devclass; ++devclass) { + 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) + if(c->status.active && c->node && c->node->devclass == devclass) { connects += 1; + } } 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) + if(c->outgoing && c->node && c->node->devclass >= devclass) { splay_insert(nodes, c->node); + } } if(nodes->head) { @@ -495,13 +552,14 @@ static void periodic_handler(event_loop_t *loop, void *data) { disconnect_from = (node_t *)nodes->head->data; } - splay_free_tree(nodes); + splay_delete_tree(nodes); break; } } - if(!disconnect_from) + if(!disconnect_from) { logger(mesh, MESHLINK_DEBUG, "* no suboptimal outgoing connections"); + } } @@ -511,8 +569,9 @@ static void periodic_handler(event_loop_t *loop, void *data) { 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) + if(c->status.active && c->node) { splay_insert(nodes, c->node); + } } if(nodes->head) { @@ -520,10 +579,11 @@ static void periodic_handler(event_loop_t *loop, void *data) { //timeout = 0; disconnect_from = (node_t *)nodes->head->data; - } else + } else { logger(mesh, MESHLINK_DEBUG, "* no node we want to disconnect, even though we have too many connections"); + } - splay_free_tree(nodes); + splay_delete_tree(nodes); } @@ -536,14 +596,28 @@ static void periodic_handler(event_loop_t *loop, void *data) { 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 ---"); } + 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; + } + } + timeout_set(&mesh->loop, data, &(struct timeval) { - timeout, rand() % 100000 + timeout, prng(mesh, TIMER_FUDGE) }); } @@ -558,18 +632,68 @@ 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 }); } - /* Check for outgoing connections that are in progress, and reset their ping timers */ +#ifdef HAVE_IFADDRS_H + struct ifaddrs *ifa = NULL; + getifaddrs(&ifa); +#endif + + /* 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; + } + +#ifdef HAVE_IFADDRS_H + + if(!ifa) { + continue; + } + + sockaddr_t sa; + socklen_t salen = sizeof(sa); + + if(getsockname(c->socket, &sa.sa, &salen)) { + continue; + } + + bool found = false; + + for(struct ifaddrs *ifap = ifa; ifap; ifap = ifap->ifa_next) { + if(ifap->ifa_addr && !sockaddrcmp_noport(&sa, (sockaddr_t *)ifap->ifa_addr)) { + found = true; + break; + } + + } + + if(!found) { + logger(mesh, MESHLINK_DEBUG, "Local address for connection to %s no longer valid, terminating", c->name); + terminate_connection(mesh, c, c->status.active); + } + +#endif + } + +#ifdef HAVE_IFADDRS_H + + if(ifa) { + freeifaddrs(ifa); } +#endif + /* Kick the ping timeout handler */ timeout_set(&mesh->loop, &mesh->pingtimer, &(struct timeval) { 0, 0 @@ -579,9 +703,9 @@ void retry(meshlink_handle_t *mesh) { /* this is where it all happens... */ -int main_loop(meshlink_handle_t *mesh) { +void main_loop(meshlink_handle_t *mesh) { timeout_add(&mesh->loop, &mesh->pingtimer, timeout_handler, &mesh->pingtimer, &(struct timeval) { - mesh->pingtimeout, rand() % 100000 + 1, prng(mesh, TIMER_FUDGE) }); timeout_add(&mesh->loop, &mesh->periodictimer, periodic_handler, &mesh->periodictimer, &(struct timeval) { 0, 0 @@ -589,15 +713,14 @@ int main_loop(meshlink_handle_t *mesh) { //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), &(mesh->mesh_mutex))) { + if(!event_loop_run(&mesh->loop, &mesh->mutex)) { logger(mesh, MESHLINK_ERROR, "Error while waiting for input: %s", strerror(errno)); - return 1; + 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; }