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 */;
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");
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);
}
#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;
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
-};
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.
*
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.
*
*/
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
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
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
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;
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
}
#endif
+static const int default_timeout = 5;
+
/*
Terminate a connection:
- Mark it as inactive
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 {
}
timeout_set(&mesh->loop, data, &(struct timeval) {
- mesh->pingtimeout, rand() % 100000
+ default_timeout, rand() % 100000
});
}
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. */
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);
// 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);
*/
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
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;
}
if(n->mtuprobes > 32) {
if(!n->minmtu) {
n->mtuprobes = 31;
- timeout = mesh->pinginterval;
+ timeout = mesh->dev_class_traits[n->devclass].pinginterval;
goto end;
}
}
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++) {
init_edges(mesh);
init_requests(mesh);
- mesh->pinginterval = 60;
- mesh->pingtimeout = 5;
-
if(!setup_myself(mesh)) {
return false;
}
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++;
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);
#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);
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;
}