Add the AutoConnect option.
authorGuus Sliepen <guus@tinc-vpn.org>
Sun, 21 Oct 2012 15:35:13 +0000 (17:35 +0200)
committerGuus Sliepen <guus@tinc-vpn.org>
Sun, 21 Oct 2012 15:35:13 +0000 (17:35 +0200)
When set to a non-zero value, tinc will try to maintain exactly that number of
meta connections to other nodes.  If there are not enough connections, it will
periodically try to set up an outgoing connection to a random node.  If there
are too many connections, it will periodically try to remove an outgoing
connection.

doc/tinc.conf.5.in
doc/tinc.texi
src/net.c
src/net.h
src/net_setup.c
src/tincctl.c

index 423b2ae32d50ec3a274d5843c7802c5966ccb4cc..029df1fa3891c8d107728f5a8277c07c8a6ab8bf 100644 (file)
@@ -114,6 +114,15 @@ If
 .Qq any
 is selected, then depending on the operating system both IPv4 and IPv6 or just
 IPv6 listening sockets will be created.
+.It Va AutoConnect Li = Ar count Po 0 Pc Bq experimental
+If set to a non-zero value,
+.Nm
+will try to only have
+.Ar count
+meta connections to other nodes,
+by automatically making or breaking connections to known nodes.
+Higher values increase redundancy but also increase meta data overhead.
+When using this option, a good value is 3.
 .It Va BindToAddress Li = Ar address Op Ar port
 If your computer has more than one IPv4 or IPv6 address,
 .Nm tinc
index ac3a630894ba0ee24f810dc531eb010350e2c1ab..aa1c1157de3b3cb37bc588639d85da3dfa04b8f9 100644 (file)
@@ -805,6 +805,14 @@ This option affects the address family of listening and outgoing sockets.
 If any is selected, then depending on the operating system
 both IPv4 and IPv6 or just IPv6 listening sockets will be created.
 
+@cindex AutoConnect
+@item AutoConnect = <count> (0) [experimental]
+If set to a non-zero value,
+tinc will try to only have count meta connections to other nodes,
+by automatically making or breaking connections to known nodes.
+Higher values increase redundancy but also increase meta data overhead.
+When using this option, a good value is 3.
+
 @cindex BindToAddress
 @item BindToAddress = <@var{address}> [<@var{port}>]
 If your computer has more than one IPv4 or IPv6 address, tinc
index f8ffbe346ce168e511aa4b32fab0847d97d4ba82..b333c5b7b05777595ede76205b18921953a7f70e 100644 (file)
--- a/src/net.c
+++ b/src/net.c
@@ -74,7 +74,7 @@ void purge(void) {
                                if(e->to == n)
                                        return;
 
-                       if(!strictsubnets || !n->subnet_tree->head)
+                       if(!autoconnect && (!strictsubnets || !n->subnet_tree->head))
                                /* in strictsubnets mode do not delete nodes with subnets */
                                node_del(n);
                }
@@ -164,6 +164,15 @@ static void timeout_handler(int fd, short events, void *event) {
                }
        }
 
