From c6a38f5069a2c45e1251c5fd0ad56c2a5ca64580 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Wed, 14 Dec 2016 18:00:49 +0100 Subject: [PATCH] clientlog: allow very short rate limiting intervals Support negative token shift to allow coarse rate limiting with intervals down to -19. --- clientlog.c | 27 ++++++++++++++++++++------- doc/chrony.conf.adoc | 4 +++- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/clientlog.c b/clientlog.c index 9dca9f1..1057326 100644 --- a/clientlog.c +++ b/clientlog.c @@ -95,7 +95,7 @@ static unsigned int max_slots; number of tokens spent on response are determined from configured minimum inverval between responses (in log2) and burst length. */ -#define MIN_LIMIT_INTERVAL (-TS_FRAC) +#define MIN_LIMIT_INTERVAL (-15 - TS_FRAC) #define MAX_LIMIT_INTERVAL 12 #define MIN_LIMIT_BURST 1 #define MAX_LIMIT_BURST 255 @@ -105,7 +105,8 @@ static uint16_t max_cmd_tokens; static uint16_t ntp_tokens_per_packet; static uint16_t cmd_tokens_per_packet; -/* Reduction of token rates to avoid overflow of 16-bit counters */ +/* Reduction of token rates to avoid overflow of 16-bit counters. Negative + shift is used for coarse limiting with intervals shorter than -TS_FRAC. */ static int ntp_token_shift; static int cmd_token_shift; @@ -271,10 +272,17 @@ set_bucket_params(int interval, int burst, uint16_t *max_tokens, interval = CLAMP(MIN_LIMIT_INTERVAL, interval, MAX_LIMIT_INTERVAL); burst = CLAMP(MIN_LIMIT_BURST, burst, MAX_LIMIT_BURST); - /* Find smallest shift with which the maximum number fits in 16 bits */ - for (*token_shift = 0; *token_shift < interval + TS_FRAC; (*token_shift)++) { - if (burst << (TS_FRAC + interval - *token_shift) < 1U << 16) - break; + if (interval >= -TS_FRAC) { + /* Find the smallest shift with which the maximum number fits in 16 bits */ + for (*token_shift = 0; *token_shift < interval + TS_FRAC; (*token_shift)++) { + if (burst << (TS_FRAC + interval - *token_shift) < 1U << 16) + break; + } + } else { + /* Coarse rate limiting */ + *token_shift = interval + TS_FRAC; + *tokens_per_packet = 1; + burst = MAX(1U << -*token_shift, burst); } *tokens_per_packet = 1U << (TS_FRAC + interval - *token_shift); @@ -369,7 +377,12 @@ update_record(struct timespec *now, uint32_t *last_hit, uint32_t *hits, if (prev_hit == INVALID_TS || (int32_t)interval < 0) return; - new_tokens = (now_ts >> token_shift) - (prev_hit >> token_shift); + if (token_shift >= 0) + new_tokens = (now_ts >> token_shift) - (prev_hit >> token_shift); + else if (now_ts - prev_hit > max_tokens) + new_tokens = max_tokens; + else + new_tokens = (now_ts - prev_hit) << -token_shift; *tokens = MIN(*tokens + new_tokens, max_tokens); /* Convert the interval to scaled and rounded log2 */ diff --git a/doc/chrony.conf.adoc b/doc/chrony.conf.adoc index 4aea902..d7710ad 100644 --- a/doc/chrony.conf.adoc +++ b/doc/chrony.conf.adoc @@ -1238,7 +1238,9 @@ in any order): *interval*::: This option sets the minimum interval between responses. It is defined as a power of 2 in seconds. The default value is 3 (8 seconds). The minimum value -is -4 and the maximum value is 12. +is -19 and the maximum value is 12. Note that with values below -4 the rate +limiting is coarse (responses are allowed in bursts, even if the interval +between them is shorter than the specified interval). *burst*::: This option sets the maximum number of responses that can be sent in a burst, temporarily exceeding the limit specified by the *interval* option. This is