From: Guus Sliepen Date: Sun, 21 Oct 2012 15:35:13 +0000 (+0200) Subject: Add the AutoConnect option. X-Git-Tag: release-1.1pre4~32 X-Git-Url: https://tinc-vpn.org/git/browse?p=tinc;a=commitdiff_plain;h=717ea66d7ba0c23f27d86b3d5c6992b751135455 Add the AutoConnect option. 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. --- diff --git a/doc/tinc.conf.5.in b/doc/tinc.conf.5.in index 423b2ae3..029df1fa 100644 --- a/doc/tinc.conf.5.in +++ b/doc/tinc.conf.5.in @@ -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 diff --git a/doc/tinc.texi b/doc/tinc.texi index ac3a6308..aa1c1157 100644 --- a/doc/tinc.texi +++ b/doc/tinc.texi @@ -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 = (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 diff --git a/src/net.c b/src/net.c index f8ffbe34..b333c5b7 100644 --- 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; diff --git a/src/net.h b/src/net.h index 23b8caef..2cb7f940 100644 --- 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) diff --git a/src/net_setup.c b/src/net_setup.c index 95ff5c3e..74c57c5d 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -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 */ diff --git a/src/tincctl.c b/src/tincctl.c index c1cabdb0..2de89e81 100644 --- a/src/tincctl.c +++ b/src/tincctl.c @@ -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},