]> git.meshlink.io Git - meshlink/commitdiff
converging auto connect algorithm
authorNiklas Hofmann <niklas.hofmann@everbase.net>
Tue, 12 Aug 2014 03:32:22 +0000 (05:32 +0200)
committerNiklas Hofmann <niklas.hofmann@everbase.net>
Tue, 12 Aug 2014 03:32:22 +0000 (05:32 +0200)
13 files changed:
examples/chat.c
examples/chatpp.cc
examples/manynodes.c
examples/meshlinkapp.c
src/meshlink++.h
src/meshlink.c
src/meshlink.h
src/meshlink_internal.h
src/net.c
src/net_setup.c
src/node.h
src/protocol_auth.c
src/protocol_edge.c

index 3eaa5c7e3b3fe5ef093b7955d0db011cf15591f0..c6d67fb5c1ba2c111d4c814fdc1971699dc85ccc 100644 (file)
@@ -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;
index e500ad2733547b63fcd29ef46293061baafc5767..331f6584543b35ae236bc794e961f30611866ffe 100644 (file)
@@ -187,7 +187,7 @@ int main(int argc, char *argv[]) {
        if(argc > 2)
                nick = argv[2];
 
-       ChatMesh* mesh = meshlink::open<ChatMesh>(confbase, nick, "chatpp", STATIONARY);
+       ChatMesh* mesh = meshlink::open<ChatMesh>(confbase, nick, "chatpp", DEV_CLASS_STATIONARY);
 
        if(!mesh) {
                fprintf(stderr, "Could not open MeshLink: %s\n", meshlink::strerror());
index 96aa5f0f9843db84bb7c4bc7481a285965fa6491..22ebad2145e52ae1cb5d75b595f9c6f756e06c1a 100644 (file)
@@ -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");
index 6c56165c2dbdd8d6554c3f4aed0108ce4df2fc0c..d9e5b7e0ba4d6dfcb52632ff1f0a16852a1c71fe 100644 (file)
@@ -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);
index 8271ab2f4cbfb4814009a81d2f9b61a7c8381f68..5f5539a272b137aba704cd0253fcf347ece50dde 100644 (file)
@@ -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<class MESH>
-       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;
        }
 
index 95ab41c7ab03f072fa1547b42be6eee92b20cadf..3661f064c4ef6a89279503525ac5275ea7863f88 100644 (file)
@@ -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;
@@ -776,10 +776,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);
        pthread_mutex_init ( &(mesh->nodes_mutex), NULL);
        mesh->threadstarted = false;
@@ -1706,100 +1712,11 @@ static void __attribute__((destructor)) meshlink_exit(void) {
        crypto_exit();
 }
 
-int cweight_from_dclass(dclass_t dclass)
-{
-       switch(dclass)
-       {
-       case BACKBONE:
-               return 1;
-
-       case STATIONARY:
-               return 3;
-
-       case PORTABLE:
-               return 6;
-       }
-
-       return 9;
-}
-
-int max_ccount_from_dclass(dclass_t dclass)
-{
-       switch(dclass)
-       {
-       case BACKBONE:
-               return 10000;
 
-       case STATIONARY:
-               return 100;
-
-       case PORTABLE:
-               return 3;
-       }
-
-       return 3;
-}
-
-bool dclass_ccounts_satisfied(dclass_t dclass, splay_tree_t* counts, int total_count)
-{
-       // lookup keys
-       dclass_ccount_t key_portable;
-       key_portable.dclass = PORTABLE;
-
-       dclass_ccount_t key_stationary;
-       key_stationary.dclass = STATIONARY;
-
-       dclass_ccount_t key_backbone;
-       key_backbone.dclass = BACKBONE;
-
-       // check num of portable devices
-       dclass_ccount_t* portable = splay_search(counts, &key_portable);
-
-       if(portable)
-       {
-               if(portable->ccount > 9)
-                       return true;
-       }
-
-       // check num of stationary devices
-       dclass_ccount_t* stationary = splay_search(counts, &key_stationary);
-
-       if(stationary)
-       {
-               if(stationary->ccount > 6)
-                       return true;
-       }
-
-       // check num of backbone devices
-       dclass_ccount_t* backbone = splay_search(counts, &key_backbone);
-
-       if(backbone)
-       {
-               if(backbone->ccount > 3)
-                       return true;
-       }
-
-       // fallback
-       if(total_count >= max_ccount_from_dclass(dclass))
-               return true;
-
-       return false;
-}
-
-int dclass_ccount_compare(const void *a, const void *b)
-{
-       const dclass_ccount_t* da = a;
-       const dclass_ccount_t* db = b;
-
-       return da->dclass - db->dclass;
-}
-
-dclass_ccount_t* dclass_ccount_alloc()
-{
-       return xzalloc(sizeof(dclass_ccount_t));
-}
-
-void dclass_ccount_delete(void *c)
-{
-       free(c);
-}
+/// Device class
+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 6858b5bc9d889f80b07331edf7fcc36106632114..9f6c63ceb5e55616c6e7662f2e1940b58c9a4d24 100644 (file)
@@ -65,12 +65,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
@@ -84,6 +86,8 @@ extern __thread meshlink_errno_t meshlink_errno;
 
 struct meshlink_handle {
        const char *name;
+       char *appname;
+       dev_class_t devclass;
        void *priv;
 };
 
