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.
This commit is contained in:
Guus Sliepen 2012-10-21 17:35:13 +02:00
parent 1f8b70efa0
commit 717ea66d7b
6 changed files with 160 additions and 2 deletions

View 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

View 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

107
src/net.c
View file

@ -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;

View file

@ -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)

View 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 */

View 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},