feat(relay): log egress datagram budget skips
PLAN.md keeps MVP forwarding to one Ethernet frame per QUIC datagram with no fragmentation. The relay already skipped targets whose negotiated datagram budget could not carry an encoded forwarded frame, but that skip was silent. Retain the no-fragmentation behavior and log the skipped egress target with the room, ingress peer, target peer, encoded length, and target datagram budget. Store the peer id in relay sessions so the diagnostic can identify the skipped target directly. Document the egress budget-skip diagnostic in the relay README section. Test Plan: - cargo fmt --check - cargo test -p lanparty-relay - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - git diff --check Refs: PLAN.md
This commit is contained in:
@@ -131,6 +131,8 @@ Safety-policy rejects use the `filtered` action so they are distinguishable
|
|||||||
from malformed/unknown-destination drops and rate limits.
|
from malformed/unknown-destination drops and rate limits.
|
||||||
Malformed peer datagrams log their per-peer count before the relay disconnects
|
Malformed peer datagrams log their per-peer count before the relay disconnects
|
||||||
peers that cross the malformed-datagram threshold.
|
peers that cross the malformed-datagram threshold.
|
||||||
|
Relay egress skips caused by a target peer's smaller datagram budget are logged
|
||||||
|
with the ingress peer, target peer, encoded length, and target budget.
|
||||||
Unknown unicast from a client is forwarded only to the gateway port; unknown
|
Unknown unicast from a client is forwarded only to the gateway port; unknown
|
||||||
unicast from the gateway is dropped instead of flooded to every remote client.
|
unicast from the gateway is dropped instead of flooded to every remote client.
|
||||||
When a peer joins or leaves, the relay sends a reliable lifecycle control event
|
When a peer joins or leaves, the relay sends a reliable lifecycle control event
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ impl PeerKey {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct PeerSession {
|
struct PeerSession {
|
||||||
connection: quinn::Connection,
|
connection: quinn::Connection,
|
||||||
|
peer_id: u32,
|
||||||
max_datagram_size: usize,
|
max_datagram_size: usize,
|
||||||
latest_stats: Option<TunnelStats>,
|
latest_stats: Option<TunnelStats>,
|
||||||
}
|
}
|
||||||
@@ -299,6 +300,7 @@ async fn register_peer(
|
|||||||
PeerKey::from_accepted(accepted),
|
PeerKey::from_accepted(accepted),
|
||||||
PeerSession {
|
PeerSession {
|
||||||
connection,
|
connection,
|
||||||
|
peer_id: accepted.peer.peer_id(),
|
||||||
max_datagram_size: accepted.max_datagram_size,
|
max_datagram_size: accepted.max_datagram_size,
|
||||||
latest_stats: None,
|
latest_stats: None,
|
||||||
},
|
},
|
||||||
@@ -429,6 +431,16 @@ async fn forward_peer_datagram(
|
|||||||
|
|
||||||
for target in target_sessions {
|
for target in target_sessions {
|
||||||
if outgoing.len() > target.max_datagram_size {
|
if outgoing.len() > target.max_datagram_size {
|
||||||
|
eprintln!(
|
||||||
|
"{}",
|
||||||
|
egress_budget_skip_log_line(
|
||||||
|
&accepted.room,
|
||||||
|
accepted.peer.peer_id(),
|
||||||
|
target.peer_id,
|
||||||
|
outgoing.len(),
|
||||||
|
target.max_datagram_size,
|
||||||
|
)
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -590,6 +602,19 @@ fn malformed_datagram_log_line(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn egress_budget_skip_log_line(
|
||||||
|
room: &RoomCode,
|
||||||
|
ingress_peer_id: u32,
|
||||||
|
target_peer_id: u32,
|
||||||
|
datagram_len: usize,
|
||||||
|
max_datagram_size: usize,
|
||||||
|
) -> String {
|
||||||
|
format!(
|
||||||
|
"relay egress skipped room={} peer_id={} target_peer_id={} len={} max_datagram_size={} reason=datagram_budget",
|
||||||
|
room, ingress_peer_id, target_peer_id, datagram_len, max_datagram_size
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async fn collect_target_sessions(
|
async fn collect_target_sessions(
|
||||||
sessions: &Arc<Mutex<HashMap<PeerKey, PeerSession>>>,
|
sessions: &Arc<Mutex<HashMap<PeerKey, PeerSession>>>,
|
||||||
room: &RoomCode,
|
room: &RoomCode,
|
||||||
@@ -1030,6 +1055,21 @@ mod tests {
|
|||||||
assert!(line.contains("disconnecting=true"));
|
assert!(line.contains("disconnecting=true"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn formats_egress_budget_skip_log_line() {
|
||||||
|
let room = RoomCode::new("TESTROOM").unwrap();
|
||||||
|
|
||||||
|
let line = egress_budget_skip_log_line(&room, 2, 7, 1401, 1400);
|
||||||
|
|
||||||
|
assert!(line.contains("relay egress skipped"));
|
||||||
|
assert!(line.contains("room=TESTROOM"));
|
||||||
|
assert!(line.contains("peer_id=2"));
|
||||||
|
assert!(line.contains("target_peer_id=7"));
|
||||||
|
assert!(line.contains("len=1401"));
|
||||||
|
assert!(line.contains("max_datagram_size=1400"));
|
||||||
|
assert!(line.contains("reason=datagram_budget"));
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn classifies_bad_peer_datagrams_as_malformed() {
|
async fn classifies_bad_peer_datagrams_as_malformed() {
|
||||||
let rooms = Arc::new(Mutex::new(RoomRegistry::default()));
|
let rooms = Arc::new(Mutex::new(RoomRegistry::default()));
|
||||||
|
|||||||
Reference in New Issue
Block a user