Add connection rate limiting.
authorGuus Sliepen <guus@tinc-vpn.org>
Thu, 11 Jul 2013 21:38:38 +0000 (23:38 +0200)
committerGuus Sliepen <guus@tinc-vpn.org>
Thu, 11 Jul 2013 21:38:38 +0000 (23:38 +0200)
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
doc/tinc.texi
src/net.h
src/net_setup.c
src/net_socket.c
src/tincctl.c

index 0376328..69deace 100644 (file)
@@ -335,6 +335,11 @@ This only has effect when
 .Va Mode
 is set to
 .Qq switch .
 .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
 .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
index 08021f9..a39e9ae 100644 (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 only has effect when Mode is set to "switch".
 
 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.
 @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.
 
 pass all traffic, but leaves tinc vulnerable to replay-based attacks on your
 traffic.
 
-
 @cindex StrictSubnets
 @item StrictSubnets <yes|no> (no) [experimental]
 When this option is enabled tinc will only use Subnet statements which are
 @cindex StrictSubnets
 @item StrictSubnets <yes|no> (no) [experimental]
 When this option is enabled tinc will only use Subnet statements which are
index 5f6224e..9a97276 100644 (file)
--- 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 keylifetime;
 extern int udp_rcvbuf;
 extern int udp_sndbuf;
+extern int max_connection_burst;
 extern bool do_prune;
 extern char *myport;
 extern int autoconnect;
 extern bool do_prune;
 extern char *myport;
 extern int autoconnect;
index 8ae1e72..334ea5d 100644 (file)
@@ -711,7 +711,12 @@ static bool setup_myself(void) {
        get_config_bool(lookup_config(config_tree, "TunnelServer"), &tunnelserver);
        strictsubnets |= tunnelserver;
 
        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) {
 
        if(get_config_int(lookup_config(config_tree, "UDPRcvBuf"), &udp_rcvbuf)) {
                if(udp_rcvbuf <= 0) {
index 1b49aee..ded9224 100644 (file)
@@ -45,6 +45,7 @@ int maxtimeout = 900;
 int seconds_till_retry = 5;
 int udp_rcvbuf = 0;
 int udp_sndbuf = 0;
 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;
 
 listen_socket_t listen_socket[MAXSOCKETS];
 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->name = xstrdup("<unknown>");
        c->outcipher = myself->connection->outcipher;
        c = new_connection();
        c->name = xstrdup("<unknown>");
        c->outcipher = myself->connection->outcipher;
index 1183dd7..b3e10c8 100644 (file)
@@ -1296,6 +1296,7 @@ const var_t variables[] = {
        {"KeyExpire", VAR_SERVER},
        {"LocalDiscovery", VAR_SERVER},
        {"MACExpire", VAR_SERVER},
        {"KeyExpire", VAR_SERVER},
        {"LocalDiscovery", VAR_SERVER},
        {"MACExpire", VAR_SERVER},
+       {"MaxConnectionBurst", VAR_SERVER},
        {"MaxOutputBufferSize", VAR_SERVER},
        {"MaxTimeout", VAR_SERVER},
        {"Mode", VAR_SERVER | VAR_SAFE},
        {"MaxOutputBufferSize", VAR_SERVER},
        {"MaxTimeout", VAR_SERVER},
        {"Mode", VAR_SERVER | VAR_SAFE},