Skip to content

Feature/anti dos and metricsImplement Stage 8 (FEC Deduplication) and Stage 9 (Keepalive Feedback) + Security Improvements#11

Open
paraisospelosul wants to merge 4 commits into
irlserver:mainfrom
paraisospelosul:feature/anti-dos-and-metrics
Open

Feature/anti dos and metricsImplement Stage 8 (FEC Deduplication) and Stage 9 (Keepalive Feedback) + Security Improvements#11
paraisospelosul wants to merge 4 commits into
irlserver:mainfrom
paraisospelosul:feature/anti-dos-and-metrics

Conversation

@paraisospelosul

@paraisospelosul paraisospelosul commented Jun 11, 2026

Copy link
Copy Markdown

This PR implements Stage 8 (selective packet deduplication/FEC) and Stage 9 (bidirectional keepalive feedback) for srtla_rec, along with several security and stability improvements:

  • Stage 8 (FEC/Deduplication): Implemented a direct-mapped sequence number cache (8192 slots) in ConnectionGroup to discard duplicate packets without forwarding them to the SRT server, while still acknowledging them to keep the connection alive.
  • Stage 9 (Keepalive Feedback): Implemented 4-byte bidirectional keepalive feedback payload (weight, error points, link status) on keepalive responses.
  • Security & DoS Protection: Added IP-based rate limiting (max 5 REG1/min/IP), pending connection caps (max 50), and aggressive timeouts (5s) for unauthenticated groups.
  • StreamID Validation: Fast lookup via std::unordered_set with dynamic configuration hot-reload via SIGHUP.
  • Telemetry: Atomic JSON metrics writer.

Summary by CodeRabbit

Release Notes

  • New Features

    • Metrics export to JSON file with real-time connection and group statistics
    • Stream ID validation for connection authentication
    • IP-based rate limiting to prevent registration abuse
    • Signal-based graceful shutdown (SIGTERM/SIGINT) and Stream ID configuration hot-reload (SIGHUP)
  • Improvements

    • Adaptive connection timeout based on packet activity
    • SRT packet deduplication to reduce redundant processing

…up counter for metrics, feedback debug log

- Fix rate_limiter.h: check count BEFORE increment (was allowing limit+1 through)
- Fix rate_limiter.h: remove exact count from warn log (info leak to attackers)
- Fix stream_id_validator.h: replace O(n) vector scan with O(1) unordered_set
- Move DEDUP_CACHE_SIZE to receiver_config.h with other tunable constants
- Add dedup_count_ counter to ConnectionGroup for observability
- Add dedup_packets_discarded field to metrics JSON output
- Add debug log showing keepalive feedback values (weight/errors/status)
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@paraisospelosul, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 42 minutes and 35 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 78766e22-fab5-4ce9-a344-5ffb51a14124

📥 Commits

Reviewing files that changed from the base of the PR and between 407f475 and 297cb57.

📒 Files selected for processing (2)
  • src/protocol/srtla_handler.h
  • src/receiver_main.cpp

Walkthrough

This PR implements a comprehensive anti-DoS layer with stream ID validation, IP-based rate limiting, duplicate packet detection, adaptive connection timeouts, and JSON metrics export. It adds signal-driven hot-reload of stream ID allowlists and graceful shutdown signaling.

Changes

Anti-DoS and Stream ID Validation with Metrics Reporting

