From e80c7b5b65347b3dcf0b5bbf298a2c8378501b41 Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Wed, 23 Jun 2021 16:19:45 +0200 Subject: [PATCH] Add set::anti-flood options lag-penalty and lag-penalty-sec. This also allows known-users to execute slightly more commands per second. For people who want their trusted users/bots to allow even more commands per second (eg 20cmds/sec) we now have a nice FAQ item that uses this: https://www.unrealircd.org/docs/FAQ#high-command-rate --- doc/RELEASE-NOTES.md | 8 ++++++++ include/struct.h | 2 ++ src/conf.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ src/modules/stats.c | 8 ++++++++ src/parse.c | 13 +++++++++++- src/user.c | 1 + 6 files changed, 79 insertions(+), 1 deletion(-) diff --git a/doc/RELEASE-NOTES.md b/doc/RELEASE-NOTES.md index 94b135ba3..40a558f9e 100644 --- a/doc/RELEASE-NOTES.md +++ b/doc/RELEASE-NOTES.md @@ -33,6 +33,14 @@ Enhancements: * New [security-group block](https://www.unrealircd.org/docs/Security-group_block) item called *include-mask*. This can be used to put clients matching a [mask](https://www.unrealircd.org/docs/Mask_item) into a security group. +* New option *lag-penalty* and *lag-penalty-bytes* in the + [set::anti-flood block](https://www.unrealircd.org/docs/Anti-flood_settings). + * *known-users* can now executes commands at a slightly faster rate than + *unknown-users*. + * It can further be used to allow really trusted users/bots to execute + commands at even higher rates, such as 20 commands per second, + without making them IRCOp. This explained in + [FAQ: How to allow users to send more commands per second](https://www.unrealircd.org/docs/FAQ#high-command-rate). UnrealIRCd 5.2.0 ----------------- diff --git a/include/struct.h b/include/struct.h index 8047f7c9d..12df11e0d 100644 --- a/include/struct.h +++ b/include/struct.h @@ -1225,6 +1225,7 @@ typedef enum FloodOption { FLD_INVITE = 3, /**< invite-flood */ FLD_KNOCK = 4, /**< knock-flood */ FLD_CONVERSATIONS = 5, /**< max-concurrent-conversations */ + FLD_LAG_PENALTY = 6, /**< lag-penalty / lag-penalty-bytes */ } FloodOption; #define MAXFLOODOPTIONS 10 @@ -1268,6 +1269,7 @@ struct LocalClient { int fd; /**< File descriptor, can be <0 if socket has been closed already. */ SSL *ssl; /**< OpenSSL/LibreSSL struct for SSL/TLS connection */ time_t since; /**< Time when user will next be allowed to send something (actually sincetls_options = safe_alloc(sizeof(TLSOptions)); @@ -7681,6 +7683,8 @@ int _conf_set(ConfigFile *conf, ConfigEntry *ce) else if (!strcmp(cep->ce_varname, "anti-flood")) { for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { + int lag_penalty = -1; + int lag_penalty_bytes = -1; for (ceppp = cepp->ce_entries; ceppp; ceppp = ceppp->ce_next) { if (!strcmp(ceppp->ce_varname, "handshake-data-flood")) @@ -7715,6 +7719,16 @@ int _conf_set(ConfigFile *conf, ConfigEntry *ce) { config_parse_flood_generic(ceppp->ce_vardata, &tempiConf, cepp->ce_varname, FLD_KNOCK); } + else if (!strcmp(ceppp->ce_varname, "lag-penalty")) + { + lag_penalty = atoi(ceppp->ce_vardata); + } + else if (!strcmp(ceppp->ce_varname, "lag-penalty-bytes")) + { + lag_penalty_bytes = config_checkval(ceppp->ce_vardata, CFG_SIZE); + if (lag_penalty_bytes <= 0) + lag_penalty_bytes = INT_MAX; + } else if (!strcmp(ceppp->ce_varname, "connect-flood")) { int cnt, period; @@ -7752,6 +7766,13 @@ int _conf_set(ConfigFile *conf, ConfigEntry *ce) } } } + if ((lag_penalty != -1) && (lag_penalty_bytes != -1)) + { + /* We use a hack here to make it fit our storage format */ + char buf[64]; + snprintf(buf, sizeof(buf), "%d:%d", lag_penalty_bytes, lag_penalty); + config_parse_flood_generic(buf, &tempiConf, cepp->ce_varname, FLD_LAG_PENALTY); + } } } else if (!strcmp(cep->ce_varname, "options")) { @@ -8508,6 +8529,9 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce) for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) { + int has_lag_penalty = 0; + int has_lag_penalty_bytes = 0; + /* Test for old options: */ if (flood_option_is_old(cepp->ce_varname)) { @@ -8721,6 +8745,24 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce) errors++; } } + else if (!strcmp(ceppp->ce_varname, "lag-penalty")) + { + int v; + CheckNull(ceppp); + v = atoi(ceppp->ce_vardata); + has_lag_penalty = 1; + if ((v < 0) || (v > 10000)) + { + config_error("%s:%i: set::anti-flood::%s::lag-penalty: value is in milliseconds and should be between 0 and 10000", + ceppp->ce_fileptr->cf_filename, ceppp->ce_varlinenum, cepp->ce_varname); + errors++; + } + } + else if (!strcmp(ceppp->ce_varname, "lag-penalty-bytes")) + { + has_lag_penalty_bytes = 1; + CheckNull(ceppp); + } else if (!strcmp(ceppp->ce_varname, "connect-flood")) { int cnt, period; @@ -8782,6 +8824,12 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce) continue; } } + if (has_lag_penalty+has_lag_penalty_bytes == 1) + { + config_error("%s:%i: set::anti-flood::%s: if you use lag-penalty then you must also add an lag-penalty-bytes item (and vice-versa)", + cepp->ce_fileptr->cf_filename, cepp->ce_varlinenum, cepp->ce_varname); + errors++; + } } /* Now the warnings: */ if (anti_flood_old == 1) diff --git a/src/modules/stats.c b/src/modules/stats.c index bc7309d83..ad0ca147f 100644 --- a/src/modules/stats.c +++ b/src/modules/stats.c @@ -796,6 +796,14 @@ static void stats_set_anti_flood(Client *client, FloodSettings *f) f->name, floodoption_names[i], (int)f->limit[i], pretty_time_val(f->period[i])); } + if (i == FLD_LAG_PENALTY) + { + sendtxtnumeric(client, "anti-flood::%s::lag-penalty: %d msec", + f->name, (int)f->period[i]); + sendtxtnumeric(client, "anti-flood::%s::lag-penalty-bytes: %d", + f->name, + f->limit[i] == INT_MAX ? 0 : (int)f->limit[i]); + } else { sendtxtnumeric(client, "anti-flood::%s::%s: %d per %s", diff --git a/src/parse.c b/src/parse.c index 696695870..b83f1eb53 100644 --- a/src/parse.c +++ b/src/parse.c @@ -573,13 +573,24 @@ static void ban_handshake_data_flooder(Client *client) */ void parse_addlag(Client *client, int cmdbytes) { + FloodSettings *settings = get_floodsettings_for_user(client, FLD_LAG_PENALTY); + if (!IsServer(client) && !IsNoFakeLag(client) && #ifdef FAKELAG_CONFIGURABLE !(client->local->class && (client->local->class->options & CLASS_OPT_NOFAKELAG)) && #endif !ValidatePermissionsForPath("immune:lag",client,NULL,NULL,NULL)) { - client->local->since += (1 + cmdbytes/90); + int lag_penalty = settings->period[FLD_LAG_PENALTY]; + int lag_penalty_bytes = settings->limit[FLD_LAG_PENALTY]; + + client->local->since_msec += (1 + (cmdbytes/lag_penalty_bytes)) * lag_penalty; + + /* This code takes into account not only the msecs we just calculated + * but also any leftover msec from previous lagging up. + */ + client->local->since += (client->local->since_msec / 1000); + client->local->since_msec = client->local->since_msec % 1000; } } diff --git a/src/user.c b/src/user.c index a6236520b..3ba172b0c 100644 --- a/src/user.c +++ b/src/user.c @@ -1086,5 +1086,6 @@ MODVAR char *floodoption_names[] = { "invite-flood", "knock-flood", "max-concurrent-conversations", + "lag-penalty", NULL };