From 2ef39497c745fe6bb60c3fea1c613e7cfb310e05 Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Sat, 14 Sep 2024 20:11:56 +0200 Subject: [PATCH] Similar to previous commit, move maxperip stuff from core to module. This was in src/hash.c, src/list.c and src/modules/stats.c. Now all in src/modules/nick.c... or should this go into a new module? Again, this needs some more testing, like previous commit. --- include/h.h | 7 -- include/struct.h | 9 -- src/hash.c | 96 ----------------- src/list.c | 2 - src/modules/connect-flood.c | 26 ++--- src/modules/nick.c | 208 +++++++++++++++++++++++++++++++++++- src/modules/stats.c | 44 -------- 7 files changed, 220 insertions(+), 172 deletions(-) diff --git a/include/h.h b/include/h.h index db645a065..01806db63 100644 --- a/include/h.h +++ b/include/h.h @@ -436,7 +436,6 @@ extern void del_queries(const char *); #define NICK_HASH_TABLE_SIZE 32768 #define CHAN_HASH_TABLE_SIZE 32768 #define WHOWAS_HASH_TABLE_SIZE 32768 -#define IPUSERS_HASH_TABLE_SIZE 8192 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); @@ -462,12 +461,6 @@ extern Client *hash_find_id(const char *, Client *); extern Client *hash_find_nickatserver(const char *, Client *); extern Channel *find_channel(const char *name); extern Client *hash_find_server(const char *, Client *); -extern IpUsersBucket *find_ipusers_bucket(Client *client); -extern IpUsersBucket *add_ipusers_bucket(Client *client); -extern void decrease_ipusers_bucket(Client *client); -extern MODVAR IpUsersBucket *IpUsersHash_ipv4[IPUSERS_HASH_TABLE_SIZE]; -extern MODVAR IpUsersBucket *IpUsersHash_ipv6[IPUSERS_HASH_TABLE_SIZE]; - /* Mode externs */ diff --git a/include/struct.h b/include/struct.h index cff02afe3..b6aecae4b 100644 --- a/include/struct.h +++ b/include/struct.h @@ -2469,15 +2469,6 @@ extern MODVAR SSL_CTX *ctx_client; #define TLS_PROTOCOL_ALL 0xffff -typedef struct IpUsersBucket IpUsersBucket; -struct IpUsersBucket -{ - IpUsersBucket *prev, *next; - char rawip[16]; - int local_clients; - int global_clients; -}; - typedef struct CoreChannelModeTable CoreChannelModeTable; struct CoreChannelModeTable { long mode; /**< Mode value (which bit will be set) */ diff --git a/src/hash.c b/src/hash.c index e68940ed9..a3c111ace 100644 --- a/src/hash.c +++ b/src/hash.c @@ -264,7 +264,6 @@ static Channel *channelTable[CHAN_HASH_TABLE_SIZE]; static char siphashkey_nick[SIPHASH_KEY_LENGTH]; static char siphashkey_chan[SIPHASH_KEY_LENGTH]; static char siphashkey_whowas[SIPHASH_KEY_LENGTH]; -static char siphashkey_ipusers[SIPHASH_KEY_LENGTH]; extern char unreallogo[]; @@ -276,7 +275,6 @@ void init_hash(void) siphash_generate_key(siphashkey_nick); siphash_generate_key(siphashkey_chan); siphash_generate_key(siphashkey_whowas); - siphash_generate_key(siphashkey_ipusers); for (i = 0; i < NICK_HASH_TABLE_SIZE; i++) INIT_LIST_HEAD(&clientTable[i]); @@ -599,97 +597,3 @@ Client *find_server_by_uid(const char *uid) strlcpy(sid, uid, sizeof(sid)); return hash_find_id(sid, NULL); } - -/**** IP users hash table *****/ - -MODVAR IpUsersBucket *IpUsersHash_ipv4[IPUSERS_HASH_TABLE_SIZE]; -MODVAR IpUsersBucket *IpUsersHash_ipv6[IPUSERS_HASH_TABLE_SIZE]; - -uint64_t hash_ipusers(const char *ip) -{ - return siphash(ip, siphashkey_ipusers) % IPUSERS_HASH_TABLE_SIZE; -} - -IpUsersBucket *find_ipusers_bucket(Client *client) -{ - int hash = 0; - IpUsersBucket *p; - - hash = hash_ipusers(client->ip); - - if (IsIPV6(client)) - { - for (p = IpUsersHash_ipv6[hash]; p; p = p->next) - if (memcmp(p->rawip, client->rawip, 16) == 0) - return p; - } else { - for (p = IpUsersHash_ipv4[hash]; p; p = p->next) - if (memcmp(p->rawip, client->rawip, 4) == 0) - return p; - } - - return NULL; -} - -IpUsersBucket *add_ipusers_bucket(Client *client) -{ - int hash; - IpUsersBucket *n; - - hash = hash_ipusers(client->ip); - - n = safe_alloc(sizeof(IpUsersBucket)); - if (IsIPV6(client)) - { - memcpy(n->rawip, client->rawip, 16); - AddListItem(n, IpUsersHash_ipv6[hash]); - } else { - memcpy(n->rawip, client->rawip, 4); - AddListItem(n, IpUsersHash_ipv4[hash]); - } - return n; -} - -void decrease_ipusers_bucket(Client *client) -{ - int hash = 0; - IpUsersBucket *p; - - if (!(client->flags & CLIENT_FLAG_IPUSERS_BUMPED)) - return; /* nothing to do */ - - client->flags &= ~CLIENT_FLAG_IPUSERS_BUMPED; - - hash = hash_ipusers(client->ip); - - if (IsIPV6(client)) - { - for (p = IpUsersHash_ipv6[hash]; p; p = p->next) - if (memcmp(p->rawip, client->rawip, 16) == 0) - break; - } else { - for (p = IpUsersHash_ipv4[hash]; p; p = p->next) - if (memcmp(p->rawip, client->rawip, 4) == 0) - break; - } - - if (!p) - { - unreal_log(ULOG_INFO, "user", "BUG_DECREASE_IPUSERS_BUCKET", client, - "[BUG] decrease_ipusers_bucket() called but bucket is gone for client $client.details"); - return; - } - - p->global_clients--; - if (MyConnect(client)) - p->local_clients--; - - if ((p->global_clients == 0) && (p->local_clients == 0)) - { - if (IsIPV6(client)) - DelListItem(p, IpUsersHash_ipv6[hash]); - else - DelListItem(p, IpUsersHash_ipv4[hash]); - safe_free(p); - } -} diff --git a/src/list.c b/src/list.c index 31a73383a..c6521a4d2 100644 --- a/src/list.c +++ b/src/list.c @@ -284,8 +284,6 @@ void free_user(Client *client) { RunHook(HOOKTYPE_FREE_USER, client); - decrease_ipusers_bucket(client); - safe_free(client->user->away); if (client->user->swhois) { diff --git a/src/modules/connect-flood.c b/src/modules/connect-flood.c index 0ce5090b1..bfa4bc37e 100644 --- a/src/modules/connect-flood.c +++ b/src/modules/connect-flood.c @@ -65,6 +65,19 @@ MOD_INIT() return MOD_SUCCESS; } +MOD_LOAD() +{ + add_throttling_timeout_timer(modinfo); + return MOD_SUCCESS; +} + +MOD_UNLOAD() +{ + SavePersistentPointer(modinfo, siphashkey_throttling); + SavePersistentPointer(modinfo, ThrottlingHash); + return MOD_SUCCESS; +} + void siphashkey_throttling_free(ModData *m) { safe_free(siphashkey_throttling); @@ -79,19 +92,6 @@ void throttlinghash_free(ModData *m) m->ptr = NULL; } -MOD_LOAD() -{ - add_throttling_timeout_timer(modinfo); - return MOD_SUCCESS; -} - -MOD_UNLOAD() -{ - SavePersistentPointer(modinfo, siphashkey_throttling); - SavePersistentPointer(modinfo, ThrottlingHash); - return MOD_SUCCESS; -} - int connect_flood_throttle(Client *client, int exitflags) { int val; diff --git a/src/modules/nick.c b/src/modules/nick.c index c59a9d959..1c60ada21 100644 --- a/src/modules/nick.c +++ b/src/modules/nick.c @@ -45,8 +45,23 @@ ModuleHeader MOD_HEADER */ #define ASSUME_NICK_IN_FLIGHT +#define IPUSERS_HASH_TABLE_SIZE 8192 + +/* Structs */ +typedef struct IpUsersBucket IpUsersBucket; +struct IpUsersBucket +{ + IpUsersBucket *prev, *next; + char rawip[16]; + int local_clients; + int global_clients; +}; + /* Variables */ static char spamfilter_user[NICKLEN + USERLEN + HOSTLEN + REALLEN + 64]; +IpUsersBucket **IpUsersHash_ipv4 = NULL; +IpUsersBucket **IpUsersHash_ipv6 = NULL; +char *siphashkey_ipusers = NULL; /* Forward declarations */ CMD_FUNC(cmd_nick); @@ -57,6 +72,13 @@ int _register_user(Client *client); void nick_collision(Client *cptr, const char *newnick, const char *newid, Client *new, Client *existing, int type); int AllowClient(Client *client); int exceeds_maxperip(Client *client, ConfigItem_allow *aconf); +void siphashkey_ipusers_free(ModData *m); +void ipusershash_free_4(ModData *m); +void ipusershash_free_6(ModData *m); +IpUsersBucket *add_ipusers_bucket(Client *client); +void decrease_ipusers_bucket(Client *client); +int decrease_ipusers_bucket_wrapper(Client *client); +int stats_maxperip(Client *client, const char *para); MOD_TEST() { @@ -67,9 +89,26 @@ MOD_TEST() MOD_INIT() { + MARK_AS_OFFICIAL_MODULE(modinfo); + + LoadPersistentPointer(modinfo, siphashkey_ipusers, siphashkey_ipusers_free); + if (!siphashkey_ipusers) + { + siphashkey_ipusers = safe_alloc(SIPHASH_KEY_LENGTH); + siphash_generate_key(siphashkey_ipusers); + } + LoadPersistentPointer(modinfo, IpUsersHash_ipv4, ipusershash_free_4); + if (!IpUsersHash_ipv4) + IpUsersHash_ipv4 = safe_alloc(sizeof(IpUsersBucket *) * IPUSERS_HASH_TABLE_SIZE); + LoadPersistentPointer(modinfo, IpUsersHash_ipv6, ipusershash_free_6); + if (!IpUsersHash_ipv6) + IpUsersHash_ipv6 = safe_alloc(sizeof(IpUsersBucket *) * IPUSERS_HASH_TABLE_SIZE); + CommandAdd(modinfo->handle, "NICK", cmd_nick, MAXPARA, CMD_USER|CMD_SERVER|CMD_UNREGISTERED); CommandAdd(modinfo->handle, "UID", cmd_uid, MAXPARA, CMD_SERVER); - MARK_AS_OFFICIAL_MODULE(modinfo); + + HookAdd(modinfo->handle, HOOKTYPE_FREE_USER, 0, decrease_ipusers_bucket_wrapper); + HookAdd(modinfo->handle, HOOKTYPE_STATS, 0, stats_maxperip); return MOD_SUCCESS; } @@ -80,9 +119,176 @@ MOD_LOAD() MOD_UNLOAD() { + SavePersistentPointer(modinfo, siphashkey_ipusers); + SavePersistentPointer(modinfo, IpUsersHash_ipv4); + SavePersistentPointer(modinfo, IpUsersHash_ipv6); return MOD_SUCCESS; } +void siphashkey_ipusers_free(ModData *m) +{ + safe_free(siphashkey_ipusers); + m->ptr = NULL; +} + +void ipusershash_free_4(ModData *m) +{ + // FIXME: need to free every bucket in a for loop + // and then end with this: + safe_free(IpUsersHash_ipv4); + m->ptr = NULL; +} + +void ipusershash_free_6(ModData *m) +{ + // FIXME: need to free every bucket in a for loop + // and then end with this: + safe_free(IpUsersHash_ipv6); + m->ptr = NULL; +} + +uint64_t hash_ipusers(const char *ip) +{ + return siphash(ip, siphashkey_ipusers) % IPUSERS_HASH_TABLE_SIZE; +} + +IpUsersBucket *find_ipusers_bucket(Client *client) +{ + int hash = 0; + IpUsersBucket *p; + + hash = hash_ipusers(client->ip); + + if (IsIPV6(client)) + { + for (p = IpUsersHash_ipv6[hash]; p; p = p->next) + if (memcmp(p->rawip, client->rawip, 16) == 0) + return p; + } else { + for (p = IpUsersHash_ipv4[hash]; p; p = p->next) + if (memcmp(p->rawip, client->rawip, 4) == 0) + return p; + } + + return NULL; +} + +/* (wrapper needed because hook has return type 'int' and function is 'void' */ +int decrease_ipusers_bucket_wrapper(Client *client) +{ + decrease_ipusers_bucket(client); + return 0; +} + +IpUsersBucket *add_ipusers_bucket(Client *client) +{ + int hash; + IpUsersBucket *n; + + hash = hash_ipusers(client->ip); + + n = safe_alloc(sizeof(IpUsersBucket)); + if (IsIPV6(client)) + { + memcpy(n->rawip, client->rawip, 16); + AddListItem(n, IpUsersHash_ipv6[hash]); + } else { + memcpy(n->rawip, client->rawip, 4); + AddListItem(n, IpUsersHash_ipv4[hash]); + } + return n; +} + +void decrease_ipusers_bucket(Client *client) +{ + int hash = 0; + IpUsersBucket *p; + + if (!(client->flags & CLIENT_FLAG_IPUSERS_BUMPED)) + return; /* nothing to do */ + + client->flags &= ~CLIENT_FLAG_IPUSERS_BUMPED; + + hash = hash_ipusers(client->ip); + + if (IsIPV6(client)) + { + for (p = IpUsersHash_ipv6[hash]; p; p = p->next) + if (memcmp(p->rawip, client->rawip, 16) == 0) + break; + } else { + for (p = IpUsersHash_ipv4[hash]; p; p = p->next) + if (memcmp(p->rawip, client->rawip, 4) == 0) + break; + } + + if (!p) + { + unreal_log(ULOG_INFO, "user", "BUG_DECREASE_IPUSERS_BUCKET", client, + "[BUG] decrease_ipusers_bucket() called but bucket is gone for client $client.details"); + return; + } + + p->global_clients--; + if (MyConnect(client)) + p->local_clients--; + + if ((p->global_clients == 0) && (p->local_clients == 0)) + { + if (IsIPV6(client)) + DelListItem(p, IpUsersHash_ipv6[hash]); + else + DelListItem(p, IpUsersHash_ipv4[hash]); + safe_free(p); + } +} + +int stats_maxperip(Client *client, const char *para) +{ + int i; + IpUsersBucket *e; + char ipbuf[256]; + const char *ip; + + /* '/STATS 8' or '/STATS maxperip' is for us... */ + if (strcmp(para, "8") && strcasecmp(para, "maxperip")) + return 0; + + if (!ValidatePermissionsForPath("server:info:stats",client,NULL,NULL,NULL)) + { + sendnumeric(client, ERR_NOPRIVILEGES); + return 0; + } + + sendtxtnumeric(client, "MaxPerIp IPv4 hash table:"); + for (i=0; i < IPUSERS_HASH_TABLE_SIZE; i++) + { + for (e = IpUsersHash_ipv4[i]; e; e = e->next) + { + ip = inetntop(AF_INET, e->rawip, ipbuf, sizeof(ipbuf)); + if (!ip) + ip = ""; + sendtxtnumeric(client, "IPv4 #%d %s: %d local / %d global", + i, ip, e->local_clients, e->global_clients); + } + } + + sendtxtnumeric(client, "MaxPerIp IPv6 hash table:"); + for (i=0; i < IPUSERS_HASH_TABLE_SIZE; i++) + { + for (e = IpUsersHash_ipv6[i]; e; e = e->next) + { + ip = inetntop(AF_INET6, e->rawip, ipbuf, sizeof(ipbuf)); + if (!ip) + ip = ""; + sendtxtnumeric(client, "IPv6 #%d %s: %d local / %d global", + i, ip, e->local_clients, e->global_clients); + } + } + + return 0; +} + /** Hmm.. don't we already have such a function? */ void set_user_modes_dont_spread(Client *client, const char *umode) { diff --git a/src/modules/stats.c b/src/modules/stats.c index 2f9fa6b77..99f4a255a 100644 --- a/src/modules/stats.c +++ b/src/modules/stats.c @@ -84,7 +84,6 @@ int stats_officialchannels(Client *, const char *); int stats_spamfilter(Client *, const char *); int stats_fdtable(Client *, const char *); int stats_linecache(Client *client, const char *para); -int stats_maxperip(Client *, const char *); #define SERVER_AS_PARA 0x1 #define FLAGS_AS_PARA 0x2 @@ -136,7 +135,6 @@ struct statstab StatsTable[] = { { 'v', "denyver", stats_denyver, 0 }, { 'x', "notlink", stats_notlink, 0 }, { 'y', "class", stats_class, 0 }, - { '8', "maxperip", stats_maxperip, 0 }, { '9', "linecache", stats_linecache, 0 }, { 0, NULL, NULL, 0 } }; @@ -1081,45 +1079,3 @@ int stats_linecache(Client *client, const char *para) return 0; } - -int stats_maxperip(Client *client, const char *para) -{ - int i; - IpUsersBucket *e; - char ipbuf[256]; - const char *ip; - - if (!ValidatePermissionsForPath("server:info:stats",client,NULL,NULL,NULL)) - { - sendnumeric(client, ERR_NOPRIVILEGES); - return 0; - } - - sendtxtnumeric(client, "MaxPerIp IPv4 hash table:"); - for (i=0; i < IPUSERS_HASH_TABLE_SIZE; i++) - { - for (e = IpUsersHash_ipv4[i]; e; e = e->next) - { - ip = inetntop(AF_INET, e->rawip, ipbuf, sizeof(ipbuf)); - if (!ip) - ip = ""; - sendtxtnumeric(client, "IPv4 #%d %s: %d local / %d global", - i, ip, e->local_clients, e->global_clients); - } - } - - sendtxtnumeric(client, "MaxPerIp IPv6 hash table:"); - for (i=0; i < IPUSERS_HASH_TABLE_SIZE; i++) - { - for (e = IpUsersHash_ipv6[i]; e; e = e->next) - { - ip = inetntop(AF_INET6, e->rawip, ipbuf, sizeof(ipbuf)); - if (!ip) - ip = ""; - sendtxtnumeric(client, "IPv6 #%d %s: %d local / %d global", - i, ip, e->local_clients, e->global_clients); - } - } - - return 0; -}