@@ -130,15 +134,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
index f7f0ba789b7164cd66e8afef1a542d4a27df02aa..c1f3659da0d2fdf73563017952124daf27d9a2be 100644 (file)
@@ -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,17 +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 cweight_from_dclass(dclass_t dclass);
-extern int max_ccount_from_dclass(dclass_t dclass);
-extern bool dclass_ccounts_satisfied(dclass_t dclass, splay_tree_t* counts, int total_count);
-
+/// Device class
 typedef struct {
-       dclass_t dclass;
-       int ccount;
-} dclass_ccount_t;
+       unsigned int min_connects;
+       unsigned int max_connects;
+       int edge_weight;
+} dev_class_traits_t;
 
-extern int dclass_ccount_compare(const void *a, const void *b);
-extern dclass_ccount_t* dclass_ccount_alloc();
-extern void dclass_ccount_delete(void *c);
+extern dev_class_traits_t dev_class_traits[];
 
 #endif // MESHLINK_INTERNAL_H
index ed12924580e40955218608d5824a6b874c554f3c..654a7813cee55fca752d9092458a9a0cd6bc9091 100644 (file)
--- a/src/net.c
+++ b/src/net.c
@@ -31,6 +31,7 @@
 #include "protocol.h"
 #include "xalloc.h"
 
+
 static const int min(int a, int b) {
        return a < b ? a : b;
 }
@@ -129,75 +130,61 @@ 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(n->connection)
-               return false;
-       
-       return true;
+       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_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;
 }
 
+
 static void periodic_handler(event_loop_t *loop, void *data) {
        meshlink_handle_t *mesh = loop->data;
 
@@ -229,127 +216,212 @@ static void periodic_handler(event_loop_t *loop, void *data) {
 
                logger(mesh, MESHLINK_INFO, "--- autoconnect begin ---");
 
-               splay_tree_t* ccounts = splay_alloc_tree(dclass_ccount_compare, NULL);
 
-               /* Count number of active connections per device class */
-               int num_total = 0;
-               for list_each(connection_t, c, mesh->connections) {
-                       if(c->status.active)
+               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);
+
+
+               // get min_connects and max_connects
+
+               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)
                        {
-                               dclass_ccount_t key;
-                               key.dclass = c->node->dclass;
+                               logger(mesh, MESHLINK_INFO, "* found best one for initial connect");
+
+                               timeout = 0;
+                               connect_to = (node_t*)nodes->head->data;
+                       }
+
+                       splay_free_tree(nodes);
+               }
+
 
-                               dclass_ccount_t* ccount = splay_search(ccounts, &key);
+               // find better nodes to connect to
+
+               if(!connect_to && min_connects <= cur_connects < max_connects)
+               {
+                       unsigned int connects = 0;
 
-                               if(!ccount)
+                       for(int devclass = 0; devclass <= mesh->devclass; ++devclass)
+                       {
+                               for list_each(connection_t, c, mesh->connections)
                                {
-                                       ccount = dclass_ccount_alloc();
-                                       ccount->dclass = c->node->dclass;
-                                       splay_insert(ccounts, ccount);
+                                       if(!c->status.remove_unused && c->node && c->node->devclass == devclass)
+                                               { connects += 1; }
                                }
 
-                               ccount->ccount++;
-                               num_total++;
+                               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; }
                        }
                }
 