Layer / File(s) Summary
Security Primitives: Rate Limiter and Stream ID Validator
src/security/rate_limiter.h, src/security/stream_id_validator.h
New RateLimiter tracks per-IP packet counts within time windows, denies requests over limit, and cleans up stale entries. New StreamIdValidator loads an allowlist from file, validates stream IDs, and extracts StreamID from SRT handshake packet headers including extension block parsing.
ConnectionGroup State Extensions: Auth, Stream ID, Dedup Cache
src/connection/connection_group.h, src/connection/connection_group.cpp
ConnectionGroup adds authentication state accessors, stream ID storage, and duplicate packet detection via fixed-size cache indexed by sequence number. Exposes nak_cache() and new anti-DoS/dedup helpers.
Configuration Constants for Anti-DoS and Adaptive Timeouts
src/receiver_config.h
Adds constants for pending group timeout, max unauthenticated group cap, REG1 rate limit parameters, extended active connection timeout, and dedup cache size.
ConnectionRegistry Adaptive Timeouts Based on Connection State
src/connection/connection_registry.cpp
conn_timed_out selects timeout based on packet reception history (active vs. idle). cleanup_inactive applies adaptive group timeout: pending/unauthenticated groups expire faster; authenticated groups use standard timeout.
SRTLAHandler Anti-DoS Integration
src/protocol/srtla_handler.h, src/protocol/srtla_handler.cpp
Handler wires in rate limiting and stream ID validation. process_single_packet triggers authentication for unauthenticated groups and checks for duplicate SRT packets. register_group enforces IP rate limits and pending group cap. handle_keepalive echoes extended telemetry with feedback. New helpers: try_authenticate_group, count_pending_groups, notify_shutdown.
JSON Metrics Writer Infrastructure
src/metrics/metrics_writer.h
New MetricsWriter periodically throttles and outputs group/connection metrics to JSON with atomic file swap. Serializes timestamp, group counts, rate limiter state, per-group auth/stream/dedup info, and per-connection stats (IP/port, packets/loss, RTT/NACK, bitrate, window, recovery status).
Receiver Main: Signal Handling, Hot-Reload, Metrics, Graceful Shutdown
src/receiver_main.cpp
Adds SIGHUP for stream ID allowlist reload and SIGTERM/SIGINT for graceful shutdown. New CLI args --streamid_file and --metrics_file (optional). Conditionally initializes validator and metrics writer. Main loop terminates on shutdown flag and includes per-iteration rate-limiter cleanup, stream ID hot-reload on pending signal, and metrics output. Explicit shutdown sequence calls notify_shutdown() and closes resources.

Sequence Diagram

sequenceDiagram
  participant Client
  participant SRTLAHandler
  participant RateLimiter
  participant StreamIdValidator
  participant ConnectionGroup
  Client->>SRTLAHandler: REG or data packet
  alt is registration
    SRTLAHandler->>RateLimiter: allow(ip, now)
    RateLimiter-->>SRTLAHandler: pass/fail
    SRTLAHandler->>SRTLAHandler: count_pending_groups()
    SRTLAHandler-->>Client: error if over limit
  else is data (unauthenticated group)
    SRTLAHandler->>StreamIdValidator: try_authenticate_group()
    StreamIdValidator->>StreamIdValidator: extract_stream_id(buf)
    StreamIdValidator->>ConnectionGroup: set_authenticated(true)
  end
  alt SRT packet with sequence number
    SRTLAHandler->>ConnectionGroup: is_duplicate_srt_packet(sn)
    ConnectionGroup-->>SRTLAHandler: duplicate?
    alt duplicate
      SRTLAHandler-->>Client: discard
    else not duplicate
      SRTLAHandler->>SRTLAHandler: register_packet()
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • irlserver/srtla#8: Introduces extended keepalive/telemetry support that this PR builds upon for echoing sender connection info with feedback fields.

Poem

🐰 A rabbit hops through anti-DoS walls,
Validating streams as each packet calls,
Rate limits tick with heartbeat care,
Metrics bloom in JSON air,
Signals guide the graceful close—
Security sewn from security prose! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.88% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the PR's main objectives: implementing Stage 8 (FEC Deduplication) and Stage 9 (Keepalive Feedback) plus security improvements. It is specific, clear, and directly related to the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/receiver_main.cpp (1)

288-295: 💤 Low value

Pending group count duplicates SRTLAHandler::count_pending_groups().

The pending group counting logic is duplicated here and in SRTLAHandler::count_pending_groups(). Consider reusing the handler method for consistency.

