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 .
+.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
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".
 
+@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 <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 max_connection_burst;
 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;
 
-
+       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) {
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 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("<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},
+       {"MaxConnectionBurst", VAR_SERVER},
        {"MaxOutputBufferSize", VAR_SERVER},
        {"MaxTimeout", VAR_SERVER},
        {"Mode", VAR_SERVER | VAR_SAFE},