-               /* Count number of unreachable nodes */
-               int num_unreachable = 0;
-               for splay_each(node_t, n, mesh->nodes) {
-                       if(!n->status.reachable)
-                               num_unreachable++;
-               }
 
-               bool satisfied = dclass_ccounts_satisfied(mesh->self->dclass, ccounts, num_total);
-               int maxcc = max_ccount_from_dclass(mesh->self->dclass);
+               // heal partitions
 
-               logger(mesh, MESHLINK_INFO, "* num_total = %d, satisfied = %d, maxcc = %d", num_total, satisfied, maxcc);
+               if(!connect_to && min_connects <= cur_connects < max_connects)
+               {
+                       splay_tree_t *nodes = splay_alloc_tree(node_compare_devclass_asc_last_connect_try_desc, NULL);
 
-               if(!satisfied) {
-                       logger(mesh, MESHLINK_INFO, "* Not enough active connections, try to add one.");
-                       /* 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);
-               }
+                       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;
+                       }
 
-               if(satisfied && num_unreachable > 0) {
-                       logger(mesh, MESHLINK_INFO, "* Min number of connections established. Now heal possible partitions.");
-                       /* 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);
+                       splay_free_tree(nodes);
                }
-               
-               if(num_total > maxcc) {
-                       logger(mesh, MESHLINK_INFO, "* Too many active connections, try to remove one.");
-                       /* 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() % num_total;
-                       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)
-                                       break;
+               // perform connect
 
-                               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(connect_to && !connect_to->connection)
+               {
+                       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(satisfied) {
-                       logger(mesh, MESHLINK_INFO, "* We have enough active connections, remove pending outgoing connections.");
-                       /* 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 <= 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 (!satisfied && (num_total + mesh->outgoings->count) < mesh->nodes->count)
+
+               // 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, "* No timeout.");
-                       timeout = 0;
+                       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);
                }
 
-               splay_free_tree(ccounts);
+
+               // done!
 
                logger(mesh, MESHLINK_INFO, "--- autoconnect end ---");
        }
index 3a14729a12248fd7a29156b81d5c5f51b537d772..fd4386746c2b1f626a35bb273e456ce3f08a80b0 100644 (file)
@@ -122,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;
@@ -133,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;
@@ -165,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;
@@ -199,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);
        }
 
@@ -329,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);
 
index 2ece66c5ad542d53d7a382cccf627987a802f38c..23237522f89468b365ed0d2e6fcdf3db8eddfe0a 100644 (file)
@@ -41,7 +41,7 @@ typedef struct node_status_t {
 typedef struct node_t {
        char *name;                             /* name of this node */
        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 +67,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 */
index 65736253020c08cf49d6c6758b4c37187aaee749..82254593e36aa0a5982ad447dae801c5baa76f89 100644 (file)
@@ -37,7 +37,7 @@
 #include "xalloc.h"
 #include "ed25519/sha512.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);
 
 static bool send_proxyrequest(meshlink_handle_t *mesh, connection_t *c) {
        switch(mesh->proxytype) {
@@ -360,7 +360,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 +375,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 +419,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;
@@ -444,7 +450,7 @@ bool ack_h(meshlink_handle_t *mesh, connection_t *c, const char *request) {
        sockaddr2str(&c->address, &hisaddress, NULL);
        c->edge->address = str2sockaddr(hisaddress, hisport);
        free(hisaddress);
-       c->edge->weight = cweight_from_dclass(dclass);
+       c->edge->weight = dev_class_traits[devclass].edge_weight;
        c->edge->connection = c;
        c->edge->options = c->options;
 
index d09a78779ed370fc2bd57bcf73f2086fdbed5970..89ed901d1979b0e04e5a865dc18deb78a2e2700f 100644 (file)
@@ -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 */