+       event_add(event, &(struct timeval){pingtimeout, 0});
+}
+
+static void periodic_handler(int fd, short events, void *event) {
+       /* Check if there are too many contradicting ADD_EDGE and DEL_EDGE messages.
+          This usually only happens when another node has the same Name as this node.
+          If so, sleep for a short while to prevent a storm of contradicting messages.
+       */
+
        if(contradicting_del_edge > 100 && contradicting_add_edge > 100) {
                logger(DEBUG_ALWAYS, LOG_WARNING, "Possible node with same Name as us! Sleeping %d seconds.", sleeptime);
                usleep(sleeptime * 1000000LL);
@@ -179,7 +188,97 @@ static void timeout_handler(int fd, short events, void *event) {
        contradicting_add_edge = 0;
        contradicting_del_edge = 0;
 
-       event_add(event, &(struct timeval){pingtimeout, 0});
+       /* If AutoConnect is set, check if we need to make or break connections. */
+
+       if(autoconnect && node_tree->count > 1) {
+               /* Count number of active connections */
+               int nc = 0;
+               for list_each(connection_t, c, connection_list) {
+                       if(c->status.active && !c->status.control)
+                               nc++;
+               }
+
+               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.
+                       */
+                       int r = rand() % node_tree->count;
+                       int i = 0;
+
+                       for splay_each(node_t, n, node_tree) {
+                               if(i++ != r)
+                                       continue;
+
+                               if(n->connection)
+                                       break;
+
+                               bool found = false;
+
+                               for list_each(outgoing_t, outgoing, outgoing_list) {
+                                       if(!strcmp(outgoing->name, n->name)) {
+                                               found = true;
+                                               break;
+                                       }
+                               }
+
+                               if(!found) {
+                                       logger(DEBUG_CONNECTIONS, LOG_INFO, "Autoconnecting to %s", n->name);
+                                       outgoing_t *outgoing = xmalloc_and_zero(sizeof *outgoing);
+                                       outgoing->name = xstrdup(n->name);
+                                       list_insert_tail(outgoing_list, outgoing);
+                                       setup_outgoing_connection(outgoing);
+                               }
+                               break;
+                       }
+               } else 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, connection_list) {
+                               if(!c->status.active || c->status.control)
+                                       continue;
+
+                               if(i++ != r)
+                                       continue;
+
+                               if(!c->outgoing || !c->node || c->node->edge_tree->count < 2)
+                                       break;
+
+                               logger(DEBUG_CONNECTIONS, LOG_INFO, "Autodisconnecting from %s", c->name);
+                               list_delete(outgoing_list, c->outgoing);
+                               c->outgoing = NULL;
+                               terminate_connection(c, c->status.active);
+                               break;
+                       }
+               }
+
+               if(nc >= autoconnect) {
+                       /* If we have enough active connections,
+                          remove any pending outgoing connections.
+                       */
+                       for list_each(outgoing_t, o, outgoing_list) {
+                               bool found = false;
+                               for list_each(connection_t, c, connection_list) {
+                                       if(c->outgoing == o) {
+                                               found = true;
+                                               break;
+                                       }
+                               }
+                               if(!found) {
+                                       logger(DEBUG_CONNECTIONS, LOG_INFO, "Cancelled outgoing connection to %s", o->name);
+                                       list_delete_node(outgoing_list, node);
+                               }
+                       }
+               }
+       }
+
+       event_add(event, &(struct timeval){5, 0});
 }
 
 void handle_meta_connection_data(int fd, short events, void *data) {
@@ -347,10 +446,14 @@ void retry(void) {
 */
 int main_loop(void) {
        struct event timeout_event;
+       struct event periodic_event;
 
        timeout_set(&timeout_event, timeout_handler, &timeout_event);
        event_add(&timeout_event, &(struct timeval){pingtimeout, 0});
 
+       timeout_set(&periodic_event, periodic_handler, &periodic_event);
+       event_add(&periodic_event, &(struct timeval){5, 0});
+
 #ifndef HAVE_MINGW
        struct event sighup_event;
        struct event sigterm_event;
index 23b8caef8a3426d08863ba63dd073f25d2489458..2cb7f940781f2c2e92b4e27503a9bacdaf1741aa 100644 (file)
--- a/src/net.h
+++ b/src/net.h
@@ -134,6 +134,7 @@ extern int udp_rcvbuf;
 extern int udp_sndbuf;
 extern bool do_prune;
 extern char *myport;
+extern int autoconnect;
 extern int contradicting_add_edge;
 extern int contradicting_del_edge;
 extern time_t last_config_check;
@@ -190,6 +191,7 @@ extern void purge(void);
 extern void retry(void);
 extern int reload_configuration(void);
 extern void load_all_subnets(void);
+extern void load_all_nodes(void);
 
 #ifndef HAVE_MINGW
 #define closesocket(s) close(s)
index 95ff5c3e962e08c82149a3bdb1816782d99ad2b1..74c57c5dd07cfadbcaaa3a9ebc94034da3716aaf 100644 (file)
@@ -50,6 +50,7 @@ char *proxyport;
 char *proxyuser;
 char *proxypass;
 proxytype_t proxytype;
+int autoconnect;
 
 char *scriptinterpreter;
 char *scriptextension;
@@ -347,6 +348,36 @@ void load_all_subnets(void) {
        closedir(dir);
 }
 
+void load_all_nodes(void) {
+       DIR *dir;
+       struct dirent *ent;
+       char *dname;
+
+       xasprintf(&dname, "%s" SLASH "hosts", confbase);
+       dir = opendir(dname);
+       if(!dir) {
+               logger(DEBUG_ALWAYS, LOG_ERR, "Could not open %s: %s", dname, strerror(errno));
+               free(dname);
+               return;
+       }
+
+       while((ent = readdir(dir))) {
+               if(!check_id(ent->d_name))
+                       continue;
+
+               node_t *n = lookup_node(ent->d_name);
+               if(n)
+                       continue;
+
+               n = new_node();
+               n->name = xstrdup(ent->d_name);
+               node_add(n);
+       }
+
+       closedir(dir);
+}
+
+
 char *get_name(void) {
        char *name = NULL;
 
@@ -570,6 +601,8 @@ bool setup_myself_reloadable(void) {
        if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
                keylifetime = 3600;
 
+       get_config_int(lookup_config(config_tree, "AutoConnect"), &autoconnect);
+
        return true;
 }
 
@@ -730,6 +763,8 @@ static bool setup_myself(void) {
 
        if(strictsubnets)
                load_all_subnets();
+       else if(autoconnect)
+               load_all_nodes();
 
        /* Open device */
 
index c1cabdb0944c2f7db348ca1a510a6f11e84c70c2..2de89e81200728ecc0bc5008c760ad9b0268ac2d 100644 (file)
@@ -1233,6 +1233,7 @@ static struct {
 } const variables[] = {
        /* Server configuration */
        {"AddressFamily", VAR_SERVER},
+       {"AutoConnect", VAR_SERVER},
        {"BindToAddress", VAR_SERVER | VAR_MULTIPLE},
        {"BindToInterface", VAR_SERVER},
        {"Broadcast", VAR_SERVER},