♻️ Proposed refactor to reuse existing method
     // Metrics: Write JSON metrics file
     if (metrics_writer) {
-      std::size_t pending = 0;
-      for (const auto &g : registry.groups()) {
-        if (!g->is_authenticated()) pending++;
-      }
+      std::size_t pending = srtla_handler.count_pending_groups();
       metrics_writer->write(registry, srtla_handler.rate_limiter(), pending, ts);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/receiver_main.cpp` around lines 288 - 295, The pending group counting
loop duplicates SRTLAHandler::count_pending_groups(); replace the manual loop
that computes `pending` over `registry.groups()` with a call to
`srtla_handler.count_pending_groups()` and pass that value into
`metrics_writer->write(registry, srtla_handler.rate_limiter(), pending, ts)`
(retain use of `metrics_writer`, `registry`, `srtla_handler.rate_limiter()`, and
`ts`) so the handler's single-source-of-truth is reused.
src/metrics/metrics_writer.h (1)

131-145: 💤 Low value

escape_json does not escape all JSON control characters.

The JSON spec requires escaping all control characters (U+0000 through U+001F). Characters like \x00\x08, \x0B, \x0C, \x0E\x1F would produce invalid JSON if present in stream_id. Consider escaping them as \uXXXX.

♻️ Proposed enhancement for full JSON compliance
     static std::string escape_json(const std::string &s) {
         std::string result;
         result.reserve(s.size());
         for (char c : s) {
+            unsigned char uc = static_cast<unsigned char>(c);
             switch (c) {
                 case '"':  result += "\\\""; break;
                 case '\\': result += "\\\\"; break;
                 case '\n': result += "\\n"; break;
                 case '\r': result += "\\r"; break;
                 case '\t': result += "\\t"; break;
-                default:   result += c; break;
+                default:
+                    if (uc < 0x20) {
+                        char buf[8];
+                        snprintf(buf, sizeof(buf), "\\u%04x", uc);
+                        result += buf;
+                    } else {
+                        result += c;
+                    }
+                    break;
             }
         }
         return result;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/metrics/metrics_writer.h` around lines 131 - 145, The escape_json
function currently only handles a subset of escapes; update escape_json to also
convert all control characters U+0000 through U+001F into JSON-style unicode
escapes (e.g. "\u00XX") while preserving the existing special-case escapes for
'"' '\\' '\n' '\r' '\t'; implement this by checking if (unsigned char)c < 0x20
and appending a "\u00" + two-hex-digit representation of the byte (using the
function's characters/loop and result string) so characters like NUL, BEL, VT,
FF, and others are emitted as \u00XX and produce valid JSON.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/connection/connection_registry.cpp`:
- Around line 154-159: Current logic only applies PENDING_GROUP_TIMEOUT when
connections.empty(), allowing unauthenticated groups to persist; change the
removal condition so pending timeout is enforced regardless of attached
connections: replace the single timeout variable and check with an explicit
branch—if !group->is_authenticated() then remove when (group->created_at() +
PENDING_GROUP_TIMEOUT) < current_time; else (group->is_authenticated()) remove
only when connections.empty() && (group->created_at() + GROUP_TIMEOUT) <
current_time. Update the condition around groups_.erase(group_it) (and the
analogous block at the other location) to implement this logic, using
group->is_authenticated(), group->created_at(), PENDING_GROUP_TIMEOUT,
GROUP_TIMEOUT, connections.empty(), and groups_.erase(group_it).

In `@src/metrics/metrics_writer.h`:
- Around line 51-55: The code opens an ofstream named out(tmp_filepath_) and
calls rename() even if writes failed; update the write/flush/close sequence to
detect stream errors before performing atomic rename by checking out.fail() or
out.bad() (and/or checking out.flush() result) after all writes and before
calling rename(); if the stream indicates failure, log a warning with
tmp_filepath_ and abort the rename. Apply the same check-and-abort logic to the
other occurrence referenced (the block around the second ofstream usage at lines
~124-127) so both write paths verify stream state before moving the temporary
file into place.

In `@src/protocol/srtla_handler.cpp`:
- Around line 179-190: The duplicate check currently happens after
register_packet, causing duplicates to be recorded and potentially trigger SRTLA
ACKs; reorder logic so you call group->is_duplicate_srt_packet(sn) before
register_packet(group, conn, sn) and only call register_packet when is_dup is
false. Locate get_srt_sn(...) to obtain sn, then perform is_dup =
group->is_duplicate_srt_packet(sn) and if not duplicate call register_packet;
keep the existing spdlog trace and early return when duplicates are detected to
avoid populating recv_log or generating ACKs.

In `@src/security/rate_limiter.h`:
- Around line 46-48: The deny-path currently calls spdlog::warn on every blocked
request (when entry.count >= REG1_RATE_LIMIT), causing log flooding; modify the
rate-limiter to throttle these logs by storing and checking a per-IP timestamp
or counter in the entry (e.g., add a field like last_warn_ts or
warn_suppressed_count to the RateLimiterEntry), only call spdlog::warn when the
last_warn_ts is older than a configurable interval (or when a sampled threshold
is reached), and update that field when you log; keep the existing return false
behavior and reference entry.count, REG1_RATE_LIMIT and the spdlog::warn call
when implementing the change.

---

Nitpick comments:
In `@src/metrics/metrics_writer.h`:
- Around line 131-145: The escape_json function currently only handles a subset
of escapes; update escape_json to also convert all control characters U+0000
through U+001F into JSON-style unicode escapes (e.g. "\u00XX") while preserving
the existing special-case escapes for '"' '\\' '\n' '\r' '\t'; implement this by
checking if (unsigned char)c < 0x20 and appending a "\u00" + two-hex-digit
representation of the byte (using the function's characters/loop and result
string) so characters like NUL, BEL, VT, FF, and others are emitted as \u00XX
and produce valid JSON.

In `@src/receiver_main.cpp`:
- Around line 288-295: The pending group counting loop duplicates
SRTLAHandler::count_pending_groups(); replace the manual loop that computes
`pending` over `registry.groups()` with a call to
`srtla_handler.count_pending_groups()` and pass that value into
`metrics_writer->write(registry, srtla_handler.rate_limiter(), pending, ts)`
(retain use of `metrics_writer`, `registry`, `srtla_handler.rate_limiter()`, and
`ts`) so the handler's single-source-of-truth is reused.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3dac740c-c017-4eb2-bf49-d536c7f86268

📥 Commits

Reviewing files that changed from the base of the PR and between 2de6dbb and 407f475.

📒 Files selected for processing (10)
  • src/connection/connection_group.cpp
  • src/connection/connection_group.h
  • src/connection/connection_registry.cpp
  • src/metrics/metrics_writer.h
  • src/protocol/srtla_handler.cpp
  • src/protocol/srtla_handler.h
  • src/receiver_config.h
  • src/receiver_main.cpp
  • src/security/rate_limiter.h
  • src/security/stream_id_validator.h

Comment on lines +154 to 159
// Adaptive group timeout:
// - Unauthenticated groups (never validated StreamID): fast 5s timeout
// - Authenticated groups with no connections: standard 30s timeout
int timeout = group->is_authenticated() ? GROUP_TIMEOUT : PENDING_GROUP_TIMEOUT;
if (connections.empty() && (group->created_at() + timeout) < current_time) {
group_it = groups_.erase(group_it);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Pending-group timeout is bypassed while a connection remains attached

At Line 158, PENDING_GROUP_TIMEOUT is only enforced when connections.empty(). That allows unauthenticated groups to outlive the 5s pending window, weakening the anti-DoS contract.

Suggested fix
-        int timeout = group->is_authenticated() ? GROUP_TIMEOUT : PENDING_GROUP_TIMEOUT;
-        if (connections.empty() && (group->created_at() + timeout) < current_time) {
+        const bool pending_expired =
+            !group->is_authenticated() &&
+            (group->created_at() + PENDING_GROUP_TIMEOUT) < current_time;
+
+        const bool authenticated_empty_expired =
+            group->is_authenticated() &&
+            connections.empty() &&
+            (group->created_at() + GROUP_TIMEOUT) < current_time;
+
+        if (pending_expired || authenticated_empty_expired) {
             group_it = groups_.erase(group_it);
             removed_groups++;
-            spdlog::info("[Group: {}] Group removed ({}, timeout={}s)",
+            spdlog::info("[Group: {}] Group removed ({}, timeout={}s)",
                          static_cast<void *>(group.get()),
                          group->is_authenticated() ? "no connections" : "unauthenticated",
-                         timeout);
+                         group->is_authenticated() ? GROUP_TIMEOUT : PENDING_GROUP_TIMEOUT);

Also applies to: 161-164

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/connection/connection_registry.cpp` around lines 154 - 159, Current logic
only applies PENDING_GROUP_TIMEOUT when connections.empty(), allowing
unauthenticated groups to persist; change the removal condition so pending
timeout is enforced regardless of attached connections: replace the single
timeout variable and check with an explicit branch—if !group->is_authenticated()
then remove when (group->created_at() + PENDING_GROUP_TIMEOUT) < current_time;
else (group->is_authenticated()) remove only when connections.empty() &&
(group->created_at() + GROUP_TIMEOUT) < current_time. Update the condition
around groups_.erase(group_it) (and the analogous block at the other location)
to implement this logic, using group->is_authenticated(), group->created_at(),
PENDING_GROUP_TIMEOUT, GROUP_TIMEOUT, connections.empty(), and
groups_.erase(group_it).

Comment on lines +51 to +55
std::ofstream out(tmp_filepath_);
if (!out.is_open()) {
spdlog::warn("[metrics] Cannot open metrics file: {}", tmp_filepath_);
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Check for write errors before atomic rename.

If any write operation fails (disk full, I/O error), the stream's failbit will be set, but the code proceeds to rename() anyway, potentially replacing a valid metrics file with a truncated/corrupted one. Check the stream state before renaming.

🛡️ Proposed fix: check stream state before rename
         out << "\n  ]\n";
         out << "}\n";
         out.close();

+        if (out.fail()) {
+            spdlog::warn("[metrics] Write error to metrics file: {}", tmp_filepath_);
+            std::remove(tmp_filepath_.c_str());
+            return;
+        }
+
         // Atomic rename: guarantees readers never see partial JSON
         std::rename(tmp_filepath_.c_str(), filepath_.c_str());

Also applies to: 124-127

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/metrics/metrics_writer.h` around lines 51 - 55, The code opens an
ofstream named out(tmp_filepath_) and calls rename() even if writes failed;
update the write/flush/close sequence to detect stream errors before performing
atomic rename by checking out.fail() or out.bad() (and/or checking out.flush()
result) after all writes and before calling rename(); if the stream indicates
failure, log a warning with tmp_filepath_ and abort the rename. Apply the same
check-and-abort logic to the other occurrence referenced (the block around the
second ofstream usage at lines ~124-127) so both write paths verify stream state
before moving the temporary file into place.

Comment on lines 179 to +190
int32_t sn = get_srt_sn(const_cast<char *>(buf), n);
bool is_dup = false;
if (sn >= 0) {
is_dup = group->is_duplicate_srt_packet(sn);
register_packet(group, conn, sn);
}

if (is_dup) {
spdlog::trace("[Group: {}] Duplicate SRT packet (SN: {}) discarded",
static_cast<void *>(group.get()), sn);
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Duplicate packet check happens after register_packet is called.

The call to register_packet(group, conn, sn) on line 183 occurs before the duplicate check on line 186. This means duplicates are still registered in recv_log and potentially trigger SRTLA ACKs, defeating the purpose of early discard. Move the duplicate check before registration.

🐛 Proposed fix: check for duplicate before registering
     int32_t sn = get_srt_sn(const_cast<char *>(buf), n);
-    bool is_dup = false;
     if (sn >= 0) {
-        is_dup = group->is_duplicate_srt_packet(sn);
+        if (group->is_duplicate_srt_packet(sn)) {
+            spdlog::trace("[Group: {}] Duplicate SRT packet (SN: {}) discarded",
+                          static_cast<void *>(group.get()), sn);
+            return;
+        }
         register_packet(group, conn, sn);
     }

-    if (is_dup) {
-        spdlog::trace("[Group: {}] Duplicate SRT packet (SN: {}) discarded",
-                      static_cast<void *>(group.get()), sn);
-        return;
-    }
-
     if (!srt_handler_.forward_to_srt_server(group, buf, n)) {
         return;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/protocol/srtla_handler.cpp` around lines 179 - 190, The duplicate check
currently happens after register_packet, causing duplicates to be recorded and
potentially trigger SRTLA ACKs; reorder logic so you call
group->is_duplicate_srt_packet(sn) before register_packet(group, conn, sn) and
only call register_packet when is_dup is false. Locate get_srt_sn(...) to obtain
sn, then perform is_dup = group->is_duplicate_srt_packet(sn) and if not
duplicate call register_packet; keep the existing spdlog trace and early return
when duplicates are detected to avoid populating recv_log or generating ACKs.

Comment on lines +46 to +48
if (entry.count >= REG1_RATE_LIMIT) {
spdlog::warn("[security] Rate limit exceeded for IP {}", ip);
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Throttle deny-path logging to avoid log-flood amplification.

Line 47 logs on every blocked request. Under sustained abuse, this can become a secondary DoS vector (CPU/disk/log pipeline pressure), even though packet handling is dropped.

Suggested fix
 struct RateEntry {
     int count = 0;
     time_t window_start = 0;
+    bool limit_logged = false;
 };
@@
         if ((now - entry.window_start) >= REG1_RATE_WINDOW) {
             entry.count = 0;
             entry.window_start = now;
+            entry.limit_logged = false;
         }
 
         if (entry.count >= REG1_RATE_LIMIT) {
-            spdlog::warn("[security] Rate limit exceeded for IP {}", ip);
+            if (!entry.limit_logged) {
+                spdlog::warn("[security] Rate limit exceeded for IP {}", ip);
+                entry.limit_logged = true;
+            }
             return false;
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/security/rate_limiter.h` around lines 46 - 48, The deny-path currently
calls spdlog::warn on every blocked request (when entry.count >=
REG1_RATE_LIMIT), causing log flooding; modify the rate-limiter to throttle
these logs by storing and checking a per-IP timestamp or counter in the entry
(e.g., add a field like last_warn_ts or warn_suppressed_count to the
RateLimiterEntry), only call spdlog::warn when the last_warn_ts is older than a
configurable interval (or when a sampled threshold is reached), and update that
field when you log; keep the existing return false behavior and reference
entry.count, REG1_RATE_LIMIT and the spdlog::warn call when implementing the
change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant