From 20003a0f923b5b107b47b406d8134dded1367dc3 Mon Sep 17 00:00:00 2001 From: Bernhard Kirchen Date: Fri, 27 Jun 2025 22:01:13 +0200 Subject: [PATCH] syslog: implement rate limiting enabling a lot of logging messages, e.g., by setting verbose as the default level, can make the system unresponsive if syslogging is enabled. to protect against this, a rate limiting algorithm is implemented in the syslogger. --- include/SyslogLogger.h | 9 +++++++++ src/SyslogLogger.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/include/SyslogLogger.h b/include/SyslogLogger.h index f38f3730..6b6472ff 100644 --- a/include/SyslogLogger.h +++ b/include/SyslogLogger.h @@ -20,6 +20,7 @@ private: return _address != INADDR_NONE; } static uint8_t calculatePrival(uint8_t facility, char errorCode); + bool consumeToken(); Task _loopTask; std::mutex _mutex; @@ -30,6 +31,14 @@ private: String _header; uint16_t _port; bool _enabled; + + static constexpr uint32_t RATE_LIMIT_WINDOW_MS = 1000; + static constexpr size_t RATE_LIMIT_MAX_TOKENS = 100; + size_t _available_tokens = RATE_LIMIT_MAX_TOKENS; + uint32_t _last_token_refill_millis = 0; + size_t _rate_limited_packets = 0; + uint32_t _last_rate_limit_warning_millis = 0; + static constexpr uint32_t RATE_LIMIT_WARNING_INTERVAL_MS = 1000; }; extern SyslogLogger Syslog; diff --git a/src/SyslogLogger.cpp b/src/SyslogLogger.cpp index 9b0a49e2..5866b5c0 100644 --- a/src/SyslogLogger.cpp +++ b/src/SyslogLogger.cpp @@ -8,6 +8,7 @@ #include "defaults.h" #include #include +#include #undef TAG static const char* TAG = "syslog"; @@ -65,6 +66,27 @@ void SyslogLogger::write(const uint8_t* buffer, size_t size) return; } + // Check rate limiting using token bucket + if (!consumeToken()) { + if (_rate_limited_packets == 0) { + _last_rate_limit_warning_millis = millis(); + } + ++_rate_limited_packets; + return; + } + + if (_rate_limited_packets > 0) { + uint32_t elapsed = (millis() - _last_rate_limit_warning_millis); + if (elapsed > RATE_LIMIT_WARNING_INTERVAL_MS) { + char buffer[128]; + snprintf(buffer, sizeof(buffer), "Rate limited %d packets in the last %dms", + _rate_limited_packets, elapsed); + Serial.println(buffer); + _rate_limited_packets = 0; + _last_rate_limit_warning_millis = millis(); + } + } + String header = "<"; header += String(calculatePrival(1, buffer[0])); @@ -82,6 +104,26 @@ void SyslogLogger::write(const uint8_t* buffer, size_t size) _udp.endPacket(); } +bool SyslogLogger::consumeToken() +{ + uint32_t now = millis(); + + uint32_t elapsed = now - _last_token_refill_millis; + size_t new_tokens = RATE_LIMIT_MAX_TOKENS * elapsed / RATE_LIMIT_WINDOW_MS; + + if (new_tokens > 0) { + _available_tokens = std::min(_available_tokens + new_tokens, RATE_LIMIT_MAX_TOKENS); + _last_token_refill_millis = now; + } + + if (_available_tokens > 0) { + --_available_tokens; + return true; + } + + return false; +} + void SyslogLogger::disable() { ESP_LOGI(TAG, "Disable"); @@ -103,6 +145,8 @@ void SyslogLogger::enable() std::lock_guard lock(_mutex); _enabled = true; + _available_tokens = RATE_LIMIT_MAX_TOKENS; + _last_token_refill_millis = millis(); } bool SyslogLogger::resolveAndStart()