]> git.meshlink.io Git - meshlink/commitdiff
Make ping intervals and timeouts configurable for each device class.
authorGuus Sliepen <guus@meshlink.io>
Thu, 5 Sep 2019 15:53:12 +0000 (17:53 +0200)
committerGuus Sliepen <guus@meshlink.io>
Thu, 5 Sep 2019 18:00:11 +0000 (20:00 +0200)
src/meshlink++.h
src/meshlink.c
src/meshlink.h
src/meshlink.sym
src/meshlink_internal.h
src/net.c
src/net_packet.c
src/net_setup.c
src/protocol.c
src/protocol_auth.c
src/protocol_key.c

index 7877aeca22becc6d0e32c67abf9e10b67cc99460..5550a9e302339c02a2ce5a66862072384d86ee62 100644 (file)
@@ -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 */;
index a7e94e3a599db2cbb7772f2adffc1f93f0cf97fa..e65b7b199c76205d82c9fb76e3ea55ada5c08322 100644 (file)
@@ -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
-};
index 28f36273b0bd3e95b21c39fcf691e04761b0fab2..40ed38f50d7f1eece92883e4231353b0b6f68050 100644 (file)
@@ -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
index f6bce730018f2e6183c33731216e9c8684706fff..784ff5c49940678d7fbf563501e7f4e40d045a63 100644 (file)
@@ -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
index 3d6ac782735d4fd9254dbc78051ed5fe5616440a..4e459b4e847bd5861047ebccca89caeeca8bc33a 100644 (file)
@@ -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
index 7ced1d15ffe688b936c89eecc1db5415af7525f5..b5705a471b198dde06bed1792d0208a835490a1d 100644 (file)
--- 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;
        }
 
index d1cbf47d91397ed08f0e87a61131aefac8a99981..0bef53433030d0606e49151ca6de2a8d34d0a67c 100644 (file)
@@ -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++) {
index 0045cd6ffdf12d35d3f3797d5699149d117fbe8f..fc6b3c97298551f3f7c1b02e23a601b20e67061c 100644 (file)
@@ -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;
        }
index 95f6aa39f49eda059094a615753ce417c33a43ed..fd180f4faafab84fd4c3bfedf0bf465dbf96b13c 100644 (file)
@@ -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++;
index a6144c5fa043319655d937f58324edb458d388b7..1193b43ce6c3b09df65a995db5a783f55f752f31 100644 (file)
@@ -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);
index 2fbbe126e7d22dbc43396889366d1d9fcf1b1239..2a4dbe02d1d5c4c24252a9dddd825872ed983a04 100644 (file)
@@ -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;
                        }