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", - ) - ); - } - } } } }