The MVP tunnel negotiates an effective TAP MTU and configures the Windows TAP IP interface to that value, but the forwarding path only rejected frames that were standard-Ethernet jumbo frames or exceeded the QUIC datagram budget. A frame could therefore be larger than the negotiated TAP MTU while still fitting inside the QUIC datagram budget. Make the TAP-MTU frame limit an explicit shared protocol helper and enforce it at every data-path boundary: Windows client send/receive, Linux gateway send/receive, and relay forwarding. Such frames now produce TapMtuExceeded in logs and counters instead of being forwarded until a later layer drops or accepts them implicitly. This keeps the no-fragmentation contract honest: one Ethernet frame still maps to one QUIC datagram, but only if that frame also fits the room's negotiated TAP MTU. Test Plan: - cargo fmt --check - cargo test -p lanparty-proto tap_mtu - cargo test -p lanparty-client-core connects_to_relay_control_stream_as_client - cargo test -p lanparty-gateway connects_to_relay_control_stream_as_gateway - cargo test -p lanparty-relay drops_frames_above_effective_tap_mtu - cargo test -p lanparty-relay rate_limits_client_total_bandwidth_after_burst - cargo test --workspace - cargo clippy --workspace --all-targets -- -D warnings - cargo build --release -p lanparty-relay -p lanparty-gateway - git diff --check - git diff --cached --check Refs: MVP no-fragmentation tunnel MTU contract
10 KiB
MVP Test Guide
This guide is for the manual end-to-end MVP proof:
Windows TAP client -> public QUIC relay -> Linux AF_PACKET gateway -> LAN
The MVP is intentionally manual. It does not include an installer, GUI, production certificates, auth, or end-to-end payload encryption.
MVP Pass Conditions
The MVP proof is successful when all of these are true:
- Relay shows one gateway peer and one Windows client peer in the same room.
- Windows client reports
gateway connected yes. - Windows TAP adapter gets a real LAN DHCP address, not only
169.254.x.x. - A LAN host can be reached from the TAP address with
ping -S. - The physical switch learns the Windows client MAC on the Linux gateway port.
- A LAN game can discover or join a server through the TAP adapter.
Fill these in before starting so the commands below are just copy/edit/run:
relay host: relay.example.net
relay UDP port: 8443
room code: ROOM1
gateway iface: eth0
LAN test host: <lan-host-ip>
Machines
- Relay: public Linux host reachable over UDP.
- Gateway: Linux machine plugged into the LAN party switch with wired Ethernet.
- Client: Windows 11 machine with TAP-Windows6 installed.
Use the same room code everywhere, for example ROOM1.
Build Prerequisites
On Windows, use the Rust MSVC toolchain and install Visual Studio Build Tools
with the C++ build tools. The dependency stack includes native code, so tools
such as cl.exe and lib.exe must be available in the build environment.
TAP-Windows6 must be installed before running the client.
Quick Windows checks:
rustc -vV
where.exe cl
where.exe lib
rustc -vV should report a host containing x86_64-pc-windows-msvc.
Build
On the relay or Linux build host:
cargo build --release -p lanparty-relay -p lanparty-gateway
On Windows, in an Administrator terminal:
cargo build --release -p lanparty-client-win
The Windows client must run elevated because it opens TAP and edits routes. The gateway usually needs root because it opens an AF_PACKET raw socket.
Start The Relay
Use a high UDP port first unless you already want to deal with privileged
443/udp binding:
./target/release/lanparty-relay \
--listen 0.0.0.0:8443 \
--dev-cert-der-out relay-cert.der
Open inbound UDP for the selected port on the relay host firewall.
Expected relay output:
lanparty-relay configured for 0.0.0.0:8443/udp ...
lanparty-relay listening on 0.0.0.0:8443
Copy relay-cert.der to the gateway and Windows client. The development
certificate is for lanparty-relay.local, so keep
--server-name lanparty-relay.local even when --relay is an IP address or
another DNS name.
Start The Gateway
On the LAN gateway machine:
sudo ./target/release/lanparty-gateway \
--relay relay.example.net:8443 \
--server-name lanparty-relay.local \
--relay-ca-cert ./relay-cert.der \
--room ROOM1 \
--interface eth0
Use the real wired LAN interface name for --interface. --iface is accepted
as a shorter alias. Do not use Wi-Fi. The gateway fails before joining the
relay if sysfs reports no Ethernet carrier.
Expected gateway output:
lanparty-gateway opening interface eth0 and connecting to relay ...
lanparty-gateway opened AF_PACKET socket on eth0 ...
lanparty-gateway connected as peer ...
lanparty-gateway bridging frames; press Ctrl-C to stop
Expected relay output:
accepted Gateway peer ... in room ROOM1 ...
Leave this running before starting the Windows client.
Start The Windows Client
In an Administrator terminal on Windows:
.\target\release\lanparty-client-win.exe `
--relay relay.example.net:8443 `
--server-name lanparty-relay.local `
--relay-ca-cert .\relay-cert.der `
--room ROOM1
If the Windows machine has multiple TAP-Windows6 adapters, select the intended one explicitly:
.\target\release\lanparty-client-win.exe --list-tap-adapters
.\target\release\lanparty-client-win.exe `
--relay relay.example.net:8443 `
--server-name lanparty-relay.local `
--relay-ca-cert .\relay-cert.der `
--room ROOM1 `
--tap-instance-id "{InterfaceGuid-from-the-command-above}"
Expected client output:
prepared TAP adapter ... MAC ... configured and media disconnected before relay connect
lanparty-client-win connected as peer ...
relay route pinned before TAP ...
relay route verified after TAP activation ...
TAP driver reports MAC ... and MTU ...
client diagnostics: relay reachable yes gateway connected yes route pinned yes ...
The route pin line ends with (created) or (already existed). Either is OK.
already existed usually means a matching relay host route was already present,
for example after a previous crashed test run.
The first diagnostics line may show IP unknown. After DHCP succeeds, a later
line should show:
DHCP received: 10.x.x.x
If Windows reports both a 169.254.x.x TAP address and a real LAN IPv4
address, the client diagnostics should prefer the real LAN address.
Verify The Tunnel
- Relay sees both peers:
accepted Gateway peer ...
accepted Client peer ...
- Client sees the gateway:
gateway connected yes
Connected to LAN gateway
- Windows TAP gets an address from the LAN:
Get-NetIPAddress | ? InterfaceAlias -like "*TAP*"
Use the non-link-local IPv4 address as <tap-ip> in the next step.
- ARP and ping work from the TAP-side address:
arp -d *
ping -S <tap-ip> <lan-host-ip>
arp -a
- The LAN switch learns the remote client MAC on the gateway port.
Use the switch UI or CLI and look for the client MAC printed by the Windows client. It should appear on the physical port connected to the Linux gateway.
- A real LAN game discovers or joins a LAN server.
This is the practical MVP acceptance test.
Useful Log Signals
Relay frame forwarding:
relay frame room=ROOM1 ... action=Forwarded drop_reason=- targets=1
Gateway LAN traffic:
gateway control event: client peer ... joined with MAC ...
gateway frame interface=eth0 direction=LanToRemote ... action=Forwarded
gateway frame interface=eth0 direction=RemoteToLan ... action=Forwarded
gateway CAM refresh interface=eth0 peer_id=... mac=... reason=peer_joined
gateway CAM refresh interface=eth0 peer_id=... mac=... reason=periodic
Client health:
Relay RTT: 23 ms
Broadcast traffic flowing
Broadcast sent toward LAN; waiting for LAN broadcast reply
LAN broadcast received
client frame direction=TapToRelay ... action=Forwarded drop_reason=-
client frame direction=RelayToTap ... action=Forwarded drop_reason=-
Drops that can be normal during testing:
drop_reason=UnknownDestination
drop_reason=TapMtuExceeded
drop_reason=DatagramBudget
drop_reason=RateLimit
On gateway LanToRemote logs, UnknownDestination usually means the gateway
captured unrelated LAN unicast and dropped it locally instead of sending it to
the relay.
TapMtuExceeded means a host emitted an Ethernet frame larger than the
negotiated tunnel MTU; occasional drops can happen while testing software that
does not honor the smaller adapter MTU yet.
Drops that should be investigated if they dominate:
drop_reason=Malformed
drop_reason=InvalidSourceMac
drop_reason=UnauthorizedSourceMac
drop_reason=ControlPlaneEtherType
drop_reason=VlanTaggedFrame
drop_reason=DhcpServerReply
drop_reason=Ipv6RouterAdvertisement
drop_reason=Ipv6Fragment
On gateway RemoteToLan logs, UnauthorizedSourceMac means the relayed peer id
did not match the client MAC announced by lifecycle events. If it repeats,
check relay lifecycle logs and duplicate-MAC rejection first.
Troubleshooting
If the client says Waiting for LAN gateway, check that the gateway uses the
same room code and is connected to the same relay.
If the gateway fails with reports no carrier, plug the selected Ethernet
interface into the LAN party switch, bring the interface up, and restart the
gateway.
If startup fails before the relay connection while preparing the TAP adapter,
check that the terminal is elevated, TAP-Windows6 is installed, and
--tap-instance-id selects the intended adapter when more than one TAP adapter
exists.
If the client says Waiting for TAP IP, DHCP is not making the full round trip.
Check relay/gateway frame logs for broadcast traffic and check that the gateway
is on wired Ethernet.
If the client reports a TAP link-local address such as 169.254.x.x, treat it
the same way: Windows has self-assigned an address, but LAN DHCP did not
complete through the tunnel.
If startup fails with a TAP MAC mismatch, disable/enable the TAP adapter or
reinstall TAP-Windows6 so Windows reloads the NetworkAddress value. Do not
continue with a mismatched MAC.
If startup says the relay route changed, stop. The client is refusing to run because Windows would route the relay connection through the tunnel.
If ping fails but DHCP worked, check Windows firewall, the target LAN host
firewall, and whether the LAN subnet conflicts with the client's home LAN.
Uncommon LAN subnets such as 10.73.42.0/24 are safer than 192.168.0.0/24.
If switch MAC learning does not show the Windows client MAC on the gateway
port, look for gateway CAM refresh ... reason=peer_joined immediately after
join and gateway CAM refresh ... reason=periodic about once per minute after
that. If those lines are present but the switch still does not learn it, check
the selected gateway interface and switch port first.
Cleanup
Stop client, gateway, and relay with Ctrl-C. The Windows client removes the relay host route only when it created that route itself, restores the TAP route policy, and marks TAP media disconnected when it exits normally.
Keep lanparty-client-identity.json if you want the same virtual MAC on the
next run. Delete it only when you intentionally want a new client identity.
Report Back
For a useful test report, capture:
- relay command and relay logs
- gateway command and gateway logs
- client command and client logs
- Windows TAP MAC and IP
- ping result from
<tap-ip>to a LAN host - switch MAC-table entry for the Windows client MAC
- LAN game discovery or join result