Make throttling code (connect-flood) use SipHash and increase the

hash table size from 1019 to 8192 to have fewer collisions.
This commit is contained in:
Bram Matthys 2019-06-26 16:46:28 +02:00
parent d5395848b7
commit f1b0b6b76e
No known key found for this signature in database
GPG key ID: BF8116B163EAAE98
5 changed files with 60 additions and 35 deletions

View file

@ -369,6 +369,7 @@ extern time_t expire_cache(time_t);
extern void del_queries(char *);
extern uint64_t siphash(const char *in, const char *k);
extern uint64_t siphash_raw(const char *in, size_t len, const char *k);
extern uint64_t siphash_nocase(const char *in, const char *k);
extern void siphash_generate_key(char *k);
extern void init_hash(void);
@ -390,6 +391,7 @@ extern aClient *hash_find_client(const char *, aClient *);
extern aClient *hash_find_id(const char *, aClient *);
extern aClient *hash_find_nickatserver(const char *, aClient *);
extern aClient *hash_find_server(const char *, aClient *);
extern struct MODVAR ThrottlingBucket *ThrottlingHash[THROTTLING_HASH_TABLE_SIZE];
extern char *find_by_aln(char *);
extern char *convert2aln(int);
extern int convertfromaln(char *);

View file

@ -26,6 +26,7 @@
#define CHAN_HASH_TABLE_SIZE 32768
#define WATCH_HASH_TABLE_SIZE 32768
#define WHOWAS_HASH_TABLE_SIZE 32768
#define THROTTLING_HASH_TABLE_SIZE 8192
typedef struct hashentry {
int hits;
@ -33,11 +34,6 @@ typedef struct hashentry {
void *list;
} aHashEntry;
/*
* Throttling
*/
#define THROTTLING_HASH_SIZE 1019 /* prime number */
#define NullChn ((aChannel *)0)
#define find_channel hash_find_channel

View file

@ -1852,10 +1852,10 @@ struct PendingNet {
aPendingServer *servers; /**< The list of servers connected to the client */
};
void init_throttling_hash();
struct ThrottlingBucket *find_throttling_bucket(aClient *);
void add_throttling_bucket(aClient *);
int throttle_can_connect(aClient *);
extern void init_throttling();
extern struct ThrottlingBucket *find_throttling_bucket(aClient *);
extern void add_throttling_bucket(aClient *);
extern int throttle_can_connect(aClient *);
typedef struct _maxtargets MaxTarget;
struct _maxtargets {

View file

@ -19,7 +19,7 @@
#include "unrealircd.h"
/* Next #define's, the siphash() and siphash_nocase() functions are based
/* Next #define's, the siphash_raw() and siphash_nocase() functions are based
* on the SipHash reference C implementation to which the following applies:
* Copyright (c) 2012-2016 Jean-Philippe Aumasson
* <jeanphilippe.aumasson@gmail.com>
@ -86,19 +86,23 @@
v2 = ROTL(v2, 32); \
} while (0)
/** Generic hash function in UnrealIRCd.
* @param str The string to hash (NUL-terminated)
/** Generic hash function in UnrealIRCd - raw version.
* Note that you probably want siphash() or siphash_nocase() instead.
* @param in The data to hash
* @param inlen The length of the data
* @param k The key to use for hashing (16 bytes, not NUL terminated)
* @returns Hash result as a 64 bit unsigned integer.
* @notes The key (k) should be random and must stay the same for
* as long as you use the function for your specific hash table.
* Simply use the following on boot: siphash_generate_key(k);
*
* This siphash_raw() version is meant for non-strings,
* such as raw IP address structs and such.
*/
uint64_t siphash(const char *in, const char *k)
uint64_t siphash_raw(const char *in, size_t inlen, const char *k)
{
uint64_t hash;
char *out = (char*) &hash;
size_t inlen = strlen(in);
uint64_t v0 = 0x736f6d6570736575ULL;
uint64_t v1 = 0x646f72616e646f6dULL;
uint64_t v2 = 0x6c7967656e657261ULL;
@ -224,6 +228,22 @@ uint64_t siphash_nocase(const char *in, const char *k)
return hash;
}
/* End of imported code */
/** Generic hash function in UnrealIRCd.
* @param str The string to hash (NUL-terminated)
* @param k The key to use for hashing (16 bytes, not NUL terminated)
* @returns Hash result as a 64 bit unsigned integer.
* @notes The key (k) should be random and must stay the same for
* as long as you use the function for your specific hash table.
* Simply use the following on boot: siphash_generate_key(k);
*/
uint64_t siphash(const char *in, const char *k)
{
size_t inlen = strlen(in);
return siphash_raw(in, inlen, k);
}
/** Generate a key that is used by siphash() and siphash_nocase().
* @param k The key, this must be a char array of size 16.
*/
@ -243,6 +263,7 @@ static char siphashkey_nick[16];
static char siphashkey_chan[16];
static char siphashkey_watch[16];
static char siphashkey_whowas[16];
static char siphashkey_throttling[16];
extern char unreallogo[];
@ -255,6 +276,7 @@ void init_hash(void)
siphash_generate_key(siphashkey_chan);
siphash_generate_key(siphashkey_watch);
siphash_generate_key(siphashkey_whowas);
siphash_generate_key(siphashkey_throttling);
for (i = 0; i < NICK_HASH_TABLE_SIZE; i++)
INIT_LIST_HEAD(&clientTable[i]);
@ -265,6 +287,12 @@ void init_hash(void)
memset(channelTable, 0, sizeof(channelTable));
memset(watchTable, 0, sizeof(watchTable));
bzero(ThrottlingHash, sizeof(ThrottlingHash));
/* do not call init_throttling() here, as
* config file has not been read yet.
* The hash table is ready, anyway.
*/
if (strcmp(BASE_VERSION, &unreallogo[337]))
loop.tainted = 1;
}
@ -861,20 +889,20 @@ int hash_del_watch_list(aClient *cptr)
return 0;
}
/*
* Throttling
* -by Stskeeps
*/
/* Throttling - originally by Stskeeps */
struct MODVAR ThrottlingBucket *ThrottlingHash[THROTTLING_HASH_SIZE+1];
/* Note that we call this set::anti-flood::connect-flood nowadays */
void init_throttling_hash()
struct MODVAR ThrottlingBucket *ThrottlingHash[THROTTLING_HASH_TABLE_SIZE];
void init_throttling()
{
long v;
bzero(ThrottlingHash, sizeof(ThrottlingHash));
long v;
if (!THROTTLING_PERIOD)
{
v = 120;
else
} else
{
v = THROTTLING_PERIOD/2;
if (v > 5)
@ -883,12 +911,12 @@ long v;
EventAdd(NULL, "bucketcleaning", v, 0, e_clean_out_throttling_buckets, NULL);
}
int hash_throttling(char *ip)
u_int64_t hash_throttling(char *ip)
{
return hash_client_name(ip) %THROTTLING_HASH_SIZE; // TODO: improve/fix ;)
return siphash(ip, siphashkey_throttling) % THROTTLING_HASH_TABLE_SIZE;
}
struct ThrottlingBucket *find_throttling_bucket(aClient *acptr)
struct ThrottlingBucket *find_throttling_bucket(aClient *acptr)
{
int hash = 0;
struct ThrottlingBucket *p;
@ -909,7 +937,7 @@ EVENT(e_clean_out_throttling_buckets)
int i;
static time_t t = 0;
for (i = 0; i < THROTTLING_HASH_SIZE; i++)
for (i = 0; i < THROTTLING_HASH_TABLE_SIZE; i++)
{
for (n = ThrottlingHash[i]; n; n = n_next)
{
@ -952,9 +980,9 @@ EVENT(e_clean_out_throttling_buckets)
void add_throttling_bucket(aClient *acptr)
{
int hash;
struct ThrottlingBucket *n;
int hash;
struct ThrottlingBucket *n;
n = MyMallocEx(sizeof(struct ThrottlingBucket));
n->next = n->prev = NULL;
n->ip = strdup(acptr->ip);
@ -965,13 +993,13 @@ void add_throttling_bucket(aClient *acptr)
return;
}
/** Checks wether the user is connect-flooding.
/** Checks whether the user is connect-flooding.
* @retval 0 Denied, throttled.
* @retval 1 Allowed, but known in the list.
* @retval 2 Allowed, not in list or is an exception.
* @see add_connection()
*/
int throttle_can_connect(aClient *sptr)
int throttle_can_connect(aClient *sptr)
{
struct ThrottlingBucket *b;

View file

@ -743,7 +743,6 @@ static void do_version_check()
extern void applymeblock(void);
extern MODVAR Event *events;
extern struct MODVAR ThrottlingBucket *ThrottlingHash[THROTTLING_HASH_SIZE+1];
/** This functions resets a couple of timers and does other things that
* are absolutely cruicial when the clock is adjusted - particularly
@ -815,7 +814,7 @@ void fix_timers(void)
* sonner than we should.
*/
cnt = 0;
for (i = 0; i < THROTTLING_HASH_SIZE; i++)
for (i = 0; i < THROTTLING_HASH_TABLE_SIZE; i++)
{
for (thr = ThrottlingHash[i]; thr; thr = thr->next)
{
@ -1383,7 +1382,7 @@ int InitUnrealIRCd(int argc, char *argv[])
fix_timers();
write_pidfile();
Debug((DEBUG_NOTICE, "Server ready..."));
init_throttling_hash();
init_throttling();
loop.ircd_booted = 1;
#if defined(HAVE_SETPROCTITLE)
setproctitle("%s", me.name);