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 423b2ae..029df1f 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 ac3a630..aa1c115 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 f8ffbe3..b333c5b 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 23b8cae..2cb7f94 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 95ff5c3..74c57c5 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 c1cabdb..2de89e8 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},