diff --git a/README.md b/README.md index 3530880..1d33777 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,8 @@ Safety-policy rejects use the `filtered` action so they are distinguishable from malformed/unknown-destination drops and rate limits. Malformed peer datagrams log their per-peer count before the relay disconnects 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 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 diff --git a/crates/lanparty-relay/src/server.rs b/crates/lanparty-relay/src/server.rs index fba5570..11a87c5 100644 --- a/crates/lanparty-relay/src/server.rs +++ b/crates/lanparty-relay/src/server.rs @@ -56,6 +56,7 @@ impl PeerKey { #[derive(Debug, Clone)] struct PeerSession { connection: quinn::Connection, + peer_id: u32, max_datagram_size: usize, latest_stats: Option, } @@ -299,6 +300,7 @@ async fn register_peer( PeerKey::from_accepted(accepted), PeerSession { connection, + peer_id: accepted.peer.peer_id(), max_datagram_size: accepted.max_datagram_size, latest_stats: None, }, @@ -429,6 +431,16 @@ async fn forward_peer_datagram( for target in target_sessions { 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; } @@ -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( sessions: &Arc>>, room: &RoomCode, @@ -1030,6 +1055,21 @@ mod tests { 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] async fn classifies_bad_peer_datagrams_as_malformed() { let rooms = Arc::new(Mutex::new(RoomRegistry::default()));