Expand description
Per-source connection rate limiting for all deCDN QUIC protocol handlers.
§Problem
iroh’s Router spawns one task per accepted QUIC connection with no
concurrency cap. Its incoming_filter hook can refuse a connection before
the handshake, but it carries no rate state of its own. A single attacker
can exhaust the node’s task capacity by flooding connections faster than
the idle timeout reaps them.
§Solution
ConnectionLimiter enforces two independent layers at the top of every
deCDN-authored ProtocolHandler::accept implementation:
- Global semaphore — hard cap on total in-flight handler tasks.
- Per-source rate limit — bounds the connection rate from any one
source. Today the source axis is the remote
IpAddr(/64-grouped for IPv6); the layer’s name is intentionally generic so future keying axes (e.g.NodeID, ASN) can land without renaming the operator-visible config and metrics surface.
Relay connections that have no resolvable direct IP at accept time
bypass the per-source layer (no key to charge); the
dispatch_per_source_skipped_no_addr counter records the bypass so
operators chasing rejection anomalies can distinguish “layer didn’t
fire” from “layer wasn’t applicable”.
The keyed rate limiter is provided by the governor crate. Hot-
reload of the rate/burst quota rebuilds the limiter from scratch and
swaps the whole PerSource in through an ArcSwap — the acquire
read path is lock-free, matching the rest of the reload surface
(semaphore is an ArcSwapOption; the reload-fed serve structures
are ArcSwap/atomic). Token-bucket state is not preserved across a
reload (operators changing live quotas should expect the next acquire
from each source to start with a fresh burst budget).
The keyspace prune (retain_recent, an O(n) walk over the keyed map)
never runs on the accept path. An accept does only the constant-time
bucket check plus an O(1) keyspace-length comparison; when the
keyspace grows past cap + cap/10 the accept offloads one single-
flighted retain_recent sweep to a background task, so connection
admission stays constant-time regardless of keyspace size.
Structs§
- Connection
Limiter - Shared rate limiter used by all deCDN-authored
ProtocolHandlerimplementations. - Permit
- RAII permit that holds a semaphore slot for the lifetime of a handler task.
Enums§
- Reject
Reason - Reason a connection was rejected.