From c44dc98b22d9fcd5e7e0fc9c598cfe7bc816374b Mon Sep 17 00:00:00 2001 From: ddidderr Date: Fri, 22 May 2026 06:26:01 +0200 Subject: [PATCH] fix(gateway): prioritize lifecycle events before frames Gateway filtering and CAM refresh depend on the remote-client MAC table that is seeded by relay lifecycle events. If a lifecycle stream and packet work are both ready in the same loop turn, handle the lifecycle event first so the local MAC table is as fresh as possible before deciding whether to inject or forward frames. This does not create a cross-stream ordering guarantee. It is a local scheduling preference that reduces first-packet drops after client joins without weakening source-MAC authorization or LAN-destination filtering. Test Plan: - cargo test -p lanparty-gateway - cargo fmt --check - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - git diff --check - git diff --cached --check Refs: PLAN.md MVP gateway lifecycle and L2 bridge behavior --- README.md | 5 +++- crates/lanparty-gateway/src/lib.rs | 40 ++++++++++++++++-------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 91cec8d..449c579 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,10 @@ NICs are not supported for the physical LAN bridge. It tracks remote-client MACs from relay lifecycle events and periodically emits small CAM refresh frames so the physical switch keeps those MACs associated with the gateway port. A newly observed client also triggers an immediate CAM -refresh frame instead of waiting for the first periodic refresh tick. Gateway +refresh frame instead of waiting for the first periodic refresh tick. When +control events and frame work are both ready, the bridge handles the lifecycle +event first so first packets after a client joins use the freshest remote-MAC +state available locally. Gateway frame logs include direction, peer id when present, MACs, ethertype/length, frame length, action, and drop reason. The gateway also tracks frame/datagram counters and periodically sends stats snapshots to the relay. Malformed or runt diff --git a/crates/lanparty-gateway/src/lib.rs b/crates/lanparty-gateway/src/lib.rs index 1cb8a59..e6451fe 100644 --- a/crates/lanparty-gateway/src/lib.rs +++ b/crates/lanparty-gateway/src/lib.rs @@ -315,6 +315,8 @@ impl GatewayConnection { loop { tokio::select! { + biased; + shutdown = tokio::signal::ctrl_c() => { shutdown.context("failed to wait for Ctrl-C")?; if let Err(error) = @@ -331,6 +333,25 @@ impl GatewayConnection { endpoint.wait_idle().await; return Ok(()); } + control_stream = connection.accept_uni() => { + let control_stream = control_stream + .context("failed to accept gateway control event stream")?; + let control_event = read_gateway_control_event(control_stream).await?; + let immediate_refresh = remote_clients.observe_control_event(&control_event); + println!("{}", format_gateway_control_event(&control_event)); + if let Some(refresh) = immediate_refresh { + write_lan_ethernet(&packet_socket, refresh.frame()).await?; + println!( + "{}", + gateway_cam_refresh_log_line( + packet_socket.get_ref().interface(), + refresh.peer_id(), + refresh.mac(), + "peer_joined", + ) + ); + } + } lan_frame = read_lan_ethernet(&packet_socket) => { let lan_frame = lan_frame?; if EthernetFrame::parse(&lan_frame).is_err() { @@ -446,25 +467,6 @@ impl GatewayConnection { eprintln!("failed to send gateway stats to relay: {error:#}"); } } - control_stream = connection.accept_uni() => { - let control_stream = control_stream - .context("failed to accept gateway control event stream")?; - let control_event = read_gateway_control_event(control_stream).await?; - let immediate_refresh = remote_clients.observe_control_event(&control_event); - println!("{}", format_gateway_control_event(&control_event)); - if let Some(refresh) = immediate_refresh { - write_lan_ethernet(&packet_socket, refresh.frame()).await?; - println!( - "{}", - gateway_cam_refresh_log_line( - packet_socket.get_ref().interface(), - refresh.peer_id(), - refresh.mac(), - "peer_joined", - ) - ); - } - } } } }