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
This commit is contained in:
2026-05-22 08:50:20 +02:00
parent b5ba635116
commit d4b29346d7
+49 -49
View File
@@ -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?;