From e6e54763d9654d6c1012172d5af6cc3b4a0e40a3 Mon Sep 17 00:00:00 2001 From: Stephen Bennett Date: Sun, 27 Mar 2011 16:35:26 -0400 Subject: [PATCH] Make flood control settings configurable by those who know exactly what they're doing. From ircd-seven git changeset 29aa4203150337925a4f5c6e7da47be5394c2125 . --- doc/example.conf | 2 +- doc/reference.conf | 20 +++-- include/packet.h | 6 +- include/s_conf.h | 8 +- modules/m_info.c | 218 +++++++++++++++++++++++++-------------------- src/newconf.c | 6 +- src/packet.c | 21 +++-- src/s_conf.c | 13 ++- 8 files changed, 173 insertions(+), 121 deletions(-) diff --git a/doc/example.conf b/doc/example.conf index 558d51e52..1d49aa406 100755 --- a/doc/example.conf +++ b/doc/example.conf @@ -497,7 +497,7 @@ general { disable_auth = no; no_oper_flood = yes; max_targets = 4; - client_flood = 20; + client_flood_max_lines = 20; use_whois_actually = no; oper_only_umodes = operwall, locops, servnotice; oper_umodes = locops, servnotice, operwall, wallop; diff --git a/doc/reference.conf b/doc/reference.conf index 93ee97b32..bed0a2844 100755 --- a/doc/reference.conf +++ b/doc/reference.conf @@ -1167,11 +1167,6 @@ general { */ max_targets = 4; - /* client flood: maximum number of lines in a clients queue before - * they are dropped for flooding. - */ - client_flood = 20; - /* use_whois_actually: send clients requesting a whois a numeric * giving the real IP of non-spoofed clients to prevent DNS abuse. */ @@ -1255,6 +1250,21 @@ general { /* throttle_count: Number of connections within throttle_duration that it takes * for throttling to take effect */ throttle_count = 4; + + /* client flood_max_lines: maximum number of lines in a clients queue before + * they are dropped for flooding. + */ + client_flood_max_lines = 20; + + /* Flood control settings. DO NOT CHANGE THESE without extensive discussion + * and testing by someone who knows exactly what they do. + * + * These settings replicate charybdis-3.3 behaviour. + */ + client_flood_burst_rate = 40; + client_flood_burst_max = 5; + client_flood_message_time = 1; + client_flood_message_num = 2; }; modules { diff --git a/include/packet.h b/include/packet.h index e66dd5281..35f6c41f4 100644 --- a/include/packet.h +++ b/include/packet.h @@ -43,9 +43,13 @@ * just connected. this allows clients to rejoin multiple channels * without being so heavily penalised they excess flood. */ -#define MAX_FLOOD 5 +/* + * spb: Made these configurable + */ +#define MAX_FLOOD ConfigFileEntry.client_flood_burst_max #define MAX_FLOOD_BURST MAX_FLOOD * 8 + extern PF read_packet; extern EVH flood_recalc; extern void flood_endgrace(struct Client *); diff --git a/include/s_conf.h b/include/s_conf.h index df85c359a..10aba3aa2 100644 --- a/include/s_conf.h +++ b/include/s_conf.h @@ -205,7 +205,6 @@ struct config_file_entry int min_nonwildcard; int min_nonwildcard_simple; int default_floodcount; - int client_flood; int default_ident_timeout; int use_egd; int ping_cookie; @@ -225,6 +224,13 @@ struct config_file_entry int global_snotices; int operspy_dont_care_user_info; int use_propagated_bans; + + int client_flood_max_lines; + int client_flood_burst_rate; + int client_flood_burst_max; + int client_flood_message_time; + int client_flood_message_num; + }; struct config_channel_entry diff --git a/modules/m_info.c b/modules/m_info.c index a52b272f0..08e2f4709 100644 --- a/modules/m_info.c +++ b/modules/m_info.c @@ -122,11 +122,35 @@ static struct InfoStruct info_table[] = { "Prepend 'Client Exit:' to user QUIT messages" }, { - "client_flood", + "client_flood_max_lines", OUTPUT_DECIMAL, - &ConfigFileEntry.client_flood, + &ConfigFileEntry.client_flood_max_lines, "Number of lines before a client Excess Flood's", }, + { + "client_flood_burst_rate", + OUTPUT_DECIMAL, + &ConfigFileEntry.client_flood_burst_rate, + "Rate at which burst lines are processed", + }, + { + "client_flood_burst_max", + OUTPUT_DECIMAL, + &ConfigFileEntry.client_flood_burst_max, + "Number of lines to permit at client_flood_burst_rate", + }, + { + "client_flood_message_num", + OUTPUT_DECIMAL, + &ConfigFileEntry.client_flood_message_num, + "Number of messages to allow per client_flood_message_time outside of burst", + }, + { + "client_flood_message_time", + OUTPUT_DECIMAL, + &ConfigFileEntry.client_flood_message_time, + "Time to allow per client_flood_message_num outside of burst", + }, { "connect_timeout", OUTPUT_DECIMAL, @@ -626,9 +650,9 @@ static struct InfoStruct info_table[] = { /* *INDENT-ON* */ /* -** m_info -** parv[1] = servername -*/ + ** m_info + ** parv[1] = servername + */ static int m_info(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { @@ -638,7 +662,7 @@ m_info(struct Client *client_p, struct Client *source_p, int parc, const char *p { /* safe enough to give this on a local connect only */ sendto_one(source_p, form_str(RPL_LOAD2HI), - me.name, source_p->name, "INFO"); + me.name, source_p->name, "INFO"); sendto_one_numeric(source_p, RPL_ENDOFINFO, form_str(RPL_ENDOFINFO)); return 0; } @@ -658,9 +682,9 @@ m_info(struct Client *client_p, struct Client *source_p, int parc, const char *p } /* -** mo_info -** parv[1] = servername -*/ + ** mo_info + ** parv[1] = servername + */ static int mo_info(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { @@ -716,12 +740,12 @@ send_birthdate_online_time(struct Client *source_p) { char tbuf[26]; /* this needs to be 26 - see ctime_r manpage */ sendto_one(source_p, ":%s %d %s :Birth Date: %s, compile # %s", - get_id(&me, source_p), RPL_INFO, - get_id(source_p, source_p), creation, generation); + get_id(&me, source_p), RPL_INFO, + get_id(source_p, source_p), creation, generation); sendto_one(source_p, ":%s %d %s :On-line since %s", - get_id(&me, source_p), RPL_INFO, - get_id(source_p, source_p), rb_ctime(startup_time, tbuf, sizeof(tbuf))); + get_id(&me, source_p), RPL_INFO, + get_id(source_p, source_p), rb_ctime(startup_time, tbuf, sizeof(tbuf))); } /* @@ -746,18 +770,18 @@ send_conf_options(struct Client *source_p) if(infoptr->intvalue) { sendto_one(source_p, ":%s %d %s :%-30s %-5d [%-30s]", - get_id(&me, source_p), RPL_INFO, - get_id(source_p, source_p), - infoptr->name, infoptr->intvalue, - infoptr->desc); + get_id(&me, source_p), RPL_INFO, + get_id(source_p, source_p), + infoptr->name, infoptr->intvalue, + infoptr->desc); } else { sendto_one(source_p, ":%s %d %s :%-30s %-5s [%-30s]", - get_id(&me, source_p), RPL_INFO, - get_id(source_p, source_p), - infoptr->name, infoptr->strvalue, - infoptr->desc); + get_id(&me, source_p), RPL_INFO, + get_id(source_p, source_p), + infoptr->name, infoptr->strvalue, + infoptr->desc); } } @@ -771,95 +795,95 @@ send_conf_options(struct Client *source_p) /* * For "char *" references */ - case OUTPUT_STRING: - { - char *option = *((char **) info_table[i].option); + case OUTPUT_STRING: + { + char *option = *((char **) info_table[i].option); - sendto_one(source_p, ":%s %d %s :%-30s %-5s [%-30s]", - get_id(&me, source_p), RPL_INFO, - get_id(source_p, source_p), - info_table[i].name, - option ? option : "NONE", - info_table[i].desc ? info_table[i].desc : ""); + sendto_one(source_p, ":%s %d %s :%-30s %-5s [%-30s]", + get_id(&me, source_p), RPL_INFO, + get_id(source_p, source_p), + info_table[i].name, + option ? option : "NONE", + info_table[i].desc ? info_table[i].desc : ""); - break; - } - /* - * For "char foo[]" references - */ - case OUTPUT_STRING_PTR: - { - char *option = (char *) info_table[i].option; + break; + } + /* + * For "char foo[]" references + */ + case OUTPUT_STRING_PTR: + { + char *option = (char *) info_table[i].option; - sendto_one(source_p, ":%s %d %s :%-30s %-5s [%-30s]", - get_id(&me, source_p), RPL_INFO, - get_id(source_p, source_p), - info_table[i].name, - EmptyString(option) ? "NONE" : option, - info_table[i].desc ? info_table[i].desc : ""); + sendto_one(source_p, ":%s %d %s :%-30s %-5s [%-30s]", + get_id(&me, source_p), RPL_INFO, + get_id(source_p, source_p), + info_table[i].name, + EmptyString(option) ? "NONE" : option, + info_table[i].desc ? info_table[i].desc : ""); - break; - } - /* - * Output info_table[i].option as a decimal value. - */ - case OUTPUT_DECIMAL: - { - int option = *((int *) info_table[i].option); + break; + } + /* + * Output info_table[i].option as a decimal value. + */ + case OUTPUT_DECIMAL: + { + int option = *((int *) info_table[i].option); - sendto_one(source_p, ":%s %d %s :%-30s %-5d [%-30s]", - get_id(&me, source_p), RPL_INFO, - get_id(source_p, source_p), - info_table[i].name, - option, - info_table[i].desc ? info_table[i].desc : ""); + sendto_one(source_p, ":%s %d %s :%-30s %-5d [%-30s]", + get_id(&me, source_p), RPL_INFO, + get_id(source_p, source_p), + info_table[i].name, + option, + info_table[i].desc ? info_table[i].desc : ""); - break; - } + break; + } - /* - * Output info_table[i].option as "ON" or "OFF" - */ - case OUTPUT_BOOLEAN: - { - int option = *((int *) info_table[i].option); + /* + * Output info_table[i].option as "ON" or "OFF" + */ + case OUTPUT_BOOLEAN: + { + int option = *((int *) info_table[i].option); - sendto_one(source_p, ":%s %d %s :%-30s %-5s [%-30s]", - get_id(&me, source_p), RPL_INFO, - get_id(source_p, source_p), - info_table[i].name, - option ? "ON" : "OFF", - info_table[i].desc ? info_table[i].desc : ""); + sendto_one(source_p, ":%s %d %s :%-30s %-5s [%-30s]", + get_id(&me, source_p), RPL_INFO, + get_id(source_p, source_p), + info_table[i].name, + option ? "ON" : "OFF", + info_table[i].desc ? info_table[i].desc : ""); - break; - } - /* - * Output info_table[i].option as "YES" or "NO" - */ - case OUTPUT_BOOLEAN_YN: - { - int option = *((int *) info_table[i].option); + break; + } + /* + * Output info_table[i].option as "YES" or "NO" + */ + case OUTPUT_BOOLEAN_YN: + { + int option = *((int *) info_table[i].option); - sendto_one(source_p, ":%s %d %s :%-30s %-5s [%-30s]", - get_id(&me, source_p), RPL_INFO, - get_id(source_p, source_p), - info_table[i].name, - option ? "YES" : "NO", - info_table[i].desc ? info_table[i].desc : ""); + sendto_one(source_p, ":%s %d %s :%-30s %-5s [%-30s]", + get_id(&me, source_p), RPL_INFO, + get_id(source_p, source_p), + info_table[i].name, + option ? "YES" : "NO", + info_table[i].desc ? info_table[i].desc : ""); - break; - } + break; + } - case OUTPUT_BOOLEAN2: - { - int option = *((int *) info_table[i].option); + case OUTPUT_BOOLEAN2: + { + int option = *((int *) info_table[i].option); - sendto_one(source_p, ":%s %d %s :%-30s %-5s [%-30s]", - me.name, RPL_INFO, source_p->name, - info_table[i].name, - option ? ((option == 1) ? "MASK" : "YES") : "NO", - info_table[i].desc ? info_table[i].desc : ""); - } /* switch (info_table[i].output_type) */ + sendto_one(source_p, ":%s %d %s :%-30s %-5s [%-30s]", + me.name, RPL_INFO, source_p->name, + info_table[i].name, + option ? ((option == 1) ? "MASK" : "YES") : "NO", + info_table[i].desc ? info_table[i].desc : ""); + } /* switch (info_table[i].output_type) */ } } /* forloop */ diff --git a/src/newconf.c b/src/newconf.c index 2cb8824d4..8f8dd380d 100644 --- a/src/newconf.c +++ b/src/newconf.c @@ -2207,7 +2207,6 @@ static struct ConfEntry conf_general_table[] = { "burst_away", CF_YESNO, NULL, 0, &ConfigFileEntry.burst_away }, { "caller_id_wait", CF_TIME, NULL, 0, &ConfigFileEntry.caller_id_wait }, { "client_exit", CF_YESNO, NULL, 0, &ConfigFileEntry.client_exit }, - { "client_flood", CF_INT, NULL, 0, &ConfigFileEntry.client_flood }, { "collision_fnc", CF_YESNO, NULL, 0, &ConfigFileEntry.collision_fnc }, { "connect_timeout", CF_TIME, NULL, 0, &ConfigFileEntry.connect_timeout }, { "default_floodcount", CF_INT, NULL, 0, &ConfigFileEntry.default_floodcount }, @@ -2252,6 +2251,11 @@ static struct ConfEntry conf_general_table[] = { "use_whois_actually", CF_YESNO, NULL, 0, &ConfigFileEntry.use_whois_actually }, { "warn_no_nline", CF_YESNO, NULL, 0, &ConfigFileEntry.warn_no_nline }, { "use_propagated_bans",CF_YESNO, NULL, 0, &ConfigFileEntry.use_propagated_bans }, + { "client_flood_max_lines", CF_INT, NULL, 0, &ConfigFileEntry.client_flood_max_lines }, + { "client_flood_burst_rate", CF_INT, NULL, 0, &ConfigFileEntry.client_flood_burst_rate }, + { "client_flood_burst_max", CF_INT, NULL, 0, &ConfigFileEntry.client_flood_burst_max }, + { "client_flood_message_num", CF_INT, NULL, 0, &ConfigFileEntry.client_flood_message_num }, + { "client_flood_message_time", CF_INT, NULL, 0, &ConfigFileEntry.client_flood_message_time }, { "\0", 0, NULL, 0, NULL } }; diff --git a/src/packet.c b/src/packet.c index fb8dcabb7..6f14aa927 100644 --- a/src/packet.c +++ b/src/packet.c @@ -38,7 +38,6 @@ static char readBuf[READBUF_SIZE]; static void client_dopacket(struct Client *client_p, char *buffer, size_t length); - /* * parse_client_queued - parse client queued messages */ @@ -124,6 +123,9 @@ parse_client_queued(struct Client *client_p) { if(client_p->localClient->sent_parsed >= client_p->localClient->allow_read) break; + /* spb: Add second layer of throttling to n lines per second, even during burst */ + if(client_p->localClient->actually_read >= ConfigFileEntry.client_flood_burst_rate) + break; } /* allow opers 4 times the amount of messages as users. why 4? @@ -142,7 +144,9 @@ parse_client_queued(struct Client *client_p) client_dopacket(client_p, readBuf, dolen); if(IsAnyDead(client_p)) return; - client_p->localClient->sent_parsed++; + + client_p->localClient->sent_parsed += ConfigFileEntry.client_flood_message_time; + client_p->localClient->actually_read++; } } } @@ -188,15 +192,14 @@ flood_recalc(void *unused) continue; if(IsFloodDone(client_p)) - client_p->localClient->sent_parsed -= 2; + client_p->localClient->sent_parsed -= ConfigFileEntry.client_flood_message_num; else client_p->localClient->sent_parsed = 0; if(client_p->localClient->sent_parsed < 0) client_p->localClient->sent_parsed = 0; - if(--client_p->localClient->actually_read < 0) - client_p->localClient->actually_read = 0; + client_p->localClient->actually_read = 0; parse_client_queued(client_p); @@ -217,8 +220,7 @@ flood_recalc(void *unused) if(client_p->localClient->sent_parsed < 0) client_p->localClient->sent_parsed = 0; - if(--client_p->localClient->actually_read < 0) - client_p->localClient->actually_read = 0; + client_p->localClient->actually_read = 0; parse_client_queued(client_p); } @@ -231,7 +233,6 @@ void read_packet(rb_fde_t * F, void *data) { struct Client *client_p = data; - struct LocalUser *lclient_p = client_p->localClient; int length = 0; int lbuf_len; @@ -288,8 +289,6 @@ read_packet(rb_fde_t * F, void *data) lbuf_len = rb_linebuf_parse(&client_p->localClient->buf_recvq, readBuf, length, binary); - lclient_p->actually_read += lbuf_len; - if(IsAnyDead(client_p)) return; @@ -301,7 +300,7 @@ read_packet(rb_fde_t * F, void *data) /* Check to make sure we're not flooding */ if(!IsAnyServer(client_p) && - (rb_linebuf_alloclen(&client_p->localClient->buf_recvq) > ConfigFileEntry.client_flood)) + (rb_linebuf_alloclen(&client_p->localClient->buf_recvq) > ConfigFileEntry.client_flood_max_lines)) { if(!(ConfigFileEntry.no_oper_flood && IsOper(client_p))) { diff --git a/src/s_conf.c b/src/s_conf.c index e45a0e381..43acbdbe5 100644 --- a/src/s_conf.c +++ b/src/s_conf.c @@ -784,7 +784,6 @@ set_default_conf(void) ConfigFileEntry.min_nonwildcard_simple = 3; ConfigFileEntry.default_floodcount = 8; ConfigFileEntry.default_ident_timeout = 5; - ConfigFileEntry.client_flood = CLIENT_FLOOD_DEFAULT; ConfigFileEntry.tkline_expire_notices = 0; ConfigFileEntry.reject_after_count = 5; @@ -793,6 +792,12 @@ set_default_conf(void) ConfigFileEntry.throttle_count = 4; ConfigFileEntry.throttle_duration = 60; + ConfigFileEntry.client_flood_max_lines = CLIENT_FLOOD_DEFAULT; + ConfigFileEntry.client_flood_burst_rate = 5; + ConfigFileEntry.client_flood_burst_max = 5; + ConfigFileEntry.client_flood_message_time = 1; + ConfigFileEntry.client_flood_message_num = 2; + ServerInfo.default_max_clients = MAXCONNECTIONS; if (!alias_dict) @@ -858,9 +863,9 @@ validate_conf(void) } - if((ConfigFileEntry.client_flood < CLIENT_FLOOD_MIN) || - (ConfigFileEntry.client_flood > CLIENT_FLOOD_MAX)) - ConfigFileEntry.client_flood = CLIENT_FLOOD_MAX; + if((ConfigFileEntry.client_flood_max_lines < CLIENT_FLOOD_MIN) || + (ConfigFileEntry.client_flood_max_lines > CLIENT_FLOOD_MAX)) + ConfigFileEntry.client_flood_max_lines = CLIENT_FLOOD_MAX; if(!split_users || !split_servers || (!ConfigChannel.no_create_on_split && !ConfigChannel.no_join_on_split))