feat(receiver): upstream hardening cherry-picks + test coverage (7855012 + 39e324a) - #18
Merged
Merged
Conversation
unauthenticated reg1 packets create connection groups before any srt handshake, and a global cap of MAX_GROUPS held for GROUP_TIMEOUT (30s) lets an attacker flood ghost groups and lock out the real broadcaster with reg_err. track whether a group has forwarded real srt data (has_seen_data). groups that registered but never streamed are now reaped at PENDING_GROUP_TIMEOUT (5s) instead of 30s, and when the table is full a new registration evicts the oldest ghost group before rejecting. once a group forwards real srt traffic it is promoted and no longer evictable, so active streams and cellular reconnects are unaffected.
srtla_rec relays the srt handshake but never authenticates. detect a rejected connection on the relayed stream and count it per source ip; once an ip crosses AUTH_FAIL_THRESHOLD within AUTH_FAIL_WINDOW its new reg1 registrations are refused for AUTH_FAIL_COOLDOWN. srt-live-server (irl-srt-server) does not send a libsrt handshake rejection: it accepts the handshake, then reads the streamid, runs auth, and on failure closes the socket. so the wire signal for a failed auth is an srt shutdown that arrives before the session is established. mark a group established once the server acks media, and treat a shutdown before that as a failure. the libsrt-native handshake reject (type >= failure base) is also handled as a fallback for other servers. a failed-auth group is torn down immediately after relaying the rejection to the client, reclaiming its slot instead of letting it linger for GROUP_TIMEOUT. safe to remove inside handle_srt_data: the group is held by the by-value shared_ptr param and the main loop stops using stale epoll pointers once the group count shrinks. keys are ip-only so port rotation does not evade. thresholds are lenient (5 fails / 60s, 60s cooldown) so a mistyped key or several broadcasters behind one nat are not locked out. complements ghost group eviction by also covering attackers that complete the handshake before failing auth. also define is_srt_shutdown, which was declared but never implemented.
The two cherry-picked upstream commits (7855012 ghost-group eviction, 39e324a per-IP auth-fail rate limiter) change two receiver behaviors that our fork's pre-existing tests pinned. No upstream logic is altered; only the test harness/fixtures/setup are adapted to the new model: - SRTHandler / SRTLAHandler now take an AuthRateLimiter&. Thread an owned limiter through handler_harness.h and the inline fixture in test_registration_handshake.cpp so they construct under the new signature. - An empty group that never forwarded SRT data is now an evictable "ghost" reaped at PENDING_GROUP_TIMEOUT (5s) rather than GROUP_TIMEOUT (30s), and the (MAX_GROUPS+1)-th REG1 evicts the oldest ghost before returning REG_ERR. The MAX_GROUPS filler (test_group_limits) and the timeout-cleanup groups (test_timeout_cleanup) model established streams, so they are marked data-seen — restoring the "table genuinely full" / GROUP_TIMEOUT semantics those tests were authored against. The ghost-flood/eviction path itself is covered by test_ghost_group_eviction.cpp (unchanged). Full suite: 142/142 green.
Add tests/test_auth_rate_limiter.cpp pinning the per-IP SRT auth-fail throttle from upstream pick 39e324a and the is_srt_shutdown classifier: per-IP failure counting, the 5-in-60s block trip, sliding-window misses, cooldown expiry, IP-only keying (port rotation cannot evade), per-IP independence, stale-entry cleanup reclamation, and SHUTDOWN classification vs other control/data packets. No clock-injection refactor was needed: AuthRateLimiter already takes time_t now on record_failure/is_blocked/cleanup, so the suite drives time by passing values (no real waits; 0 sleep/usleep). The only production change is an additive read-only AuthRateLimiter::tracked_entry_count() so the cleanup-reclamation case is observable; throttling behavior is unchanged. Registered inline with TEST_PREFIX "auth_rate_limiter." so `ctest -R auth_rate_limiter` selects exactly this suite. Full suite 152/152 green.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Affected repo & language: srtla (C/C++)
What
Two upstream receiver-hardening cherry-picks (commits 7855012 + 39e324a from irlserver/main) plus comprehensive test coverage upstream never wrote. The first fix prevents pre-auth group table exhaustion DoS; the second throttles source IPs that repeatedly fail SRT authentication. Both are critical for production receiver stability.
Why
Upstream currency before hardening work. The receiver is exposed to two DoS vectors: (1) an attacker flooding REG1 packets from distinct ports fills the group table with ghost groups, blocking legitimate registrations; (2) an attacker sending malformed SRT handshakes from a single IP exhausts auth attempts, blocking that IP. These fixes close both vectors and are load-bearing for the device's multi-link bonding reliability.
How to verify
cmake -B build -DCMAKE_BUILD_TYPE=Release -DSRTLA_BUILD_TESTS=ON -DBUILD_COMPAT_TESTS=ON && cmake --build build -j$(nproc) && ctest --test-dir build --output-on-failure && tests/compat/run-matrix.sh --tier blocking.omoreferences in tracked filesRisks
Receiver lifecycle changes: group eviction timing and auth failure handling are now stricter. Mitigations: new test suites (16 cases) cover both the happy path and edge cases; oursxours compat smoke test passes (loopback, no netem). The cherry-picks are upstream-authored and tested in irlserver; we adapted the test harness to our fork's architecture (separate commit 962e920).
Checklist
git grep -n '.omo' -- ':!.gitignore'returns nothing (Rule D)