From b1cae6a2011f704dc4d3b02972def561d5c6bcb9 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Thu, 5 Sep 2019 17:53:12 +0200 Subject: [PATCH] Make ping intervals and timeouts configurable for each device class. --- src/meshlink++.h | 10 ++++++++++ src/meshlink.c | 35 +++++++++++++++++++++++++++-------- src/meshlink.h | 13 +++++++++++-- src/meshlink.sym | 1 + src/meshlink_internal.h | 22 +++++++++++----------- src/net.c | 26 +++++++++++++++++--------- src/net_packet.c | 6 +++--- src/net_setup.c | 3 --- src/protocol.c | 4 +++- src/protocol_auth.c | 2 +- src/protocol_key.c | 4 +++- 11 files changed, 87 insertions(+), 39 deletions(-) diff --git a/src/meshlink++.h b/src/meshlink++.h index 7877aeca..5550a9e3 100644 --- a/src/meshlink++.h +++ b/src/meshlink++.h @@ -871,6 +871,16 @@ public: meshlink_enable_discovery(handle, enable); } + /// Set device class timeouts + /** This sets the ping interval and timeout for a given device class. + * + * @param devclass The device class to update + * @param pinginterval The interval between keepalive packets, in seconds. The default is 60. + * @param pingtimeout The required time within which a peer should respond, in seconds. The default is 5. + * The timeout must be smaller than the interval. + */ + void set_dev_class_timeouts(dev_class_t devclass, int pinginterval, int pingtimeout); + private: // non-copyable: mesh(const mesh &) /* TODO: C++11: = delete */; diff --git a/src/meshlink.c b/src/meshlink.c index a7e94e3a..e65b7b19 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -1150,6 +1150,14 @@ void meshlink_open_params_free(meshlink_open_params_t *params) { free(params); } +/// Device class traits +static const dev_class_traits_t default_class_traits[DEV_CLASS_COUNT] = { + { .pingtimeout = 5, .pinginterval = 60, .min_connects = 3, .max_connects = 10000, .edge_weight = 1 }, // DEV_CLASS_BACKBONE + { .pingtimeout = 5, .pinginterval = 60, .min_connects = 3, .max_connects = 100, .edge_weight = 3 }, // DEV_CLASS_STATIONARY + { .pingtimeout = 5, .pinginterval = 60, .min_connects = 3, .max_connects = 3, .edge_weight = 6 }, // DEV_CLASS_PORTABLE + { .pingtimeout = 5, .pinginterval = 60, .min_connects = 1, .max_connects = 1, .edge_weight = 9 }, // DEV_CLASS_UNKNOWN +}; + meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const char *appname, dev_class_t devclass) { if(!confbase || !*confbase) { logger(NULL, MESHLINK_ERROR, "No confbase given!\n"); @@ -1262,6 +1270,8 @@ meshlink_handle_t *meshlink_open_ex(const meshlink_open_params_t *params) { mesh->netns = params->netns; mesh->submeshes = NULL; + memcpy(mesh->dev_class_traits, default_class_traits, sizeof(default_class_traits)); + if(usingname) { mesh->name = xstrdup(params->name); } @@ -3383,6 +3393,23 @@ end: #endif } +void meshlink_set_dev_class_timeouts(meshlink_handle_t *mesh, dev_class_t devclass, int pinginterval, int pingtimeout) { + if(!mesh || devclass < 0 || devclass >= DEV_CLASS_COUNT) { + meshlink_errno = EINVAL; + return; + } + + if(pinginterval < 1 || pingtimeout < 1 || pingtimeout > pinginterval) { + meshlink_errno = EINVAL; + return; + } + + pthread_mutex_lock(&mesh->mesh_mutex); + mesh->dev_class_traits[devclass].pinginterval = pinginterval; + mesh->dev_class_traits[devclass].pingtimeout = pingtimeout; + pthread_mutex_unlock(&mesh->mesh_mutex); +} + void handle_network_change(meshlink_handle_t *mesh, bool online) { (void)online; @@ -3403,11 +3430,3 @@ static void __attribute__((constructor)) meshlink_init(void) { static void __attribute__((destructor)) meshlink_exit(void) { crypto_exit(); } - -/// Device class traits -const dev_class_traits_t dev_class_traits[_DEV_CLASS_MAX + 1] = { - { .min_connects = 3, .max_connects = 10000, .edge_weight = 1 }, // DEV_CLASS_BACKBONE - { .min_connects = 3, .max_connects = 100, .edge_weight = 3 }, // DEV_CLASS_STATIONARY - { .min_connects = 3, .max_connects = 3, .edge_weight = 6 }, // DEV_CLASS_PORTABLE - { .min_connects = 1, .max_connects = 1, .edge_weight = 9 }, // DEV_CLASS_UNKNOWN -}; diff --git a/src/meshlink.h b/src/meshlink.h index 28f36273..40ed38f5 100644 --- a/src/meshlink.h +++ b/src/meshlink.h @@ -1279,7 +1279,6 @@ extern size_t meshlink_channel_get_recvq(meshlink_handle_t *mesh, meshlink_chann extern void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const struct sockaddr *addr); /// Enable or disable zeroconf discovery of local peers - /** This controls whether zeroconf discovery using the Catta library will be * enabled to search for peers on the local network. By default, it is enabled. * @@ -1289,7 +1288,6 @@ extern void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node extern void meshlink_enable_discovery(meshlink_handle_t *mesh, bool enable); /// Performs key rotation for an encrypted storage - /** This rotates the (master) key for an encrypted storage and discards the old key * if the call succeeded. This is an atomic call. * @@ -1301,6 +1299,17 @@ extern void meshlink_enable_discovery(meshlink_handle_t *mesh, bool enable); */ extern bool meshlink_encrypted_key_rotate(meshlink_handle_t *mesh, const void *new_key, size_t new_keylen); +/// Set device class timeouts +/** This sets the ping interval and timeout for a given device class. + * + * @param mesh A handle which represents an instance of MeshLink. + * @param devclass The device class to update + * @param pinginterval The interval between keepalive packets, in seconds. The default is 60. + * @param pingtimeout The required time within which a peer should respond, in seconds. The default is 5. + * The timeout must be smaller than the interval. + */ +extern void meshlink_set_dev_class_timeouts(meshlink_handle_t *mesh, dev_class_t devclass, int pinginterval, int pingtimeout); + #ifdef __cplusplus } #endif diff --git a/src/meshlink.sym b/src/meshlink.sym index f6bce730..784ff5c4 100644 --- a/src/meshlink.sym +++ b/src/meshlink.sym @@ -62,6 +62,7 @@ meshlink_set_channel_receive_cb meshlink_set_channel_sndbuf meshlink_set_connection_try_cb meshlink_set_default_blacklist +meshlink_set_dev_class_timeouts meshlink_set_invitation_timeout meshlink_set_log_cb meshlink_set_node_duplicate_cb diff --git a/src/meshlink_internal.h b/src/meshlink_internal.h index 3d6ac782..4e459b4e 100644 --- a/src/meshlink_internal.h +++ b/src/meshlink_internal.h @@ -76,6 +76,15 @@ struct meshlink_open_params { size_t keylen; }; +/// Device class traits +typedef struct { + int pinginterval; + int pingtimeout; + unsigned int min_connects; + unsigned int max_connects; + int edge_weight; +} dev_class_traits_t; + /// A handle for an instance of MeshLink. struct meshlink_handle { // public members @@ -141,11 +150,11 @@ struct meshlink_handle { dev_class_t devclass; int invitation_timeout; - int pinginterval; /* seconds between pings */ - int pingtimeout; /* seconds to wait for response */ int maxtimeout; int udp_choice; + dev_class_traits_t dev_class_traits[DEV_CLASS_COUNT]; + int netns; bool default_blacklist; @@ -245,13 +254,4 @@ extern int check_port(meshlink_handle_t *mesh); extern void handle_duplicate_node(meshlink_handle_t *mesh, struct node_t *n); extern void handle_network_change(meshlink_handle_t *mesh, bool online); -/// Device class traits -typedef struct { - unsigned int min_connects; - unsigned int max_connects; - int edge_weight; -} dev_class_traits_t; - -extern const dev_class_traits_t dev_class_traits[]; - #endif diff --git a/src/net.c b/src/net.c index 7ced1d15..b5705a47 100644 --- a/src/net.c +++ b/src/net.c @@ -39,6 +39,8 @@ static inline int min(int a, int b) { } #endif +static const int default_timeout = 5; + /* Terminate a connection: - Mark it as inactive @@ -110,18 +112,20 @@ static void timeout_handler(event_loop_t *loop, void *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; + // 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) { 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 + mesh->dev_class_traits[c->node->devclass].pinginterval <= mesh->loop.now.tv_sec) { send_ping(mesh, c); continue; } else { @@ -140,7 +144,7 @@ static void timeout_handler(event_loop_t *loop, void *data) { } timeout_set(&mesh->loop, data, &(struct timeval) { - mesh->pingtimeout, rand() % 100000 + default_timeout, rand() % 100000 }); } @@ -351,7 +355,7 @@ static void periodic_handler(event_loop_t *loop, void *data) { 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. */ @@ -359,7 +363,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); @@ -387,8 +391,8 @@ static void periodic_handler(event_loop_t *loop, void *data) { // get min_connects and max_connects - unsigned int min_connects = dev_class_traits[mesh->devclass].min_connects; - unsigned 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); @@ -690,7 +694,7 @@ void retry(meshlink_handle_t *mesh) { */ int main_loop(meshlink_handle_t *mesh) { timeout_add(&mesh->loop, &mesh->pingtimer, timeout_handler, &mesh->pingtimer, &(struct timeval) { - mesh->pingtimeout, rand() % 100000 + default_timeout, rand() % 100000 }); timeout_add(&mesh->loop, &mesh->periodictimer, periodic_handler, &mesh->periodictimer, &(struct timeval) { 0, 0 @@ -702,6 +706,10 @@ int main_loop(meshlink_handle_t *mesh) { if(!event_loop_run(&(mesh->loop), &(mesh->mesh_mutex))) { logger(mesh, MESHLINK_ERROR, "Error while waiting for input: %s", strerror(errno)); + abort(); + timeout_del(&mesh->loop, &mesh->periodictimer); + timeout_del(&mesh->loop, &mesh->pingtimer); + return 1; } diff --git a/src/net_packet.c b/src/net_packet.c index d1cbf47d..0bef5343 100644 --- a/src/net_packet.c +++ b/src/net_packet.c @@ -70,7 +70,7 @@ static void send_mtu_probe_handler(event_loop_t *loop, void *data) { if(n->mtuprobes > 32) { if(!n->minmtu) { n->mtuprobes = 31; - timeout = mesh->pinginterval; + timeout = mesh->dev_class_traits[n->devclass].pinginterval; goto end; } @@ -99,10 +99,10 @@ static void send_mtu_probe_handler(event_loop_t *loop, void *data) { } if(n->mtuprobes == 31) { - timeout = mesh->pinginterval; + timeout = mesh->dev_class_traits[n->devclass].pinginterval; goto end; } else if(n->mtuprobes == 32) { - timeout = mesh->pingtimeout; + timeout = mesh->dev_class_traits[n->devclass].pingtimeout; } for(int i = 0; i < 5; i++) { diff --git a/src/net_setup.c b/src/net_setup.c index 0045cd6f..fc6b3c97 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -453,9 +453,6 @@ bool setup_network(meshlink_handle_t *mesh) { init_edges(mesh); init_requests(mesh); - mesh->pinginterval = 60; - mesh->pingtimeout = 5; - if(!setup_myself(mesh)) { return false; } diff --git a/src/protocol.c b/src/protocol.c index 95f6aa39..fd180f4f 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -181,13 +181,15 @@ static void free_past_request(past_request_t *r) { free(r); } +static const int request_timeout = 60; + static void age_past_requests(event_loop_t *loop, void *data) { (void)data; meshlink_handle_t *mesh = loop->data; int left = 0, deleted = 0; for splay_each(past_request_t, p, mesh->past_request_tree) { - if(p->firstseen + mesh->pinginterval <= mesh->loop.now.tv_sec) { + if(p->firstseen + request_timeout <= mesh->loop.now.tv_sec) { splay_delete_node(mesh->past_request_tree, node), deleted++; } else { left++; diff --git a/src/protocol_auth.c b/src/protocol_auth.c index a6144c5f..1193b43c 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -456,7 +456,7 @@ bool ack_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { c->edge->from = mesh->self; c->edge->to = n; sockaddrcpy_setport(&c->edge->address, &c->address, atoi(hisport)); - c->edge->weight = dev_class_traits[devclass].edge_weight; + c->edge->weight = mesh->dev_class_traits[devclass].edge_weight; c->edge->connection = c; edge_add(mesh, c->edge); diff --git a/src/protocol_key.c b/src/protocol_key.c index 2fbbe126..2a4dbe02 100644 --- a/src/protocol_key.c +++ b/src/protocol_key.c @@ -31,6 +31,8 @@ #include "utils.h" #include "xalloc.h" +static const int req_key_timeout = 2; + void send_key_changed(meshlink_handle_t *mesh) { send_request(mesh, mesh->everyone, NULL, "%d %x %s", KEY_CHANGED, rand(), mesh->self->name); @@ -156,7 +158,7 @@ static bool req_key_ext_h(meshlink_handle_t *mesh, connection_t *c, const char * if(from->sptps.label) { logger(mesh, MESHLINK_DEBUG, "Got REQ_KEY from %s while we already started a SPTPS session!", from->name); - if(mesh->loop.now.tv_sec < from->last_req_key + mesh->pingtimeout / 2 && strcmp(mesh->self->name, from->name) < 0) { + if(mesh->loop.now.tv_sec < from->last_req_key + req_key_timeout && strcmp(mesh->self->name, from->name) < 0) { logger(mesh, MESHLINK_DEBUG, "Ignoring REQ_KEY from %s.", from->name); return true; } -- 2.39.2