From: Niklas Hofmann Date: Wed, 13 Aug 2014 11:30:16 +0000 (+0200) Subject: Merge branch 'mesh_topology_output' into roles X-Git-Url: http://git.meshlink.io/?p=meshlink;a=commitdiff_plain;h=e4e5a81447142da0fb1291b2d2119ed6981b89e5;hp=b9eb3647186bf697e4bdc8f60fd05cc15a99c18b Merge branch 'mesh_topology_output' into roles --- diff --git a/examples/chat.c b/examples/chat.c index 3eaa5c7e..c6d67fb5 100644 --- a/examples/chat.c +++ b/examples/chat.c @@ -195,7 +195,7 @@ int main(int argc, char *argv[]) { meshlink_set_log_cb(NULL, MESHLINK_INFO, log_message); - meshlink_handle_t *mesh = meshlink_open(confbase, nick, "chat", STATIONARY); + meshlink_handle_t *mesh = meshlink_open(confbase, nick, "chat", DEV_CLASS_STATIONARY); if(!mesh) { fprintf(stderr, "Could not open MeshLink: %s\n", meshlink_strerror(meshlink_errno)); return 1; diff --git a/examples/chatpp.cc b/examples/chatpp.cc index e500ad27..331f6584 100644 --- a/examples/chatpp.cc +++ b/examples/chatpp.cc @@ -187,7 +187,7 @@ int main(int argc, char *argv[]) { if(argc > 2) nick = argv[2]; - ChatMesh* mesh = meshlink::open(confbase, nick, "chatpp", STATIONARY); + ChatMesh* mesh = meshlink::open(confbase, nick, "chatpp", DEV_CLASS_STATIONARY); if(!mesh) { fprintf(stderr, "Could not open MeshLink: %s\n", meshlink::strerror()); diff --git a/examples/manynodes.c b/examples/manynodes.c index 96aa5f0f..22ebad21 100644 --- a/examples/manynodes.c +++ b/examples/manynodes.c @@ -241,7 +241,7 @@ int main(int argc, char *argv[]) { snprintf(nodename, sizeof nodename, "%snode%d", namesprefix,i); snprintf(filename, sizeof filename, "%s/%s", basebase, nodename); bool itsnew = access(filename, R_OK); - mesh[i] = meshlink_open(filename, nodename, "manynodes", STATIONARY); + mesh[i] = meshlink_open(filename, nodename, "manynodes", DEV_CLASS_PORTABLE); meshlink_set_log_cb(mesh[i], MESHLINK_INFO, log_message); if(itsnew) meshlink_add_address(mesh[i], "localhost"); diff --git a/examples/meshlinkapp.c b/examples/meshlinkapp.c index 6c56165c..d9e5b7e0 100644 --- a/examples/meshlinkapp.c +++ b/examples/meshlinkapp.c @@ -14,7 +14,7 @@ int main(int argc , char **argv){ meshlink_handle_t* myhandle; - myhandle = meshlink_open(confbase, name, "meshlinkapp", STATIONARY); + myhandle = meshlink_open(confbase, name, "meshlinkapp", DEV_CLASS_STATIONARY); //Register callback function for incoming data meshlink_set_receive_cb(myhandle, (meshlink_receive_cb_t)handle_recv_data); diff --git a/src/discovery.c b/src/discovery.c index b50aea8d..d17f962b 100644 --- a/src/discovery.c +++ b/src/discovery.c @@ -163,7 +163,6 @@ static void discovery_server_callback(AvahiServer *server, AvahiServerState stat { logger(mesh, MESHLINK_ERROR, "Avahi failed to set new host name: %s\n", avahi_strerror(result)); avahi_simple_poll_quit(mesh->avahi_poll); - return; } } break; @@ -347,7 +346,7 @@ static void discovery_browse_callback(AvahiSServiceBrowser *browser, AvahiIfInde logger(mesh, MESHLINK_ERROR, "(Browser) %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server))); avahi_simple_poll_quit(mesh->avahi_poll); } - return; + break; case AVAHI_BROWSER_NEW: { diff --git a/src/meshlink++.h b/src/meshlink++.h index 8271ab2f..ef578b11 100644 --- a/src/meshlink++.h +++ b/src/meshlink++.h @@ -88,7 +88,7 @@ namespace meshlink { /// A class describing a MeshLink mesh. class mesh: public meshlink_handle_t { public: - mesh() {} + mesh() : meshlink_handle_t() {} virtual ~mesh() { meshlink_close(this); @@ -387,8 +387,8 @@ namespace meshlink { * @return This function will return a pointer to a meshlink::mesh if MeshLink has succesfully set up its configuration files, NULL otherwise. */ template - static MESH* open(const char *confbase, const char *name, const char* appname, dclass_t dclass) { - void* mp = (void *)meshlink_open_with_size(confbase, name, appname, dclass, sizeof(MESH)); + static MESH* open(const char *confbase, const char *name, const char* appname, dev_class_t devclass) { + void* mp = (void *)meshlink_open_with_size(confbase, name, appname, devclass, sizeof(MESH)); return new (mp) MESH; } diff --git a/src/meshlink.c b/src/meshlink.c index e84aafca..1d52724e 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -742,11 +742,11 @@ static bool meshlink_setup(meshlink_handle_t *mesh) { return true; } -meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const char* appname, dclass_t dclass) { - return meshlink_open_with_size(confbase, name, appname, dclass, sizeof(meshlink_handle_t)); +meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const char* appname, dev_class_t devclass) { + return meshlink_open_with_size(confbase, name, appname, devclass, sizeof(meshlink_handle_t)); } -meshlink_handle_t *meshlink_open_with_size(const char *confbase, const char *name, const char* appname, dclass_t dclass, size_t size) { +meshlink_handle_t *meshlink_open_with_size(const char *confbase, const char *name, const char* appname, dev_class_t devclass, size_t size) { // Validate arguments provided by the application bool usingname = false; @@ -778,10 +778,16 @@ meshlink_handle_t *meshlink_open_with_size(const char *confbase, const char *nam } else { usingname = true;} } + if(devclass < 0 || devclass > _DEV_CLASS_MAX) { + logger(NULL, MESHLINK_ERROR, "Invalid devclass given!\n"); + meshlink_errno = MESHLINK_EINVAL; + return NULL; + } + meshlink_handle_t *mesh = xzalloc(size); mesh->confbase = xstrdup(confbase); mesh->appname = xstrdup(appname); - mesh->dclass = dclass; + mesh->devclass = devclass; if (usingname) mesh->name = xstrdup(name); // initialize mutex @@ -1910,19 +1916,11 @@ static void __attribute__((destructor)) meshlink_exit(void) { crypto_exit(); } -int weight_from_dclass(dclass_t dclass) -{ - switch(dclass) - { - case BACKBONE: - return 1; - - case STATIONARY: - return 3; - - case PORTABLE: - return 6; - } - return 9; -} +/// Device class traits +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 6f104abe..7cf1f444 100644 --- a/src/meshlink.h +++ b/src/meshlink.h @@ -68,12 +68,14 @@ typedef enum { MESHLINK_EPEER, ///< A peer caused an error } meshlink_errno_t; -// Device class +/// Device class typedef enum { - BACKBONE = 1, - STATIONARY = 2, - PORTABLE = 3 -} dclass_t; + DEV_CLASS_BACKBONE = 0, + DEV_CLASS_STATIONARY = 1, + DEV_CLASS_PORTABLE = 2, + DEV_CLASS_UNKNOWN = 3, + _DEV_CLASS_MAX = 3 +} dev_class_t; /// A variable holding the last encountered error from MeshLink. /** This is a thread local variable that contains the error code of the most recent error @@ -86,12 +88,14 @@ extern __thread meshlink_errno_t meshlink_errno; #ifndef MESHLINK_INTERNAL_H struct meshlink_handle { - const char *name; + char *name; + char *appname; + dev_class_t devclass; void *priv; }; struct meshlink_node { - const char *name; ///< Textual name of this node. It is stored in a nul-terminated C string, which is allocated by MeshLink. + char *name; ///< Textual name of this node. It is stored in a nul-terminated C string, which is allocated by MeshLink. void *priv; ///< Private pointer which may be set freely by the application, and is never used or modified by MeshLink. }; @@ -149,15 +153,15 @@ extern const char *meshlink_strerror(meshlink_errno_t err); * After the function returns, the application is free to overwrite or free @a name @a. * @param appname The application name which will be used in the mesh. * After the function returns, the application is free to overwrite or free @a name @a. - * @param dclass The device class which will be used in the mesh. + * @param devclass The device class which will be used in the mesh. * * @return A pointer to a meshlink_handle_t which represents this instance of MeshLink, or NULL in case of an error. * The pointer is valid until meshlink_close() is called. */ -extern meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const char* appname, dclass_t dclass); +extern meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const char* appname, dev_class_t devclass); /// is used by the C++ wrapper to allocate more memory behind the handle -extern meshlink_handle_t *meshlink_open_with_size(const char *confbase, const char *name, const char* appname, dclass_t dclass, size_t size); +extern meshlink_handle_t *meshlink_open_with_size(const char *confbase, const char *name, const char* appname, dev_class_t devclass, size_t size); /// Start MeshLink. /** This function causes MeshLink to open network sockets, make outgoing connections, and diff --git a/src/meshlink_internal.h b/src/meshlink_internal.h index 42e1da53..994f25bf 100644 --- a/src/meshlink_internal.h +++ b/src/meshlink_internal.h @@ -64,7 +64,7 @@ typedef struct outpacketqueue { struct meshlink_handle { char *name; char *appname; - dclass_t dclass; + dev_class_t devclass; void *priv; char *confbase; @@ -158,6 +158,13 @@ extern void meshlink_send_from_queue(event_loop_t* el,meshlink_handle_t *mesh); extern meshlink_log_level_t global_log_level; extern meshlink_log_cb_t global_log_cb; -extern int weight_from_dclass(dclass_t dclass); +/// Device class traits +typedef struct { + unsigned int min_connects; + unsigned int max_connects; + int edge_weight; +} dev_class_traits_t; + +extern dev_class_traits_t dev_class_traits[]; #endif // MESHLINK_INTERNAL_H diff --git a/src/net.c b/src/net.c index c615b893..80e866a0 100644 --- a/src/net.c +++ b/src/net.c @@ -31,6 +31,8 @@ #include "protocol.h" #include "xalloc.h" +#include + static const int min(int a, int b) { return a < b ? a : b; } @@ -129,75 +131,157 @@ static void timeout_handler(event_loop_t *loop, void *data) { timeout_set(&mesh->loop, data, &(struct timeval){mesh->pingtimeout, rand() % 100000}); } -/// 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; - - for splay_each(node_t, n, mesh->nodes) { - /* skip nodes that do not pass condition check */ - if(!(*cond_check)(&i, &r, n)) - continue; - - /* 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; - } - } +// devclass asc, last_connect_try desc +static int node_compare_devclass_asc_last_connect_try_desc(const void *a, const void *b) +{ + const node_t *na = a, *nb = b; - 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(mesh, MESHLINK_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->devclass < nb->devclass) + { return -1; } + + if(na->devclass > nb->devclass) + { return 1; } + + if(na->last_connect_try == nb->last_connect_try) + return 0; + + if(nb->last_connect_try == 0 || na->last_connect_try < nb->last_connect_try) + return -1; + + if(na->last_connect_try == 0 || na->last_connect_try > nb->last_connect_try) + return 1; + + return 0; } -static bool found_random_node(int *i, int *r, node_t *n) { - if((*i)++ != *r) - return false; +// last_connect_try desc +static int node_compare_last_connect_try_desc(const void *a, const void *b) +{ + const node_t *na = a, *nb = b; + + if(na->last_connect_try == nb->last_connect_try) + return 0; + + if(nb->last_connect_try == 0 || na->last_connect_try < nb->last_connect_try) + return -1; + + if(na->last_connect_try == 0 || na->last_connect_try > nb->last_connect_try) + return 1; - if(n->connection) - return false; - - return true; + 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(na->devclass < nb->devclass) + { return -1; } - if(n->connection) - return false; + if(na->devclass > nb->devclass) + { return 1; } - return true; + 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; @@ -223,97 +307,234 @@ static void periodic_handler(event_loop_t *loop, void *data) { int timeout = 5; - /* If AutoConnect is set, check if we need to make or break connections. */ + /* Check if we need to make or break connections. */ + + if(mesh->nodes->count > 1) { + + logger(mesh, MESHLINK_INFO, "--- autoconnect begin ---"); + + + int retry_timeout = min(mesh->nodes->count * 5, 60); + + // connect disconnect nodes + + node_t* connect_to = NULL; + node_t* disconnect_from = NULL; + + + // get cur_connects + + int cur_connects = 0; + + for list_each(connection_t, c, mesh->connections) + { + if(!c->status.remove_unused) + { + cur_connects += 1; + } + } + + logger(mesh, MESHLINK_INFO, "* cur_connects = %d", cur_connects); + - 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++; + // 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; + + logger(mesh, MESHLINK_INFO, "* min_connects = %d", min_connects); + logger(mesh, MESHLINK_INFO, "* 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_last_connect_try_desc, NULL); + + for splay_each(node_t, n, mesh->nodes) + { + if(n->devclass <= mesh->devclass && !n->connection && (n->last_connect_try == 0 || (time(NULL) - n->last_connect_try) > retry_timeout)) + { splay_insert(nodes, n); } + } + + if(nodes->head) + { + logger(mesh, MESHLINK_INFO, "* found best one for initial connect"); + + //timeout = 0; + connect_to = (node_t*)nodes->head->data; + } + + splay_free_tree(nodes); } - /* Count number of unreachable nodes */ - int num_unreachable = 0; - for splay_each(node_t, n, mesh->nodes) { - if(!n->status.reachable) - num_unreachable++; + + // find better nodes to connect to + + if(!connect_to && min_connects <= cur_connects && cur_connects < max_connects) + { + unsigned int connects = 0; + + for(int devclass = 0; devclass <= mesh->devclass; ++devclass) + { + for list_each(connection_t, c, mesh->connections) + { + if(!c->status.remove_unused && c->node && c->node->devclass == devclass) + { connects += 1; } + } + + if( connects < min_connects ) + { + splay_tree_t *nodes = splay_alloc_tree(node_compare_last_connect_try_desc, NULL); + + for splay_each(node_t, n, mesh->nodes) + { + if(n->devclass == devclass && !n->connection && (n->last_connect_try == 0 || (time(NULL) - n->last_connect_try) > retry_timeout)) + { splay_insert(nodes, n); } + } + + if(nodes->head) + { + logger(mesh, MESHLINK_INFO, "* found better node"); + connect_to = (node_t*)nodes->head->data; + + splay_free_tree(nodes); + break; + } + + splay_free_tree(nodes); + } + else + { break; } + } } - 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); + + // heal partitions + + if(!connect_to && min_connects <= cur_connects && cur_connects < max_connects) + { + splay_tree_t *nodes = splay_alloc_tree(node_compare_devclass_asc_last_connect_try_desc, NULL); + + for splay_each(node_t, n, mesh->nodes) + { + if(n->devclass <= mesh->devclass && !n->status.reachable && (n->last_connect_try == 0 || (time(NULL) - n->last_connect_try) > retry_timeout)) + { splay_insert(nodes, n); } + } + + if(nodes->head) + { + logger(mesh, MESHLINK_INFO, "* try to heal partition"); + connect_to = (node_t*)nodes->head->data; + } + + splay_free_tree(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; - if(!c->outgoing || !c->node || c->node->edge_tree->count < 2) + // perform connect + + if(connect_to && !connect_to->connection) + { + /* 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, connect_to->name)) { + found = true; break; + } + } - logger(mesh, MESHLINK_INFO, "Autodisconnecting from %s", c->name); - list_delete(mesh->outgoings, c->outgoing); - c->outgoing = NULL; - terminate_connection(mesh, c, c->status.active); - break; + if(!found) + { + logger(mesh, MESHLINK_INFO, "Autoconnecting to %s", connect_to->name); + outgoing_t *outgoing = xzalloc(sizeof(outgoing_t)); + outgoing->mesh = mesh; + outgoing->name = xstrdup(connect_to->name); + 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; - for list_each(connection_t, c, mesh->connections) { - if(c->outgoing == o) { - found = true; - break; - } + // disconnect suboptimal outgoing connections + + if(min_connects < cur_connects && cur_connects <= max_connects) + { + unsigned int connects = 0; + + for(int devclass = 0; devclass <= mesh->devclass; ++devclass) + { + for list_each(connection_t, c, mesh->connections) + { + if(!c->status.remove_unused && c->node && c->node->devclass == devclass) + { connects += 1; } } - if(!found) { - logger(mesh, MESHLINK_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->status.remove_unused && c->outgoing && c->node && c->node->devclass >= devclass) + { splay_insert(nodes, c->node); } + } + + if(nodes->head) + { + logger(mesh, MESHLINK_INFO, "* disconnect suboptimal outgoing connection"); + disconnect_from = (node_t*)nodes->head->data; + } + + splay_free_tree(nodes); + break; } } } - if (nc + mesh->outgoings->count < min(autoconnect, mesh->nodes->count - 1)) - timeout = 0; + + // 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.remove_unused && c->node) + { splay_insert(nodes, c->node); } + } + + if(nodes->head) + { + logger(mesh, MESHLINK_INFO, "* disconnect connection (too many connections"); + + //timeout = 0; + disconnect_from = (node_t*)nodes->head->data; + } + + splay_free_tree(nodes); + } + + + // perform disconnect + + if(disconnect_from && disconnect_from->connection) + { + logger(mesh, MESHLINK_INFO, "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); + } + + + // done! + + logger(mesh, MESHLINK_INFO, "--- autoconnect end ---"); } timeout_set(&mesh->loop, data, &(struct timeval){timeout, rand() % 100000}); diff --git a/src/net.h b/src/net.h index 6014a3d6..d2a60bcf 100644 --- a/src/net.h +++ b/src/net.h @@ -76,7 +76,6 @@ extern unsigned replaywin; extern int keylifetime; extern int max_connection_burst; extern bool do_prune; -extern int autoconnect; /* Yes, very strange placement indeed, but otherwise the typedefs get all tangled up */ #include "connection.h" diff --git a/src/net_setup.c b/src/net_setup.c index 68b8ed74..fd438674 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -32,8 +32,6 @@ #include "utils.h" #include "xalloc.h" -int autoconnect = 3; - bool node_read_ecdsa_public_key(meshlink_handle_t *mesh, node_t *n) { if(ecdsa_active(n->ecdsa)) return true; @@ -124,8 +122,8 @@ static bool read_invitation_key(meshlink_handle_t *mesh) { return mesh->invitation_key; } -bool node_read_dclass(meshlink_handle_t *mesh, node_t *n) { - if(n->dclass != 0) +bool node_read_devclass(meshlink_handle_t *mesh, node_t *n) { + if(n->devclass != 0) return true; splay_tree_t *config_tree; @@ -135,19 +133,23 @@ bool node_read_dclass(meshlink_handle_t *mesh, node_t *n) { if(!read_host_config(mesh, config_tree, n->name)) goto exit; - if(get_config_string(lookup_config(config_tree, "DeviceClass"), &p)) { - n->dclass = atoi(p); + if(get_config_string(lookup_config(config_tree, "DeviceClass"), &p)) + { + n->devclass = atoi(p); free(p); } + if(n->devclass < 0 || n->devclass > _DEV_CLASS_MAX) + { n->devclass = _DEV_CLASS_MAX; } + exit: exit_configuration(&config_tree); - return n->dclass != 0; + return n->devclass != 0; } -bool node_write_dclass(meshlink_handle_t *mesh, node_t *n) { +bool node_write_devclass(meshlink_handle_t *mesh, node_t *n) { - if(n->dclass == 0) + if(n->devclass == 0) return false; bool result = false; @@ -167,7 +169,7 @@ bool node_write_dclass(meshlink_handle_t *mesh, node_t *n) { config_add(config_tree, cnf); } - set_config_int(cnf, n->dclass); + set_config_int(cnf, n->devclass); if(!write_host_config(mesh, config_tree, n->name)) goto fail; @@ -201,7 +203,7 @@ void load_all_nodes(meshlink_handle_t *mesh) { n = new_node(); n->name = xstrdup(ent->d_name); - node_read_dclass(mesh, n); + node_read_devclass(mesh, n); node_add(mesh, n); } @@ -230,7 +232,6 @@ bool setup_myself_reloadable(meshlink_handle_t *mesh) { mesh->localdiscovery = true; keylifetime = 3600; // TODO: check if this can be removed as well mesh->maxtimeout = 900; - autoconnect = 3; mesh->self->options |= OPTION_PMTU_DISCOVERY; read_invitation_key(mesh); @@ -332,7 +333,7 @@ bool setup_myself(meshlink_handle_t *mesh) { mesh->self = new_node(); mesh->self->connection = new_connection(); mesh->self->name = name; - mesh->self->dclass = mesh->dclass; + mesh->self->devclass = mesh->devclass; mesh->self->connection->name = xstrdup(name); read_host_config(mesh, mesh->config, name); @@ -383,8 +384,7 @@ bool setup_myself(meshlink_handle_t *mesh) { graph(mesh); - if(autoconnect) - load_all_nodes(mesh); + load_all_nodes(mesh); /* Open sockets */ diff --git a/src/node.h b/src/node.h index 2ece66c5..409449c9 100644 --- a/src/node.h +++ b/src/node.h @@ -40,8 +40,10 @@ typedef struct node_status_t { typedef struct node_t { char *name; /* name of this node */ + void *priv; + uint32_t options; /* options turned on for this node */ - dclass_t dclass; + dev_class_t devclass; struct meshlink_handle *mesh; /* The mesh this node belongs to */ @@ -67,6 +69,7 @@ typedef struct node_t { struct splay_tree_t *edge_tree; /* Edges with this node as one of the endpoints */ struct connection_t *connection; /* Connection associated with this node (if a direct connection exists) */ + time_t last_connect_try; uint32_t sent_seqno; /* Sequence number last sent to this node */ uint32_t received_seqno; /* Sequence number last received from this node */ diff --git a/src/protocol_auth.c b/src/protocol_auth.c index 76188f6e..9cb96b65 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -36,8 +36,10 @@ #include "utils.h" #include "xalloc.h" #include "ed25519/sha512.h" - -extern bool node_write_dclass(meshlink_handle_t *mesh, node_t *n); + +#include + +extern bool node_write_devclass(meshlink_handle_t *mesh, node_t *n); static bool send_proxyrequest(meshlink_handle_t *mesh, connection_t *c) { switch(mesh->proxytype) { @@ -327,11 +329,20 @@ bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { logger(mesh, MESHLINK_ERROR, "Peer %s had unknown identity (%s)", c->hostname, c->name); return false; } + } - read_ecdsa_public_key(mesh, c); - } else { - if(c->protocol_minor && !ecdsa_active(c->ecdsa)) - c->protocol_minor = 1; + read_ecdsa_public_key(mesh, c); + + if(!ecdsa_active(c->ecdsa)) { + logger(mesh, MESHLINK_ERROR, "No key known for peer %s (%s)", c->name, c->hostname); + + node_t *n = lookup_node(mesh, c->name); + if(n && !n->status.waitingforkey) { + logger(mesh, MESHLINK_INFO, "Requesting key from peer %s (%s)", c->name, c->hostname); + send_req_key(mesh, n); + } + + return false; } /* Forbid version rollback for nodes whose ECDSA key we know */ @@ -360,7 +371,7 @@ bool send_ack(meshlink_handle_t *mesh, connection_t *c) { if(mesh->self->options & OPTION_PMTU_DISCOVERY) c->options |= OPTION_PMTU_DISCOVERY; - return send_request(mesh, c, "%d %s %d %x", ACK, mesh->myport, mesh->dclass, (c->options & 0xffffff) | (PROT_MINOR << 24)); + return send_request(mesh, c, "%d %s %d %x", ACK, mesh->myport, mesh->devclass, (c->options & 0xffffff) | (PROT_MINOR << 24)); } static void send_everything(meshlink_handle_t *mesh, connection_t *c) { @@ -375,16 +386,22 @@ static void send_everything(meshlink_handle_t *mesh, connection_t *c) { bool ack_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { char hisport[MAX_STRING_SIZE]; char *hisaddress; - int dclass; + int devclass; uint32_t options; node_t *n; - if(sscanf(request, "%*d " MAX_STRING " %d %x", hisport, &dclass, &options) != 3) { + if(sscanf(request, "%*d " MAX_STRING " %d %x", hisport, &devclass, &options) != 3) { logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s)", "ACK", c->name, c->hostname); return false; } + if(devclass < 0 || devclass > _DEV_CLASS_MAX) { + logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s): %s", "ACK", c->name, + c->hostname, "devclass invalid"); + return false; + } + /* Check if we already have a node_t for him */ n = lookup_node(mesh, c->name); @@ -413,8 +430,8 @@ bool ack_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { } } - n->dclass = dclass; - node_write_dclass(mesh, n); + n->devclass = devclass; + node_write_devclass(mesh, n); n->connection = c; c->node = n; @@ -438,13 +455,15 @@ bool ack_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { /* Create an edge_t for this connection */ + assert(devclass >= 0 && devclass <= _DEV_CLASS_MAX); + c->edge = new_edge(); c->edge->from = mesh->self; c->edge->to = n; sockaddr2str(&c->address, &hisaddress, NULL); c->edge->address = str2sockaddr(hisaddress, hisport); free(hisaddress); - c->edge->weight = weight_from_dclass(dclass); + c->edge->weight = dev_class_traits[devclass].edge_weight; c->edge->connection = c; c->edge->options = c->options; diff --git a/src/protocol_edge.c b/src/protocol_edge.c index d09a7877..89ed901d 100644 --- a/src/protocol_edge.c +++ b/src/protocol_edge.c @@ -33,7 +33,7 @@ #include "utils.h" #include "xalloc.h" -extern bool node_write_dclass(meshlink_handle_t *mesh, node_t *n); +extern bool node_write_devclass(meshlink_handle_t *mesh, node_t *n); bool send_add_edge(meshlink_handle_t *mesh, connection_t *c, const edge_t *e) { bool x; @@ -42,7 +42,7 @@ bool send_add_edge(meshlink_handle_t *mesh, connection_t *c, const edge_t *e) { sockaddr2str(&e->address, &address, &port); x = send_request(mesh, c, "%d %x %s %d %s %s %s %d %x %d", ADD_EDGE, rand(), - e->from->name, e->from->dclass, e->to->name, address, port, e->to->dclass, + e->from->name, e->from->devclass, e->to->name, address, port, e->to->devclass, e->options, e->weight); free(address); free(port); @@ -54,17 +54,17 @@ bool add_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { edge_t *e; node_t *from, *to; char from_name[MAX_STRING_SIZE]; - int from_dclass; + int from_devclass; char to_name[MAX_STRING_SIZE]; char to_address[MAX_STRING_SIZE]; char to_port[MAX_STRING_SIZE]; - int to_dclass; + int to_devclass; sockaddr_t address; uint32_t options; int weight; if(sscanf(request, "%*d %*x "MAX_STRING" %d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %x %d", - from_name, &from_dclass, to_name, to_address, to_port, &to_dclass, &options, &weight) != 8) { + from_name, &from_devclass, to_name, to_address, to_port, &to_devclass, &options, &weight) != 8) { logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s)", "ADD_EDGE", c->name, c->hostname); return false; @@ -78,6 +78,20 @@ bool add_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { return false; } + // Check if devclasses are valid + + if(from_devclass < 0 || from_devclass > _DEV_CLASS_MAX) { + logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s): %s", "ADD_EDGE", c->name, + c->hostname, "from devclass invalid"); + return false; + } + + if(to_devclass < 0 || to_devclass > _DEV_CLASS_MAX) { + logger(mesh, MESHLINK_ERROR, "Got bad %s from %s (%s): %s", "ADD_EDGE", c->name, + c->hostname, "to devclass invalid"); + return false; + } + if(seen_request(mesh, request)) return true; @@ -92,8 +106,8 @@ bool add_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { node_add(mesh, from); } - from->dclass = from_dclass; - node_write_dclass(mesh, from); + from->devclass = from_devclass; + node_write_devclass(mesh, from); if(!to) { to = new_node(); @@ -101,8 +115,8 @@ bool add_edge_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { node_add(mesh, to); } - to->dclass = to_dclass; - node_write_dclass(mesh, to); + to->devclass = to_devclass; + node_write_devclass(mesh, to); /* Convert addresses */ diff --git a/src/sptps.c b/src/sptps.c index 49e0a336..2e9ac6fe 100644 --- a/src/sptps.c +++ b/src/sptps.c @@ -565,6 +565,9 @@ bool sptps_receive_data(sptps_t *s, const void *data, size_t len) { // Start a SPTPS session. bool sptps_start(sptps_t *s, void *handle, bool initiator, bool datagram, ecdsa_t *mykey, ecdsa_t *hiskey, const char *label, size_t labellen, send_data_t send_data, receive_record_t receive_record) { + if(!s || !mykey || !hiskey || !label || !labellen || !send_data || !receive_record) + return error(s, EINVAL, "Invalid argument to sptps_start()"); + // Initialise struct sptps memset(s, 0, sizeof *s);