This commit implements average RTT estimation based on PING-PONG between active TCP connections.

Average RTT can be used to update edge weight and propagate it to the network.
tinc dump edges has been also extended to give the current RTT.
New edge weight will change only if the config has EdgeUpdateInterval set to other value than 0.

- Ignore local configuration for editors
- Extended manpage with informations about EdgeUpdateInterval
- Added clone_edge and fixed potential segfault when b->from not defined
- Compute avg_rtt based on the time values we got back in PONG
- Add avg_rtt on dump edge
- Send current time on PING and return it on PONG
- Changed last_ping_time to struct timeval
- Extended edge_t with avg_rtt
This commit is contained in:
thorkill 2015-04-11 15:27:28 +02:00
parent 9910f8f2d1
commit 0cd387fd90
14 changed files with 149 additions and 33 deletions

1
.gitignore vendored
View file

@ -13,5 +13,6 @@ Makefile.in
/missing
INSTALL
.deps
.dir-locals.el
stamp-h1
/src/device.c

View file

@ -18,7 +18,7 @@ option to assign a network name to each tinc daemon.
.Pp
The effect of this option is that the daemon will set its configuration root to
.Pa @sysconfdir@/tinc/ Ns Ar NETNAME Ns Pa / ,
where
where
.Ar NETNAME
is your argument to the
.Fl n
@ -35,7 +35,7 @@ In this case, the network name would just be empty, and
.Nm tinc
now looks for files in
.Pa @sysconfdir@/tinc/ ,
instead of
instead of
.Pa @sysconfdir@/tinc/ Ns Ar NETNAME Ns Pa / ;
the configuration file should be
.Pa @sysconfdir@/tinc/tinc.conf ,
@ -90,7 +90,7 @@ or assignments in the form of:
.Pp
The variable names are case insensitive, and any spaces, tabs,
newlines and carriage returns are ignored.
Note: it is not required that you put in the
Note: it is not required that you put in the
.Li =
sign, but doing so improves readability.
If you leave it out, remember to replace it with at least one space character.
@ -129,7 +129,7 @@ This is the same as
however the address given with the
.Va BindToAddress
option will also be used for outgoing connections. This is useful if your
computer has more than one IPv4 or IPv6 address, and you want
computer has more than one IPv4 or IPv6 address, and you want
.Nm tinc
to only use a specific one for outgoing packets.
.It Va BindToInterface Li = Ar interface Bq experimental
@ -231,7 +231,7 @@ Open a multicast UDP socket and bind it to the address and port (separated by sp
Packets are read from and written to this multicast socket.
This can be used to connect to UML, QEMU or KVM instances listening on the same multicast address.
Do NOT connect multiple
.Nm tinc
.Nm tinc
daemons to the same multicast address, this will very likely cause routing loops.
Also note that this can cause decrypted VPN packets to be sent out on a real network if misconfigured.
.It uml Pq not compiled in by default
@ -276,6 +276,12 @@ When this option is enabled, packets that cannot be sent directly to the destina
but which would have to be forwarded by an intermediate node, are dropped instead.
When combined with the IndirectData option,
packets for nodes for which we do not have a meta connection with are also dropped.
.It Va EdgeUpdateInterval Li = Ar seconds Pq 0
The number of seconds between notification of edge changes.
.Nm tinc
will send updated edge informations to all active connections. When the value is 0
.Nm tinc
will not send any updates.
.It Va Ed25519PrivateKeyFile Li = Ar filename Po Pa @sysconfdir@/tinc/ Ns Ar NETNAME Ns Pa /ed25519_key.priv Pc
The file in which the private Ed25519 key of this tinc daemon resides.
This is only used if
@ -399,7 +405,7 @@ This is the name which identifies this tinc daemon.
It must be unique for the virtual private network this daemon will connect to.
.Va Name
may only consist of alphanumeric and underscore characters (a-z, A-Z, 0-9 and _), and is case sensitive.
If
If
.Va Name
starts with a
.Li $ ,
@ -428,7 +434,7 @@ It will allow this tinc daemon to authenticate itself to other daemons.
.It Va PrivateKeyFile Li = Ar filename Po Pa @sysconfdir@/tinc/ Ns Ar NETNAME Ns Pa /rsa_key.priv Pc
The file in which the private RSA key of this tinc daemon resides.
.It Va ProcessPriority Li = low | normal | high
When this option is used the priority of the
When this option is used the priority of the
.Nm tincd
process will be adjusted.
Increasing the priority may help to reduce latency and packet loss on the VPN.
@ -682,7 +688,7 @@ This script is started when an invitation has been used.
.Pp
The scripts are started without command line arguments, but can make use of certain environment variables.
Under UNIX like operating systems the names of environment variables must be preceded by a
.Li $
.Li $
in scripts.
Under Windows, in
.Pa .bat

View file

@ -99,7 +99,7 @@ typedef struct connection_t {
int tcplen; /* length of incoming TCPpacket */
int allow_request; /* defined if there's only one request possible */
time_t last_ping_time; /* last time we saw some activity from the other end or pinged them */
struct timeval last_ping_time; /* last time we saw some activity from the other end or pinged them */
splay_tree_t *config_tree; /* Pointer to configuration tree belonging to him */
} connection_t;

View file

@ -43,10 +43,12 @@ static int edge_weight_compare(const edge_t *a, const edge_t *b) {
if(result)
return result;
result = strcmp(a->from->name, b->from->name);
if (a->from && b->from) {
result = strcmp(a->from->name, b->from->name);
if(result)
return result;
if(result)
return result;
}
return strcmp(a->to->name, b->to->name);
}
@ -97,6 +99,26 @@ void edge_del(edge_t *e) {
splay_delete(e->from->edge_tree, e);
}
edge_t *clone_edge(edge_t *e) {
edge_t *v;
v = new_edge();
v->from = e->from;
v->to = e->to;
memcpy(&v->address, &e->address, sizeof(sockaddr_t));
memcpy(&v->local_address, &e->local_address, sizeof(sockaddr_t));
v->options = e->options;
v->weight = e->weight;
v->avg_rtt = e->avg_rtt;
v->connection = e->connection;
v->reverse = e->reverse;
return v;
}
edge_t *lookup_edge(node_t *from, node_t *to) {
edge_t v;
@ -111,10 +133,11 @@ bool dump_edges(connection_t *c) {
for splay_each(edge_t, e, n->edge_tree) {
char *address = sockaddr2hostname(&e->address);
char* local_address = sockaddr2hostname(&e->local_address);
send_request(c, "%d %d %s %s %s %s %x %d",
CONTROL, REQ_DUMP_EDGES,
e->from->name, e->to->name, address,
local_address, e->options, e->weight);
send_request(c, "%d %d %s %s %s %s %x %d %d",
CONTROL, REQ_DUMP_EDGES,
e->from->name, e->to->name, address,
local_address, e->options, e->weight,
e->avg_rtt);
free(address);
free(local_address);
}

View file

@ -34,6 +34,7 @@ typedef struct edge_t {
uint32_t options; /* options turned on for this edge */
int weight; /* weight of this edge */
int avg_rtt; /* average RTT */
struct connection_t *connection; /* connection associated with this edge, if available */
struct edge_t *reverse; /* edge in the opposite direction, if available */
@ -50,6 +51,7 @@ extern void free_edge_tree(splay_tree_t *);
extern void edge_add(edge_t *);
extern void edge_del(edge_t *);
extern edge_t *lookup_edge(struct node_t *, struct node_t *);
extern edge_t *clone_edge(edge_t *);
extern bool dump_edges(struct connection_t *);
#endif /* __TINC_EDGE_H__ */

View file

@ -148,12 +148,12 @@ static void timeout_handler(void *data) {
if(c->status.control)
continue;
if(c->last_ping_time + pingtimeout <= now.tv_sec) {
if(c->last_ping_time.tv_sec + pingtimeout <= now.tv_sec) {
if(c->edge) {
try_tx(c->node, false);
if(c->status.pinged) {
logger(DEBUG_CONNECTIONS, LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds", c->name, c->hostname, (long)now.tv_sec - c->last_ping_time);
} else if(c->last_ping_time + pinginterval <= now.tv_sec) {
logger(DEBUG_CONNECTIONS, LOG_INFO, "%s (%s) didn't respond to PING in %ld seconds", c->name, c->hostname, (long)now.tv_sec - c->last_ping_time.tv_sec);
} else if(c->last_ping_time.tv_sec + pinginterval <= now.tv_sec) {
send_ping(c);
continue;
} else {
@ -431,8 +431,10 @@ void retry(void) {
/* Check for outgoing connections that are in progress, and reset their ping timers */
for list_each(connection_t, c, connection_list) {
if(c->outgoing && !c->node)
c->last_ping_time = 0;
if(c->outgoing && !c->node) {
c->last_ping_time.tv_sec = 0;
c->last_ping_time.tv_usec = 0;
}
}
/* Kick the ping timeout handler */

View file

@ -147,6 +147,7 @@ extern listen_socket_t listen_socket[MAXSOCKETS];
extern int listen_sockets;
extern io_t unix_socket;
extern int keylifetime;
extern int edgeupdateinterval;
extern int udp_rcvbuf;
extern int udp_sndbuf;
extern int max_connection_burst;
@ -209,6 +210,7 @@ extern bool read_rsa_public_key(struct connection_t *);
extern void handle_device_data(void *, int);
extern void handle_meta_connection_data(struct connection_t *);
extern void regenerate_key(void);
extern void update_edge_weight(void);
extern void purge(void);
extern void retry(void);
extern int reload_configuration(void);

View file

@ -58,6 +58,7 @@
#define MIN_PROBE_SIZE 18
int keylifetime = 0;
int edgeupdateinterval = 0;
#ifdef HAVE_LZO
static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999_MEM_COMPRESS : LZO1X_1_MEM_COMPRESS];
#endif

View file

@ -311,12 +311,18 @@ static bool read_rsa_private_key(void) {
#endif
static timeout_t keyexpire_timeout;
static timeout_t edgeupdate_timeout;
static void keyexpire_handler(void *data) {
regenerate_key();
timeout_set(data, &(struct timeval){keylifetime, rand() % 100000});
}
static void edgeupdate_handler(void *data) {
update_edge_weight();
timeout_set(data, &(struct timeval){edgeupdateinterval, rand() % 100000});
}
void regenerate_key(void) {
logger(DEBUG_STATUS, LOG_INFO, "Expiring symmetric keys");
send_key_changed();
@ -324,6 +330,29 @@ void regenerate_key(void) {
n->status.validkey_in = false;
}
void update_edge_weight(void) {
edge_t *t = NULL;
logger(DEBUG_STATUS, LOG_INFO, "Update edge weight");
for list_each(connection_t, c, connection_list) {
if (c->status.control || !c->edge)
continue;
if (c->edge->avg_rtt) {
t = clone_edge(c->edge);
send_del_edge(c, c->edge);
edge_del(c->edge);
/* avg_rtt is in ms */
t->weight = t->avg_rtt*10;
c->edge = t;
edge_add(t);
send_add_edge(c, t);
}
}
}
/*
Read Subnets from all host config files
*/
@ -624,6 +653,9 @@ bool setup_myself_reloadable(void) {
if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
keylifetime = 3600;
if(!get_config_int(lookup_config(config_tree, "EdgeUpdateInterval"), &edgeupdateinterval))
edgeupdateinterval = 0;
config_t *cfg = lookup_config(config_tree, "AutoConnect");
if(cfg) {
if(!get_config_bool(cfg, &autoconnect)) {
@ -894,6 +926,9 @@ static bool setup_myself(void) {
timeout_add(&keyexpire_timeout, keyexpire_handler, &keyexpire_timeout, &(struct timeval){keylifetime, rand() % 100000});
if (edgeupdateinterval)
timeout_add(&edgeupdate_timeout, edgeupdate_handler, &edgeupdate_timeout, &(struct timeval){edgeupdateinterval, rand() % 100000});
/* Check if we want to use message authentication codes... */
int maclength = 4;

View file

@ -319,7 +319,7 @@ void retry_outgoing(outgoing_t *outgoing) {
void finish_connecting(connection_t *c) {
logger(DEBUG_CONNECTIONS, LOG_INFO, "Connected to %s (%s)", c->name, c->hostname);
c->last_ping_time = now.tv_sec;
c->last_ping_time = now;
c->status.connecting = false;
send_id(c);
@ -555,7 +555,7 @@ begin:
#endif
c->outmaclength = myself->connection->outmaclength;
c->outcompression = myself->connection->outcompression;
c->last_ping_time = now.tv_sec;
c->last_ping_time = now;
connection_add(c);
@ -708,7 +708,7 @@ void handle_new_meta_connection(void *data, int flags) {
c->address = sa;
c->hostname = sockaddr2hostname(&sa);
c->socket = fd;
c->last_ping_time = now.tv_sec;
c->last_ping_time = now;
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection from %s", c->hostname);
@ -747,7 +747,7 @@ void handle_new_unix_connection(void *data, int flags) {
c->address = sa;
c->hostname = xstrdup("localhost port unix");
c->socket = fd;
c->last_ping_time = now.tv_sec;
c->last_ping_time = now;
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection from %s", c->hostname);

View file

@ -99,6 +99,7 @@ extern bool send_error(struct connection_t *, int, const char *);
extern bool send_termreq(struct connection_t *);
extern bool send_ping(struct connection_t *);
extern bool send_pong(struct connection_t *);
bool send_pong_v2(connection_t *c, int, int);
extern bool send_add_subnet(struct connection_t *, const struct subnet_t *);
extern bool send_del_subnet(struct connection_t *, const struct subnet_t *);
extern bool send_add_edge(struct connection_t *, const struct edge_t *);

View file

@ -295,7 +295,8 @@ bool id_h(connection_t *c, const char *request) {
if(name[0] == '^' && !strcmp(name + 1, controlcookie)) {
c->status.control = true;
c->allow_request = CONTROL;
c->last_ping_time = now.tv_sec + 3600;
c->last_ping_time = now;
c->last_ping_time.tv_sec += 3600;
free(c->name);
c->name = xstrdup("<control>");

View file

@ -89,13 +89,26 @@ bool termreq_h(connection_t *c, const char *request) {
bool send_ping(connection_t *c) {
c->status.pinged = true;
c->last_ping_time = now.tv_sec;
c->last_ping_time = now;
return send_request(c, "%d", PING);
return send_request(c, "%d %d %d", PING, c->last_ping_time.tv_sec, c->last_ping_time.tv_usec);
}
bool ping_h(connection_t *c, const char *request) {
return send_pong(c);
int tv_sec, tv_usec, ret;
ret = sscanf(request, "%*d %d %d", &tv_sec, &tv_usec);
if (ret == 2) {
logger(DEBUG_CONNECTIONS, LOG_INFO, "Got PING from %s (%s) %d", c->name, request, ret);
return send_pong_v2(c, tv_sec, tv_usec);
} else {
return send_pong(c);
}
}
bool send_pong_v2(connection_t *c, int tv_sec, int tv_usec) {
return send_request(c, "%d %d %d", PONG, tv_sec, tv_usec);
}
bool send_pong(connection_t *c) {
@ -103,8 +116,37 @@ bool send_pong(connection_t *c) {
}
bool pong_h(connection_t *c, const char *request) {
int current_rtt = 0;
int tv_sec, tv_usec, ret;
struct timeval _now;
c->status.pinged = false;
ret = sscanf(request, "%*d %d %d", &tv_sec, &tv_usec);
gettimeofday(&_now, NULL);
if (ret != 2) {
/* We got PONG from older node */
tv_sec = c->last_ping_time.tv_sec;
tv_usec = c->last_ping_time.tv_usec;
}
/* RTT should be in ms */
current_rtt = (_now.tv_sec - tv_sec)*1000;
/* Compute diff between usec */
current_rtt += _now.tv_usec >= tv_usec ? _now.tv_usec - tv_usec : tv_usec - _now.tv_usec;
current_rtt = current_rtt/1000;
if (c->edge->avg_rtt == 0)
c->edge->avg_rtt = current_rtt;
else
c->edge->avg_rtt = (current_rtt + c->edge->avg_rtt)/2;
if (c->edge->reverse) {
c->edge->reverse->avg_rtt = c->edge->avg_rtt;
}
/* Succesful connection, reset timeout if this is an outgoing connection. */
if(c->outgoing) {

View file

@ -1021,7 +1021,7 @@ static int cmd_dump(int argc, char *argv[]) {
char local_port[4096];
char via[4096];
char nexthop[4096];
int cipher, digest, maclength, compression, distance, socket, weight;
int cipher, digest, maclength, compression, distance, socket, weight, avg_rtt;
short int pmtu, minmtu, maxmtu;
unsigned int options, status_int;
node_status_t status;
@ -1059,8 +1059,8 @@ static int cmd_dump(int argc, char *argv[]) {
} break;
case REQ_DUMP_EDGES: {
int n = sscanf(line, "%*d %*d %s %s %s port %s %s port %s %x %d", from, to, host, port, local_host, local_port, &options, &weight);
if(n != 8) {
int n = sscanf(line, "%*d %*d %s %s %s port %s %s port %s %x %d %d", from, to, host, port, local_host, local_port, &options, &weight, &avg_rtt);
if(n != 9) {
fprintf(stderr, "Unable to parse edge dump from tincd.\n");
return 1;
}
@ -1072,7 +1072,7 @@ static int cmd_dump(int argc, char *argv[]) {
else if(do_graph == 2)
printf(" %s -> %s [w = %f, weight = %f];\n", node1, node2, w, w);
} else {
printf("%s to %s at %s port %s local %s port %s options %x weight %d\n", from, to, host, port, local_host, local_port, options, weight);
printf("%s to %s at %s port %s local %s port %s options %x weight %d avg_rtt %d\n", from, to, host, port, local_host, local_port, options, weight, avg_rtt);
}
} break;