From d4b29346d75b74f3fb88e138477dec20a26c074e Mon Sep 17 00:00:00 2001 From: ddidderr Date: Fri, 22 May 2026 08:50:20 +0200 Subject: [PATCH] fix(gateway): prefer relay frames before LAN capture The gateway bridge loop uses a biased select so shutdown and relay lifecycle control events win over frame work. After those control paths, the loop used to poll physical LAN capture before relay datagrams. On a busy LAN, a perpetually readable AF_PACKET socket can delay ready remote client frames waiting to be injected onto the LAN. Service relay datagrams before LAN capture so remote-to-LAN traffic can cross the gateway promptly, while still preserving lifecycle-before-frame ordering. Test Plan: - cargo fmt --check - cargo test -p lanparty-gateway - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - git diff --check - git diff --cached --check Refs: MVP gateway bridge scheduling --- crates/lanparty-gateway/src/lib.rs | 98 +++++++++++++++--------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/crates/lanparty-gateway/src/lib.rs b/crates/lanparty-gateway/src/lib.rs index 0b355b6..0e9e693 100644 --- a/crates/lanparty-gateway/src/lib.rs +++ b/crates/lanparty-gateway/src/lib.rs @@ -353,6 +353,55 @@ impl GatewayConnection { ); } } + relay_frame = recv_gateway_ethernet_outcome(&connection, &welcome, &stats) => { + match relay_frame? { + GatewayReceiveOutcome::Accepted(relay_frame) => { + if let Some(drop_reason) = remote_clients.relay_frame_drop_reason( + relay_frame.source_peer_id(), + relay_frame.payload(), + )? { + stats.record_dropped_frame(); + println!( + "{}", + gateway_frame_log_line( + packet_socket.get_ref().interface(), + FrameDirection::RemoteToLan, + Some(relay_frame.source_peer_id()), + relay_frame.payload(), + FrameAction::Filtered, + Some(drop_reason), + ) + ); + continue; + } + write_lan_ethernet(&packet_socket, relay_frame.payload()).await?; + println!( + "{}", + gateway_frame_log_line( + packet_socket.get_ref().interface(), + FrameDirection::RemoteToLan, + Some(relay_frame.source_peer_id()), + relay_frame.payload(), + FrameAction::Forwarded, + None, + ) + ); + } + GatewayReceiveOutcome::Filtered(relay_frame) => { + println!( + "{}", + gateway_frame_log_line( + packet_socket.get_ref().interface(), + FrameDirection::RemoteToLan, + Some(relay_frame.source_peer_id), + &relay_frame.payload, + FrameAction::Filtered, + Some(relay_frame.drop_reason), + ) + ); + } + } + } lan_frame = read_lan_ethernet(&packet_socket) => { let lan_frame = lan_frame?; if EthernetFrame::parse(&lan_frame).is_err() { @@ -409,55 +458,6 @@ impl GatewayConnection { ) ); } - relay_frame = recv_gateway_ethernet_outcome(&connection, &welcome, &stats) => { - match relay_frame? { - GatewayReceiveOutcome::Accepted(relay_frame) => { - if let Some(drop_reason) = remote_clients.relay_frame_drop_reason( - relay_frame.source_peer_id(), - relay_frame.payload(), - )? { - stats.record_dropped_frame(); - println!( - "{}", - gateway_frame_log_line( - packet_socket.get_ref().interface(), - FrameDirection::RemoteToLan, - Some(relay_frame.source_peer_id()), - relay_frame.payload(), - FrameAction::Filtered, - Some(drop_reason), - ) - ); - continue; - } - write_lan_ethernet(&packet_socket, relay_frame.payload()).await?; - println!( - "{}", - gateway_frame_log_line( - packet_socket.get_ref().interface(), - FrameDirection::RemoteToLan, - Some(relay_frame.source_peer_id()), - relay_frame.payload(), - FrameAction::Forwarded, - None, - ) - ); - } - GatewayReceiveOutcome::Filtered(relay_frame) => { - println!( - "{}", - gateway_frame_log_line( - packet_socket.get_ref().interface(), - FrameDirection::RemoteToLan, - Some(relay_frame.source_peer_id), - &relay_frame.payload, - FrameAction::Filtered, - Some(relay_frame.drop_reason), - ) - ); - } - } - } _ = cam_refresh_tick.tick() => { for refresh in remote_clients.refreshes() { write_lan_ethernet(&packet_socket, refresh.frame()).await?;