]> git.meshlink.io Git - meshlink/commitdiff
Merge branch 'mesh_topology_output' into roles
authorNiklas Hofmann <niklas.hofmann@everbase.net>
Wed, 13 Aug 2014 11:30:16 +0000 (13:30 +0200)
committerNiklas Hofmann <niklas.hofmann@everbase.net>
Wed, 13 Aug 2014 11:30:16 +0000 (13:30 +0200)
16 files changed:
examples/chat.c
examples/chatpp.cc
examples/manynodes.c
examples/meshlinkapp.c
src/discovery.c
src/meshlink++.h
src/meshlink.c
src/meshlink.h
src/meshlink_internal.h
src/net.c
src/net.h
src/net_setup.c
src/node.h
src/protocol_auth.c
src/protocol_edge.c
src/sptps.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 b50aea8d80f689991355db5dac9269eb292fa06e..d17f962b4ebfebb7ba41b4fd3887a9eb3bc69dc3 100644 (file)
@@ -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:
             {
index 8271ab2f4cbfb4814009a81d2f9b61a7c8381f68..ef578b113068cc8993bf3728d547f194c45bf06a 100644 (file)
@@ -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<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 e84aafcac9e3a7d3197e278eb186c510b0c5a232..1d52724e26d6e31b52255bbb2ae8976e4edbb770 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;
@@ -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
+};
index 6f104abe5fcb5cc29bedb9f7e048dd465e211e37..7cf1f4445a5e780f20472dbfdac61b5432232381 100644 (file)
@@ -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
index 42e1da537b600f5b0b8b0274b82c3e4a646a2aec..994f25bf9bd5f077403ffc67ec5f484ce6165d50 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,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
index c615b893031a63cf727dfe89ccd2d17b1edaaeef..80e866a0ad461fc49bce1794379044ca97e2e33c 100644 (file)
--- a/src/net.c
+++ b/src/net.c
@@ -31,6 +31,8 @@
 #include "protocol.h"
 #include "xalloc.h"
 
+#include <assert.h>
+
 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});
index 6014a3d6207881d15b4fbc8bbfe8aa8f43d48f2e..d2a60bcf685c858ef079f59b8d1b76305b609722 100644 (file)
--- 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"
index 68b8ed7499d92209573e5aeb582bc991e753b0d4..fd4386746c2b1f626a35bb273e456ce3f08a80b0 100644 (file)
@@ -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 */
 
index 2ece66c5ad542d53d7a382cccf627987a802f38c..409449c94e80ed1c382d82522c275474ee86d7dc 100644 (file)
@@ -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 */
index 76188f6ebdba79fa11837daffeea33b897750ad0..9cb96b6590aecd6dd47f1af1fb23fc2ca694178c 100644 (file)
 #include "utils.h"
 #include "xalloc.h"
 #include "ed25519/sha512.h"
-    
-extern bool node_write_dclass(meshlink_handle_t *mesh, node_t *n);
+
+#include <assert.h>
+
+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;
 
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 */
 
index 49e0a336de6a66436fb245507bb16d0ea869ac94..2e9ac6fe63331917c320805fdac592fea91753f7 100644 (file)
@@ -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);