Suppress repeated replay detection log messages during link transitions

Add per-peer replay suppression counter to ActivePeer. Log the first 3
replay detections at DEBUG, then suppress with a one-time notice. Emit
a summary count on session replacement or peer removal.

Non-replay decryption errors continue to be logged unconditionally.
This commit is contained in:
Johnathan Corgan
2026-02-23 19:56:12 +00:00
parent e3b5bd0bcd
commit 05e3853ca1
4 changed files with 110 additions and 6 deletions
+10
View File
@@ -98,6 +98,16 @@ impl Node {
}
};
// Log suppressed replay detection summary before teardown
let suppressed = peer.replay_suppressed_count();
if suppressed > 0 {
debug!(
peer = %self.peer_display_name(node_addr),
count = suppressed,
"Suppressed replay detections during link transition"
);
}
// MMP teardown log (before we drop the peer)
if let Some(mmp) = peer.mmp() {
let name = self.peer_aliases.get(node_addr)
+36 -6
View File
@@ -1,5 +1,6 @@
//! Encrypted frame handling (hot path).
use crate::noise::NoiseError;
use crate::node::Node;
use crate::node::wire::{EncryptedHeader, strip_inner_header, FLAG_CE, FLAG_SP};
use crate::transport::ReceivedPacket;
@@ -63,12 +64,41 @@ impl Node {
) {
Ok(p) => p,
Err(e) => {
debug!(
peer = %self.peer_display_name(&node_addr),
counter = header.counter,
error = %e,
"Decryption failed"
);
if matches!(e, NoiseError::ReplayDetected(_)) {
// Suppress repeated replay detections during link transitions.
// Re-borrow peer mutably for suppression counter update.
if let Some(peer) = self.peers.get_mut(&node_addr) {
let count = peer.increment_replay_suppressed();
if count <= 3 {
debug!(
peer = %self.peer_display_name(&node_addr),
counter = header.counter,
error = %e,
"Decryption failed"
);
} else if count == 4 {
debug!(
peer = %self.peer_display_name(&node_addr),
"Suppressing further replay detection messages"
);
}
// count > 4: silently suppress
} else {
debug!(
peer = %self.peer_display_name(&node_addr),
counter = header.counter,
error = %e,
"Decryption failed"
);
}
} else {
debug!(
peer = %self.peer_display_name(&node_addr),
counter = header.counter,
error = %e,
"Decryption failed"
);
}
return;
}
};
+9
View File
@@ -450,6 +450,7 @@ impl Node {
};
if let Some(peer) = self.peers.get_mut(&peer_node_addr) {
let suppressed = peer.replay_suppressed_count();
let old_our_index = peer.replace_session(
outbound_session,
outbound_our_index,
@@ -467,6 +468,14 @@ impl Node {
peer_node_addr,
);
if suppressed > 0 {
debug!(
peer = %self.peer_display_name(&peer_node_addr),
count = suppressed,
"Suppressed replay detections during link transition"
);
}
info!(
peer = %self.peer_display_name(&peer_node_addr),
new_our_index = %outbound_our_index,