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:
@@ -168,14 +168,16 @@ cargo run -p lanparty-gateway -- \
|
|||||||
--iface eth0
|
--iface eth0
|
||||||
```
|
```
|
||||||
|
|
||||||
The gateway connects to the relay as `role = gateway`, completes the
|
The gateway first opens the wired LAN interface as an AF_PACKET socket with
|
||||||
control-stream hello/welcome handshake, opens an AF_PACKET socket on the LAN
|
promiscuous packet membership, then connects to the relay as `role = gateway`
|
||||||
interface with promiscuous packet membership, and bridges Ethernet frames
|
and completes the control-stream hello/welcome handshake. That startup order
|
||||||
between the relay and wired LAN until shutdown. It captures whole LAN frames up
|
keeps an invalid or wireless interface from briefly advertising a gateway that
|
||||||
to the overlay payload-length ceiling before deciding whether they fit the
|
cannot bridge. Once both sides are ready, it bridges Ethernet frames between the
|
||||||
tunnel. It never fragments Ethernet frames; LAN frames whose encoded datagrams
|
relay and wired LAN until shutdown. It captures whole LAN frames up to the
|
||||||
exceed the negotiated QUIC budget are counted, dropped, and logged instead of
|
overlay payload-length ceiling before deciding whether they fit the tunnel. It
|
||||||
stopping the bridge.
|
never fragments Ethernet frames; LAN frames whose encoded datagrams exceed the
|
||||||
|
negotiated QUIC budget are counted, dropped, and logged instead of stopping the
|
||||||
|
bridge.
|
||||||
`--relay` accepts a DNS name or socket address; bare hosts default to UDP/443.
|
`--relay` accepts a DNS name or socket address; bare hosts default to UDP/443.
|
||||||
The gateway rejects Linux interfaces that sysfs identifies as Wi-Fi; managed
|
The gateway rejects Linux interfaces that sysfs identifies as Wi-Fi; managed
|
||||||
wireless NICs are not supported for the physical LAN bridge.
|
wireless NICs are not supported for the physical LAN bridge.
|
||||||
|
|||||||
+2
-1
@@ -77,8 +77,9 @@ Use the real wired LAN interface name for `--iface`. Do not use Wi-Fi.
|
|||||||
Expected gateway output:
|
Expected gateway output:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
lanparty-gateway connected as peer ...
|
lanparty-gateway opening interface eth0 and connecting to relay ...
|
||||||
lanparty-gateway opened AF_PACKET socket on eth0 ...
|
lanparty-gateway opened AF_PACKET socket on eth0 ...
|
||||||
|
lanparty-gateway connected as peer ...
|
||||||
lanparty-gateway bridging frames; press Ctrl-C to stop
|
lanparty-gateway bridging frames; press Ctrl-C to stop
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,31 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use lanparty_gateway::GatewayArgs;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use lanparty_gateway::PacketSocket;
|
use lanparty_gateway::{PacketSocket, connect_gateway};
|
||||||
use lanparty_gateway::{GatewayArgs, connect_gateway};
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
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!(
|
println!(
|
||||||
"lanparty-gateway connecting interface {} to relay {} room {}",
|
"lanparty-gateway opening interface {} and connecting to relay {} room {}",
|
||||||
config.interface(),
|
config.interface(),
|
||||||
config.relay_addr(),
|
config.relay_addr(),
|
||||||
config.room()
|
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?;
|
let gateway = connect_gateway(config).await?;
|
||||||
println!(
|
println!(
|
||||||
"lanparty-gateway connected as peer {} in room id {} with TAP MTU {} over {}",
|
"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().effective_tap_mtu(),
|
||||||
gateway.welcome().mode()
|
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");
|
println!("lanparty-gateway bridging frames; press Ctrl-C to stop");
|
||||||
gateway.bridge_until_shutdown(socket).await?;
|
gateway.bridge_until_shutdown(socket).await?;
|
||||||
}
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
|
||||||
anyhow::bail!("lanparty-gateway requires Linux AF_PACKET support");
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
async fn run(_args: GatewayArgs) -> anyhow::Result<()> {
|
||||||
|
anyhow::bail!("lanparty-gateway requires Linux AF_PACKET support");
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user