summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorheinrich5991 <heinrich5991@gmail.com>2018-10-06 14:15:50 +0200
committerheinrich5991 <heinrich5991@gmail.com>2018-10-12 22:09:24 +0200
commitf5fa1a92ed81ed8da721e803a036b1553a38e39e (patch)
tree46cd97852360b098352612df2477849c35eca836
parentaababc63eeeee1bc41672502ca6c7a1dd9f61d94 (diff)
Add ratelimits for the legacy handshake
There are two sets of variables for this ratelimit First, there are `sv_old_clients_per_interval` and `sv_old_clients_interval` which specify an interval and the number of clients that can join during that time without being ratelimited. If you get ratelimited, you get a one in `sv_old_clients_skip` chance of still continuing the handshake. Together, this should maintain usability while not under attack and still being able to connect while under attack. The defaults are a maximum of 5 connections in 20 seconds without ratelimit and then only accepting every twentieth connection attempt. This comes from the following calculation: A legacy connection request packet consists of 4 bytes UDP data bytes, the response to that weighs at most 57 UDP data bytes. This results in a reflection rate of ~15, so 20 should be rather safe from that. If I add the other protocol headers, I get 42 extra bytes per packet for IPv4 and 62 extra bytes per packet for IPv6 (determined with Wireshark). In these cases, the reflection rate is just around 3 or around 2.7, respectively. So perhaps one could lower `sv_old_clients_skip` furtherly. There's an expected waiting time of `sv_old_clients_skip` / 2 seconds for legitimate clients because those send a connection attempt every 500ms, with a geometric distribution.
-rw-r--r--src/engine/shared/config_variables.h5
-rw-r--r--src/engine/shared/network.h5
-rw-r--r--src/engine/shared/network_server.cpp41
3 files changed, 51 insertions, 0 deletions
diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h
index 3da55d41b..2973e4b77 100644
--- a/src/engine/shared/config_variables.h
+++ b/src/engine/shared/config_variables.h
@@ -83,7 +83,12 @@ MACRO_CONFIG_STR(SvName, sv_name, 128, "unnamed server", CFGFLAG_SERVER, "Server
MACRO_CONFIG_STR(Bindaddr, bindaddr, 128, "", CFGFLAG_CLIENT|CFGFLAG_SERVER|CFGFLAG_MASTER, "Address to bind the client/server to")
MACRO_CONFIG_INT(SvPort, sv_port, 8303, 0, 0, CFGFLAG_SERVER, "Port to use for the server")
MACRO_CONFIG_INT(SvExternalPort, sv_external_port, 0, 0, 0, CFGFLAG_SERVER, "External port to report to the master servers")
+
MACRO_CONFIG_INT(SvAllowOldClients, sv_allow_old_clients, 1, 0, 1, CFGFLAG_SERVER, "Allow clients to connect that do not support the anti-spoof protocol (this presents a DoS risk)")
+MACRO_CONFIG_INT(SvOldClientsPerInterval, sv_old_clients_per_interval, 5, 0, 0, CFGFLAG_SERVER, "Maximum number of clients that can connect per interval set by `sv_old_clients_interval`")
+MACRO_CONFIG_INT(SvOldClientsInterval, sv_old_clients_interval, 20, 0, 0, CFGFLAG_SERVER, "Interval (in seconds) in which `sv_old_clients_per_interval` clients are allowed to connect")
+MACRO_CONFIG_INT(SvOldClientsSkip, sv_old_clients_skip, 20, 0, 0, CFGFLAG_SERVER, "How many legacy connection attempts to ignore before sending a legacy handshake despite the rate limit being hit")
+
MACRO_CONFIG_STR(SvMap, sv_map, 128, "dm1", CFGFLAG_SERVER, "Map to use on the server")
MACRO_CONFIG_INT(SvMaxClients, sv_max_clients, 8, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients that are allowed on a server")
MACRO_CONFIG_INT(SvMaxClientsPerIP, sv_max_clients_per_ip, 4, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients with the same IP that can connect to the server")
diff --git a/src/engine/shared/network.h b/src/engine/shared/network.h
index c265c1ab8..446f6ddaf 100644
--- a/src/engine/shared/network.h
+++ b/src/engine/shared/network.h
@@ -280,6 +280,9 @@ class CNetServer
unsigned char m_aaSalts[2][16];
int64 m_LastSaltUpdate;
+ int64 m_LegacyRatelimitStart;
+ int m_LegacyRatelimitNum;
+
CNetRecvUnpacker m_RecvUnpacker;
unsigned GetToken(const NETADDR &Addr) const;
@@ -290,6 +293,8 @@ class CNetServer
unsigned GetLegacyToken(const NETADDR &Addr, int SaltIndex) const;
bool IsCorrectLegacyToken(const NETADDR &Addr, unsigned LegacyToken) const;
+ bool LegacyRatelimit();
+
public:
int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
diff --git a/src/engine/shared/network_server.cpp b/src/engine/shared/network_server.cpp
index 877a25a07..577e8802d 100644
--- a/src/engine/shared/network_server.cpp
+++ b/src/engine/shared/network_server.cpp
@@ -1,5 +1,6 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
+#include <base/math.h>
#include <base/system.h>
#include <engine/console.h>
@@ -9,6 +10,7 @@
#include "netban.h"
#include "network.h"
+#include <stdlib.h>
bool CNetServer::Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int MaxClientsPerIP, int Flags)
{
@@ -34,6 +36,9 @@ bool CNetServer::Open(NETADDR BindAddr, CNetBan *pNetBan, int MaxClients, int Ma
secure_random_fill(m_aaSalts, sizeof(m_aaSalts));
m_LastSaltUpdate = time_get();
+ m_LegacyRatelimitStart = -1;
+ m_LegacyRatelimitNum = 0;
+
for(int i = 0; i < NET_MAX_CLIENTS; i++)
m_aSlots[i].m_Connection.Init(m_Socket, !g_Config.m_Debug);
@@ -150,6 +155,34 @@ bool CNetServer::IsCorrectLegacyToken(const NETADDR &Addr, unsigned LegacyToken)
return false;
}
+bool CNetServer::LegacyRatelimit()
+{
+ bool Accept = false;
+
+ int Max = g_Config.m_SvOldClientsPerInterval;
+ int Interval = g_Config.m_SvOldClientsInterval;
+ bool UseRatelimit = Max > 0 && Interval > 0;
+ if(UseRatelimit)
+ {
+ int64 Now = time_get();
+ int64 Freq = time_freq();
+ if(m_LegacyRatelimitStart < 0 || m_LegacyRatelimitStart + Interval * Freq <= Now)
+ {
+ m_LegacyRatelimitStart = Now;
+ m_LegacyRatelimitNum = clamp(m_LegacyRatelimitNum - Max, 0, Max);
+ }
+ Accept = m_LegacyRatelimitNum < Max;
+ }
+ if(g_Config.m_SvOldClientsSkip > 0 && (!Accept || !UseRatelimit))
+ {
+ Accept = rand() <= RAND_MAX / g_Config.m_SvOldClientsSkip;
+ }
+ if(Accept && UseRatelimit)
+ {
+ m_LegacyRatelimitNum++;
+ }
+ return !Accept;
+}
/*
@@ -243,6 +276,14 @@ int CNetServer::Recv(CNetChunk *pChunk)
// clients bypass the password check.
else if(g_Config.m_SvAllowOldClients && !g_Config.m_Password[0])
{
+ if(LegacyRatelimit())
+ {
+ if(g_Config.m_Debug)
+ {
+ dbg_msg("netserver", "dropping legacy connect due to ratelimit");
+ }
+ continue;
+ }
CNetPacketConstruct aPackets[2];
unsigned LegacyToken = GetLegacyToken(Addr);