fix(gateway): validate LAN interface before relay join

The gateway previously joined the relay room before opening the Linux
AF_PACKET socket. If the operator passed the wrong interface, a wireless
interface, or an interface that could not be opened, the relay could briefly
advertise a gateway that was never able to bridge LAN traffic.

Parse CLI args first, then keep platform handling in a small cfg-gated run
function. On Linux, build the config, open the AF_PACKET socket, and only then
join the relay as the room gateway. On non-Linux targets, fail before reading
relay certificate files, resolving relay names, or opening network connections.

Update the README and MVP test guide so the documented gateway startup order
matches the binary output.

Test Plan:
- cargo fmt --check
- cargo test -p lanparty-gateway
- cargo test --workspace
- cargo clippy --workspace --all-targets -- -D warnings
- git diff --check

Refs: MVP gateway startup hardening
This commit is contained in:
2026-05-21 23:19:26 +02:00
parent 2f0802dfcf
commit 47f66b7d04
3 changed files with 36 additions and 26 deletions
+24 -17
View File
@@ -1,18 +1,31 @@
use clap::Parser;
use lanparty_gateway::GatewayArgs;
#[cfg(target_os = "linux")]
use lanparty_gateway::PacketSocket;
use lanparty_gateway::{GatewayArgs, connect_gateway};
use lanparty_gateway::{PacketSocket, connect_gateway};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let config = GatewayArgs::parse().into_config()?;
let args = GatewayArgs::parse();
run(args).await
}
#[cfg(target_os = "linux")]
async fn run(args: GatewayArgs) -> anyhow::Result<()> {
let config = args.into_config()?;
println!(
"lanparty-gateway connecting interface {} to relay {} room {}",
"lanparty-gateway opening interface {} and connecting to relay {} room {}",
config.interface(),
config.relay_addr(),
config.room()
);
let socket = PacketSocket::open(config.interface())?;
println!(
"lanparty-gateway opened AF_PACKET socket on {} (ifindex {})",
socket.interface(),
socket.interface_index()
);
let gateway = connect_gateway(config).await?;
println!(
"lanparty-gateway connected as peer {} in room id {} with TAP MTU {} over {}",
@@ -21,19 +34,13 @@ async fn main() -> anyhow::Result<()> {
gateway.welcome().effective_tap_mtu(),
gateway.welcome().mode()
);
#[cfg(target_os = "linux")]
{
let socket = PacketSocket::open(gateway.config().interface())?;
println!(
"lanparty-gateway opened AF_PACKET socket on {} (ifindex {})",
socket.interface(),
socket.interface_index()
);
println!("lanparty-gateway bridging frames; press Ctrl-C to stop");
gateway.bridge_until_shutdown(socket).await?;
}
#[cfg(not(target_os = "linux"))]
anyhow::bail!("lanparty-gateway requires Linux AF_PACKET support");
println!("lanparty-gateway bridging frames; press Ctrl-C to stop");
gateway.bridge_until_shutdown(socket).await?;
Ok(())
}
#[cfg(not(target_os = "linux"))]
async fn run(_args: GatewayArgs) -> anyhow::Result<()> {
anyhow::bail!("lanparty-gateway requires Linux AF_PACKET support");
}