mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-22 07:48:26 +00:00
Update design docs for replay suppression, MMP backoff, and flap dampening
- fips-configuration.md: add flap_threshold, flap_window_secs, flap_dampening_secs to tree config table and YAML reference - fips-spanning-tree.md: add flap dampening to Stability Mechanisms, Timing Parameters, and Implementation Status tables - spanning-tree-dynamics.md: update Known Limitations to reflect flap dampening implementation, revise impact text - fips-session-layer.md: add Send Failure Backoff subsection under Session-Layer MMP - fips-mesh-layer.md: add log suppression note to Replay Protection
This commit is contained in:
@@ -151,6 +151,9 @@ Controls tree construction and parent selection.
|
||||
| `node.tree.parent_hysteresis` | f64 | `0.2` | Cost improvement fraction required for same-root parent switch (0.0–1.0) |
|
||||
| `node.tree.hold_down_secs` | u64 | `30` | Suppress non-mandatory re-evaluation after parent switch |
|
||||
| `node.tree.reeval_interval_secs` | u64 | `60` | Periodic cost-based parent re-evaluation interval (0 = disabled) |
|
||||
| `node.tree.flap_threshold` | u32 | `4` | Parent switches in window before dampening engages |
|
||||
| `node.tree.flap_window_secs` | u64 | `60` | Sliding window for counting parent switches |
|
||||
| `node.tree.flap_dampening_secs` | u64 | `120` | Extended hold-down duration when flap threshold exceeded |
|
||||
|
||||
### Bloom Filter (`node.bloom.*`)
|
||||
|
||||
@@ -351,6 +354,9 @@ node:
|
||||
parent_hysteresis: 0.2 # cost improvement fraction for parent switch
|
||||
hold_down_secs: 30 # suppress re-evaluation after switch
|
||||
reeval_interval_secs: 60 # periodic cost-based re-evaluation (0 = disabled)
|
||||
flap_threshold: 4 # parent switches before dampening
|
||||
flap_window_secs: 60 # sliding window for flap detection
|
||||
flap_dampening_secs: 120 # extended hold-down on flap
|
||||
bloom:
|
||||
update_debounce_ms: 500
|
||||
session:
|
||||
|
||||
@@ -100,6 +100,7 @@ Every peer connection begins with a Noise IK handshake that mutually
|
||||
authenticates both parties and establishes symmetric keys for link encryption.
|
||||
|
||||
The IK pattern is chosen because:
|
||||
|
||||
- The **initiator** knows the responder's static public key from configuration
|
||||
or discovery, and sends their own static key encrypted in the first message
|
||||
- The **responder** learns the initiator's identity from the first message,
|
||||
@@ -241,6 +242,7 @@ receiver index — a random 32-bit value chosen by the receiver during the
|
||||
handshake. This enables O(1) lookup without relying on source addresses.
|
||||
|
||||
Each party in a link has two indices:
|
||||
|
||||
- **our_index**: Chosen by us, included by the peer in packets sent to us
|
||||
- **their_index**: Chosen by them, included by us in packets sent to them
|
||||
|
||||
@@ -307,6 +309,12 @@ than the window are rejected.
|
||||
The replay check is performed before decryption to prevent CPU exhaustion from
|
||||
replayed packets that would pass the index lookup but fail decryption.
|
||||
|
||||
During link transitions, stale packets from a previous Noise session arrive
|
||||
encrypted with old counters and are correctly rejected. To avoid excessive log
|
||||
volume from these benign bursts, replay detection logging is suppressed after
|
||||
the first 3 occurrences per peer. A summary is emitted when the peer's session
|
||||
is re-established or the peer is removed.
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
Handshake initiation (msg1) is the primary attack surface for unauthenticated
|
||||
@@ -314,6 +322,7 @@ traffic. Each msg1 requires Noise DH operations (~200µs on modern CPUs),
|
||||
state allocation, and response generation.
|
||||
|
||||
FMP uses a global token bucket rate limiter:
|
||||
|
||||
- **Burst capacity**: Handles legitimate connection storms (e.g., node restart
|
||||
with many configured peers)
|
||||
- **Sustained rate**: Limits steady-state new connections per second
|
||||
@@ -321,6 +330,7 @@ FMP uses a global token bucket rate limiter:
|
||||
source addresses are trivially spoofable
|
||||
|
||||
Additional protections:
|
||||
|
||||
- **Connection limit**: Maximum number of pending inbound handshakes, capping
|
||||
memory usage
|
||||
- **Handshake timeout**: Stale pending handshakes are cleaned up after a
|
||||
|
||||
@@ -497,6 +497,20 @@ feedback:
|
||||
5. Notifications are sent on first measurement, on any decrease, and
|
||||
periodically at `max(10s, 5 × SRTT)`.
|
||||
|
||||
### Send Failure Backoff
|
||||
|
||||
When a session MMP report cannot be delivered (destination unreachable, no
|
||||
route), the sender applies exponential backoff to the probe interval:
|
||||
|
||||
- Each consecutive failure doubles the interval: 2x, 4x, 8x, 16x, 32x
|
||||
- Backoff caps at 32x the base interval (5 consecutive failures)
|
||||
- A successful send resets to the normal SRTT-based interval
|
||||
- Debug logging is suppressed after 3 consecutive failures; a summary is
|
||||
logged when the destination becomes reachable again
|
||||
|
||||
This prevents wasted CPU and log noise when a session's remote endpoint has
|
||||
departed the network but the local session has not yet timed out.
|
||||
|
||||
### Idle Timeout Interaction
|
||||
|
||||
MMP reports (SenderReport, ReceiverReport) and PathMtuNotification do **not**
|
||||
@@ -525,7 +539,7 @@ MMP session metrics session=npub1tdwa...84le rtt=4.3ms loss=0.6% jitter=0.2ms go
|
||||
| Explicit counter replay protection | **Implemented** |
|
||||
| Hybrid coordinate warmup (CP + CoordsWarmup) | **Implemented** |
|
||||
| FSP wire format (prefix, AAD, inner header) | **Implemented** |
|
||||
| Session-layer MMP | **Implemented** |
|
||||
| Session-layer MMP (with send-failure backoff) | **Implemented** |
|
||||
| Identity cache (LRU-only) | **Implemented** |
|
||||
| Coordinate cache (unified, TTL + refresh) | **Implemented** |
|
||||
| Session idle timeout | **Implemented** |
|
||||
|
||||
@@ -71,6 +71,13 @@ immediate parent reselection:
|
||||
Re-evaluates parent selection using current MMP link costs, independent
|
||||
of TreeAnnounce traffic. This catches link degradation after the tree
|
||||
has stabilized and TreeAnnounce gossip has stopped.
|
||||
- **Flap dampening** (`flap_threshold` / `flap_window_secs` /
|
||||
`flap_dampening_secs`): If a node switches parents more than
|
||||
`flap_threshold` times (default 4) within `flap_window_secs` (default
|
||||
60s), an extended hold-down of `flap_dampening_secs` (default 120s) is
|
||||
imposed. This reduces TreeAnnounce storms from link flapping without
|
||||
delaying legitimate reconvergence. Mandatory switches (parent loss,
|
||||
root change) bypass dampening.
|
||||
- **Local-only metrics**: Link costs use only locally measured MMP data
|
||||
(ETX and SRTT). No cumulative path costs are propagated and no wire
|
||||
format changes are required. This avoids the trust problems inherent
|
||||
@@ -230,11 +237,13 @@ direct peers and D is the tree depth. This is NOT O(N) where N is the
|
||||
network size.
|
||||
|
||||
What a node stores:
|
||||
|
||||
- Its own declaration (coordinates, sequence, timestamp, signature)
|
||||
- Each peer's declaration and ancestry chain (P entries, each with D
|
||||
ancestry entries)
|
||||
|
||||
What a node does NOT know:
|
||||
|
||||
- Other subtrees branching off its ancestors
|
||||
- Siblings of ancestors
|
||||
- Nodes in distant parts of the network
|
||||
@@ -249,6 +258,9 @@ Example: In a 1000-node network with depth 10 and 5 peers, a node stores
|
||||
| PARENT_HYSTERESIS | 0.2 (20%) | Fractional improvement in effective depth required for same-root switch |
|
||||
| HOLD_DOWN_SECS | 30s | Suppress non-mandatory re-evaluation after parent switch |
|
||||
| REEVAL_INTERVAL_SECS | 60s | Periodic cost-based parent re-evaluation interval |
|
||||
| FLAP_THRESHOLD | 4 | Parent switches in window before dampening engages |
|
||||
| FLAP_WINDOW_SECS | 60s | Sliding window for counting parent switches |
|
||||
| FLAP_DAMPENING_SECS | 120s | Extended hold-down duration when flap threshold exceeded |
|
||||
| ANNOUNCE_MIN_INTERVAL | 500ms | Minimum between announcements to same peer |
|
||||
| ROOT_TIMEOUT | 60 min | Root declaration considered stale (not yet enforced; heartbeat cascading covers common case) |
|
||||
| TREE_ENTRY_TTL | 5–10 min | Individual entry expiration |
|
||||
@@ -267,6 +279,7 @@ Example: In a 1000-node network with depth 10 and 5 peers, a node stores
|
||||
| Sequence-based freshness | **Implemented** |
|
||||
| Rate limiting (500ms per peer) | **Implemented** |
|
||||
| Coord cache flush on parent change | **Implemented** |
|
||||
| Flap dampening (extended hold-down on rapid switches) | **Implemented** |
|
||||
| Root timeout enforcement | Planned |
|
||||
| Tree entry TTL enforcement | Planned |
|
||||
| Per-ancestry-entry signatures | Future direction |
|
||||
|
||||
@@ -1104,17 +1104,22 @@ The primary stability mechanisms are implemented:
|
||||
re-evaluation after a parent switch, allowing MMP metrics to stabilize
|
||||
- **Periodic re-evaluation** (`reeval_interval_secs = 60`): catches link
|
||||
degradation after tree stabilization independent of TreeAnnounce traffic
|
||||
- **Flap dampening** (`flap_threshold = 4`, `flap_window_secs = 60`,
|
||||
`flap_dampening_secs = 120`): if a node switches parents more than 4
|
||||
times within 60s, an extended 120s hold-down is imposed. Mandatory
|
||||
switches (parent loss, root change) bypass dampening. The flap counter
|
||||
resets when the window expires naturally.
|
||||
|
||||
Remaining gaps:
|
||||
|
||||
- No sequence number advancement rate limiting
|
||||
- No minimum stable state duration before re-announcing
|
||||
|
||||
**Impact**: A rapidly flapping link could still cause moderate announcement
|
||||
traffic. The hold-down timer limits the rate of parent switches (at most
|
||||
one non-mandatory switch per 30s), and per-peer rate limiting (500ms)
|
||||
bounds announcement frequency, but the source node's announcement rate is
|
||||
not independently throttled.
|
||||
**Impact**: These mechanisms compose to bound announcement traffic even
|
||||
under rapid link flapping. The hold-down timer limits the rate of parent
|
||||
switches (at most one non-mandatory switch per 30s), flap dampening
|
||||
catches pathological patterns that persist beyond the hold-down window,
|
||||
and per-peer rate limiting (500ms) bounds announcement frequency.
|
||||
|
||||
### Known Limitation: Integration Test Gaps
|
||||
|
||||
@@ -1126,7 +1131,8 @@ propagation. However, the following failure scenarios lack test coverage:
|
||||
- Network partition formation and independent operation
|
||||
- Partition healing and root convergence
|
||||
- Stale entry cleanup (depends on TTL implementation)
|
||||
- Parent flapping under rapid topology changes
|
||||
- Parent flapping under rapid topology changes (flap dampening is
|
||||
implemented; integration test coverage is the remaining gap)
|
||||
|
||||
These tests are blocked on or related to the limitations above and should be
|
||||
added as each limitation is resolved.
|
||||
@@ -1144,7 +1150,7 @@ through:
|
||||
4. **Bounded state** - O(peers × depth) entries per node, not O(network size)
|
||||
5. **Depth-proportional convergence** - Scales with tree height, not node count
|
||||
6. **Traffic-based failure detection** - No dedicated keepalive overhead
|
||||
7. **Stability thresholds** - Hysteresis prevents flapping on similar-cost paths
|
||||
7. **Stability thresholds** - Hysteresis, hold-down, and flap dampening prevent flapping on similar-cost paths
|
||||
|
||||
Each node maintains only its own ancestry and direct peer information—not global
|
||||
topology. Reachability to arbitrary destinations is provided by Bloom-guided
|
||||
|
||||
Reference in New Issue
Block a user