From 24e3ec863ec463186501f76961c6d4b1dfe122af Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Thu, 11 Jul 2013 23:38:38 +0200 Subject: [PATCH] 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. --- doc/tinc.conf.5.in | 5 +++++ doc/tinc.texi | 8 +++++++- src/net.h | 1 + src/net_setup.c | 7 ++++++- src/net_socket.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/tincctl.c | 1 + 6 files changed, 62 insertions(+), 2 deletions(-) diff --git a/doc/tinc.conf.5.in b/doc/tinc.conf.5.in index 0376328..69deace 100644 --- a/doc/tinc.conf.5.in +++ b/doc/tinc.conf.5.in @@ -335,6 +335,11 @@ This only has effect when .Va Mode is set to .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 This is the maximum delay before trying to reconnect to other tinc daemons. .It Va Mode Li = router | switch | hub Pq router diff --git a/doc/tinc.texi b/doc/tinc.texi index 08021f9..a39e9ae 100644 --- a/doc/tinc.texi +++ b/doc/tinc.texi @@ -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 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 @item Name = <@var{name}> [required] 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 traffic. - @cindex StrictSubnets @item StrictSubnets (no) [experimental] When this option is enabled tinc will only use Subnet statements which are diff --git a/src/net.h b/src/net.h index 5f6224e..9a97276 100644 --- a/src/net.h +++ b/src/net.h @@ -133,6 +133,7 @@ extern io_t unix_socket; extern int keylifetime; extern int udp_rcvbuf; extern int udp_sndbuf; +extern int max_connection_burst; extern bool do_prune; extern char *myport; extern int autoconnect; diff --git a/src/net_setup.c b/src/net_setup.c index 8ae1e72..334ea5d 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -711,7 +711,12 @@ static bool setup_myself(void) { get_config_bool(lookup_config(config_tree, "TunnelServer"), &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(udp_rcvbuf <= 0) { diff --git a/src/net_socket.c b/src/net_socket.c index 1b49aee..ded9224 100644 --- a/src/net_socket.c +++ b/src/net_socket.c @@ -45,6 +45,7 @@ int maxtimeout = 900; int seconds_till_retry = 5; int udp_rcvbuf = 0; int udp_sndbuf = 0; +int max_connection_burst = 100; listen_socket_t listen_socket[MAXSOCKETS]; int listen_sockets; @@ -561,6 +562,47 @@ void handle_new_meta_connection(void *data, int flags) { 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->name = xstrdup(""); c->outcipher = myself->connection->outcipher; diff --git a/src/tincctl.c b/src/tincctl.c index 1183dd7..b3e10c8 100644 --- a/src/tincctl.c +++ b/src/tincctl.c @@ -1296,6 +1296,7 @@ const var_t variables[] = { {"KeyExpire", VAR_SERVER}, {"LocalDiscovery", VAR_SERVER}, {"MACExpire", VAR_SERVER}, + {"MaxConnectionBurst", VAR_SERVER}, {"MaxOutputBufferSize", VAR_SERVER}, {"MaxTimeout", VAR_SERVER}, {"Mode", VAR_SERVER | VAR_SAFE}, -- 2.25.1