Add connection rate limiting.

Tinc now strictly limits incoming connections from the same host to 1 per
second. For incoming connections from multiple hosts short bursts of incoming
connections are allowed (by default 100), but on average also only 1 connection
per second is allowed.

When an incoming connection exceeds the limit, tinc will keep the connection in
a tarpit; the connection will be kept open but it is ignored completely. Only
one connection is in a tarpit at a time to limit the number of useless open
connections.
This commit is contained in:
Guus Sliepen 2013-07-11 23:38:38 +02:00
parent 2eba793305
commit 24e3ec863e
6 changed files with 62 additions and 2 deletions

View file

@ -335,6 +335,11 @@ This only has effect when
.Va Mode .Va Mode
is set to is set to
.Qq switch . .Qq switch .
.It Va MaxConnectionBurst Li = Ar count Pq 100
This option controls how many connections tinc accepts in quick succession.
If there are more connections than the given number in a short time interval,
tinc will reduce the number of accepted connections to only one per second,
until the burst has passed.
.It Va MaxTimeout Li = Ar seconds Pq 900 .It Va MaxTimeout Li = Ar seconds Pq 900
This is the maximum delay before trying to reconnect to other tinc daemons. This is the maximum delay before trying to reconnect to other tinc daemons.
.It Va Mode Li = router | switch | hub Pq router .It Va Mode Li = router | switch | hub Pq router

View file

@ -1101,6 +1101,13 @@ impossible to crack a single key.
This option controls the amount of time MAC addresses are kept before they are removed. This option controls the amount of time MAC addresses are kept before they are removed.
This only has effect when Mode is set to "switch". This only has effect when Mode is set to "switch".
@cindex MaxConnectionBurst
@item MaxConnectionBurst = <@var{count}> (100)
This option controls how many connections tinc accepts in quick succession.
If there are more connections than the given number in a short time interval,
tinc will reduce the number of accepted connections to only one per second,
until the burst has passed.
@cindex Name @cindex Name
@item Name = <@var{name}> [required] @item Name = <@var{name}> [required]
This is a symbolic name for this connection. This is a symbolic name for this connection.
@ -1182,7 +1189,6 @@ reordering. Setting this to zero will disable replay tracking completely and
pass all traffic, but leaves tinc vulnerable to replay-based attacks on your pass all traffic, but leaves tinc vulnerable to replay-based attacks on your
traffic. traffic.
@cindex StrictSubnets @cindex StrictSubnets
@item StrictSubnets <yes|no> (no) [experimental] @item StrictSubnets <yes|no> (no) [experimental]
When this option is enabled tinc will only use Subnet statements which are When this option is enabled tinc will only use Subnet statements which are

View file

@ -133,6 +133,7 @@ extern io_t unix_socket;
extern int keylifetime; extern int keylifetime;
extern int udp_rcvbuf; extern int udp_rcvbuf;
extern int udp_sndbuf; extern int udp_sndbuf;
extern int max_connection_burst;
extern bool do_prune; extern bool do_prune;
extern char *myport; extern char *myport;
extern int autoconnect; extern int autoconnect;

View file

@ -711,7 +711,12 @@ static bool setup_myself(void) {
get_config_bool(lookup_config(config_tree, "TunnelServer"), &tunnelserver); get_config_bool(lookup_config(config_tree, "TunnelServer"), &tunnelserver);
strictsubnets |= tunnelserver; strictsubnets |= tunnelserver;
if(get_config_int(lookup_config(config_tree, "MaxConnectionBurst"), &max_connection_burst)) {
if(max_connection_burst <= 0) {
logger(DEBUG_ALWAYS, LOG_ERR, "MaxConnectionBurst cannot be negative!");
return false;
}
}
if(get_config_int(lookup_config(config_tree, "UDPRcvBuf"), &udp_rcvbuf)) { if(get_config_int(lookup_config(config_tree, "UDPRcvBuf"), &udp_rcvbuf)) {
if(udp_rcvbuf <= 0) { if(udp_rcvbuf <= 0) {

View file

@ -45,6 +45,7 @@ int maxtimeout = 900;
int seconds_till_retry = 5; int seconds_till_retry = 5;
int udp_rcvbuf = 0; int udp_rcvbuf = 0;
int udp_sndbuf = 0; int udp_sndbuf = 0;
int max_connection_burst = 100;
listen_socket_t listen_socket[MAXSOCKETS]; listen_socket_t listen_socket[MAXSOCKETS];
int listen_sockets; int listen_sockets;
@ -561,6 +562,47 @@ void handle_new_meta_connection(void *data, int flags) {
sockaddrunmap(&sa); sockaddrunmap(&sa);
// Check if we get many connections from the same host
static sockaddr_t prev_sa;
static time_t prev_time;
static int tarpit = -1;
if(tarpit >= 0) {
closesocket(tarpit);
tarpit = -1;
}
if(prev_time == now.tv_sec && !sockaddrcmp_noport(&sa, &prev_sa)) {
// if so, keep the connection open but ignore it completely.
tarpit = fd;
return;
}
memcpy(&prev_sa, &sa, sizeof sa);
prev_time = now.tv_sec;
// Check if we get many connections from different hosts
static int connection_burst;
static int connection_burst_time;
if(now.tv_sec - connection_burst_time > connection_burst)
connection_burst = 0;
else
connection_burst -= now.tv_sec - connection_burst_time;
connection_burst_time = now.tv_sec;
connection_burst++;
if(connection_burst >= max_connection_burst) {
connection_burst = max_connection_burst;
tarpit = fd;
return;
}
// Accept the new connection
c = new_connection(); c = new_connection();
c->name = xstrdup("<unknown>"); c->name = xstrdup("<unknown>");
c->outcipher = myself->connection->outcipher; c->outcipher = myself->connection->outcipher;

View file

@ -1296,6 +1296,7 @@ const var_t variables[] = {
{"KeyExpire", VAR_SERVER}, {"KeyExpire", VAR_SERVER},
{"LocalDiscovery", VAR_SERVER}, {"LocalDiscovery", VAR_SERVER},
{"MACExpire", VAR_SERVER}, {"MACExpire", VAR_SERVER},
{"MaxConnectionBurst", VAR_SERVER},
{"MaxOutputBufferSize", VAR_SERVER}, {"MaxOutputBufferSize", VAR_SERVER},
{"MaxTimeout", VAR_SERVER}, {"MaxTimeout", VAR_SERVER},
{"Mode", VAR_SERVER | VAR_SAFE}, {"Mode", VAR_SERVER | VAR_SAFE},